今天学习基本的ADO,就是通过C#程序代码来操控数据库,其实说白了还是一堆函数,程序可越来越有英语课的感觉了。
为了省事,不写try什么的了。首先是最基本的登录和断开连接:
1 #region 登陆数据库
2 //创建一个连接对象
3 SqlConnection con = new SqlConnection();
4
5 //连接字符串(可以在左边服务器资源管理器里自动生成):
6 //Data source=服务器名(IP地址)\实例名;
7 //Initial Catalog=数据库名;
8 //Integrated Security=True 集成身份验证
9 //User ID=xxx;Password=xxx 用户名密码登陆
10 string constr = @"Data source=IT-129\SQLEXPRESS;Initial Catalog=ItCastCn;Integrated Security=True";
11
12 //让字符串和SqlConnection对象关联
13 con.ConnectionString = constr;
14
15 //打开连接(避免重复打开)
16 if (con.State == System.Data.ConnectionState.Closed)
17 {
18 con.Open();
19 }
20 #endregion
21
22 #region 关闭数据库
23 //关闭连接
24 con.Close();
25
26 //释放非托管资源
27 con.Dispose();
28 #endregion
增删改都要使用SqlCommand的ExecuteNonQuery方法,下面这个例子从窗体中读取用户输入,并且修改数据库:
1 private void button1_Click(object sender, EventArgs e)
2 {
3 //从控件中取得字符串
4 string autoId = txt_autoId.Text.Trim();
5 string BrandName = txt_BrandName.Text.Trim();
6 string Price = txt_Price.Text.Trim();
7
8 //建立连接字符串
9 string constr = @"Data source=IT-129\SQLEXPRESS;Initial Catalog=ItCastCn;Integrated Security=True";
10
11 //新建一个SqlConnection对象con对数据库进行控制(连接器)
12 using (SqlConnection con = new SqlConnection(constr))
13 {
14 //定义SQL语句
15 string sql = string.Format
16 (
17 "update T1 " +
18 "set BrandName='{0}', price={1} " +
19 "where autoId = {2}" ,
20 BrandName, Price, autoId
21 );
22
23 //定义一个SqlCommand对象cmd(操作器),参数是SQL语句和连接器
24 using (SqlCommand cmd = new SqlCommand(sql, con))
25 {
26 //Open操作尽量晚
27 if (con.State == System.Data.ConnectionState.Closed)
28 {
29 con.Open();
30 }
31 //执行SQL语句,返回影响了几条
32 int r = cmd.ExecuteNonQuery();
33
34 //Close操作尽量早
35 con.Close();
36 MessageBox.Show("已成功修改" + r + "条语句!");
37 }
38 }
39 }
使用SqlDataReader输出整个表,假设一个表里有3列:
1 using (SqlConnection con = new SqlConnection(constr))
2 {
3 string sql = "select * from dbo.ClassInfo";
4
5 using (SqlCommand cmd = new SqlCommand(sql, con))
6 {
7 if (con.State == System.Data.ConnectionState.Closed)
8 {
9 con.Open();
10 }
11
12 //这句话执行后并没有返回,存放在服务器的内存里
13 SqlDataReader reader = cmd.ExecuteReader();
14 using (reader)
15 {
16 if (reader.HasRows)
17 {
18 //读取每一行
19 while (reader.Read())
20 {
21 //读取每一列
22 //object obj1 = reader.GetValue(0);
23 //object obj2 = reader.GetValue(1);
24 //object obj3 = reader.GetValue(2);
25
26 //下标方式,可以根据列名取数据
27 //object obj1 = reader["ClassId"];
28 //object obj2 = reader[1];
29 //object obj3 = reader[2];
30
31 //建议使用这种强类型的方式提高性能
32 int obj1 = reader.GetInt32(0);
33 string obj2 = reader.GetString(1);
34 string obj3 = reader.GetString(2);
35
36 Console.WriteLine("{0}\t|\t{1}\t|\t{2}", obj1, obj2, obj3);
37 }
38 }
39 else
40 {
41 Console.WriteLine("x");
42 }
43 }
44
45 con.Close();
46 }
47 }
ADO.NET中数据库的增删改查(一)
作者:佚名出处:IT专家网2009-09-09 07:00
usingSystem;
usingSystem.Data;
usingSystem.Configuration;
usingSystem.Web;
usingSystem.Web.Security;
usingSystem.Web.UI;
usingSystem.Web.UI.WebControls;
usingSystem.Web.UI.WebControls.WebParts;
usingSystem.Web.UI.HtmlControls;
usingSystem.Data.SqlClient;
publicpartialclass_Default:System.Web.UI.Page
{
protectedvoidPage_Load(objectsender,EventArgse)
{
if(!IsPostBack)//IsPostBack表示是否是第一次请求这个页面默认第一次登陆为FALSE
{
Bind();
}
}
publicvoidBind()
{
stringstrConnetion=@"DataSource=.\sqlexpress;InitialCatalog=School;IntegratedSecurity=True";
SqlConnectionconnection=newSqlConnection(strConnetion);
stringstrSelect="select*fromstudent";
SqlDataAdapteradapter=newSqlDataAdapter(strSelect,connection);
DataSetdataset=newDataSet();
adapter.Fill(dataset);
GridView1.DataSource=dataset;//数据源与页面GridView控件的连接
GridView1.DataBind();//数据源与页面GridView控件的绑定
}
protectedvoidbtAdd_Click(objectsender,EventArgse)
{
stringstrConnetion=@"DataSource=.\sqlexpress;InitialCatalog=School;IntegratedSecurity=True";//里面的地址资源如果是远程的数据库服务器,就填写远程服务器的名字或者ip地址,如果是本地的用sa用户
//登陆的服务器,则需要在此地址的基础上添加password=多少这个项
SqlConnectionconnection=newSqlConnection(strConnetion);
stringstrInsert="insertintostudentvalues('" txtStuName.Text.Trim() "','" txtEnterDate.Text.Trim() "');selectscope_identity()";//实现scope函数的添加
SqlCommandcommand=newSqlCommand(strInsert,connection);//单引号里面是数字,双引号里面是字符型
connection.Open();
objectcount=command.ExecuteScalar();//当添加scope函数时,返回函数就应该用ExecuteScalar()函数获取,定义的类型则为object
connection.Close();//ExecuteScalar方法返回的类型是object类型,这个方法返回sql语句执行后的第一行第一列的值,所以ExecuteScalar方法返回一个最基本的类型object,这个类型是所有类型的基类,换句话说:可以转换为任意类型。
//ExecuteScalar()可以执行SELECT查询,返回的是一个单值,多用于查询聚合值,如函数count(),sum(),比如selectcount(*)fromusers,那么用EexcuteScalar就是返回一个int类型的值,该值等于users里面的行数。
Response.Write(count.ToString());//ToString()函数表示转换成字符串形式
Bind();
}
protectedvoidbtUpdate_Click(objectsender,EventArgse)
{
stringstrConnetion=@"DataSource=.\sqlexpress;InitialCatalog=School;IntegratedSecurity=True";
SqlConnectionconnection=newSqlConnection(strConnetion);
stringstrUpdate="updatestudentsetstuName='" txtStuName.Text.Trim() "'wherestuID=" txtStuID.Text.Trim() "";
SqlCommandcommand=newSqlCommand(strUpdate,connection);
connection.Open();
intcount=command.ExecuteNonQuery();//ExecuteNonQuery方法是用来执行insert、delete、update语句的,由于这些语句执行后只有一个结果:“影响了**行”,所以ExecuteNonQuery方法返回的是影响的行数(int)
//
connection.Close();
Response.Write(count.ToString());
Bind();
}
protectedvoidbtDelete_Click(objectsender,EventArgse)
{
stringstrConnetion=@"DataSource=.\sqlexpress;InitialCatalog=School;IntegratedSecurity=True";
SqlConnectionconnection=newSqlConnection(strConnetion);
stringstrInsert="deletefromstudentwherestuID=" txtStuID.Text.Trim() "";
SqlCommandcommand=newSqlCommand(strInsert,connection);
connection.Open();
intcount=command.ExecuteNonQuery();
connection.Close();
Response.Write(count.ToString());
Bind();
}
protectedvoidButton4_Click(objectsender,EventArgse)
{
stringstrConnetion=@"DataSource=.\sqlexpress;InitialCatalog=School;IntegratedSecurity=True";
SqlConnectionconnection=newSqlConnection(strConnetion);
stringstrSelect="select*fromstudent";
SqlDataAdapteradapter=newSqlDataAdapter(strSelect,connection);
DataSetdataset=newDataSet();
adapter.Fill(dataset);
stringstrStuID=dataset.Tables[0].Rows[0][2].ToString();
Response.Write(strStuID);
}
protectedvoidButton5_Click(objectsender,EventArgse)
{
stringstrConnetion=@"DataSource=.\sqlexpress;InitialCatalog=School;IntegratedSecurity=True";
SqlConnectionconnection=newSqlConnection(strConnetion);
stringstrSelect="select*fromstudentwherestuid=" txtStuID.Text.Trim() "";
SqlCommandcommand=newSqlCommand(strSelect,connection);
connection.Open();
SqlDataReaderdataReader=command.ExecuteReader();//ExecuteReader()返回一个Datareader对象,这个对象的内容就是与命令匹配的所有行
stringstrStuID=null;
if(dataReader.Read())
{
Session["id"]=txtStuID.Text.Trim();//Session建立一次会话
Response.Redirect("default2.aspx");
//Response.Redirect("default2.aspx?id=" txtStuID.Text);//action为get时用户的标识显示在地址栏
}
dataReader.Close();
connection.Close();
Response.Write(strStuID);
}
}
IT专家网 > 开发平台子站 > net专区
ADO.NET中数据库的增删改查(一)
作者:佚名出处:IT专家网2009-09-09 07:00
usingSystem;
usingSystem.Data;
usingSystem.Configuration;
usingSystem.Web;
usingSystem.Web.Security;
usingSystem.Web.UI;
usingSystem.Web.UI.WebControls;
usingSystem.Web.UI.WebControls.WebParts;
usingSystem.Web.UI.HtmlControls;
usingSystem.Data.SqlClient;
publicpartialclass_Default:System.Web.UI.Page
{
protectedvoidPage_Load(objectsender,EventArgse)
{
if(!IsPostBack)//IsPostBack表示是否是第一次请求这个页面默认第一次登陆为FALSE
{
Bind();
}
}
publicvoidBind()
{
stringstrConnetion=@"DataSource=.\sqlexpress;InitialCatalog=School;IntegratedSecurity=True";
SqlConnectionconnection=newSqlConnection(strConnetion);
stringstrSelect="select*fromstudent";
SqlDataAdapteradapter=newSqlDataAdapter(strSelect,connection);
DataSetdataset=newDataSet();
adapter.Fill(dataset);
GridView1.DataSource=dataset;//数据源与页面GridView控件的连接
GridView1.DataBind();//数据源与页面GridView控件的绑定
}
protectedvoidbtAdd_Click(objectsender,EventArgse)
{
stringstrConnetion=@"DataSource=.\sqlexpress;InitialCatalog=School;IntegratedSecurity=True";//里面的地址资源如果是远程的数据库服务器,就填写远程服务器的名字或者ip地址,如果是本地的用sa用户
//登陆的服务器,则需要在此地址的基础上添加password=多少这个项
SqlConnectionconnection=newSqlConnection(strConnetion);
stringstrInsert="insertintostudentvalues('" txtStuName.Text.Trim() "','" txtEnterDate.Text.Trim() "');selectscope_identity()";//实现scope函数的添加
SqlCommandcommand=newSqlCommand(strInsert,connection);//单引号里面是数字,双引号里面是字符型
connection.Open();
objectcount=command.ExecuteScalar();//当添加scope函数时,返回函数就应该用ExecuteScalar()函数获取,定义的类型则为object
connection.Close();//ExecuteScalar方法返回的类型是object类型,这个方法返回sql语句执行后的第一行第一列的值,所以ExecuteScalar方法返回一个最基本的类型object,这个类型是所有类型的基类,换句话说:可以转换为任意类型。
//ExecuteScalar()可以执行SELECT查询,返回的是一个单值,多用于查询聚合值,如函数count(),sum(),比如selectcount(*)fromusers,那么用EexcuteScalar就是返回一个int类型的值,该值等于users里面的行数。
Response.Write(count.ToString());//ToString()函数表示转换成字符串形式
Bind();
}
protectedvoidbtUpdate_Click(objectsender,EventArgse)
{
stringstrConnetion=@"DataSource=.\sqlexpress;InitialCatalog=School;IntegratedSecurity=True";
SqlConnectionconnection=newSqlConnection(strConnetion);
stringstrUpdate="updatestudentsetstuName='" txtStuName.Text.Trim() "'wherestuID=" txtStuID.Text.Trim() "";
SqlCommandcommand=newSqlCommand(strUpdate,connection);
connection.Open();
intcount=command.ExecuteNonQuery();//ExecuteNonQuery方法是用来执行insert、delete、update语句的,由于这些语句执行后只有一个结果:“影响了**行”,所以ExecuteNonQuery方法返回的是影响的行数(int)
//
connection.Close();
Response.Write(count.ToString());
Bind();
}
protectedvoidbtDelete_Click(objectsender,EventArgse)
{
stringstrConnetion=@"DataSource=.\sqlexpress;InitialCatalog=School;IntegratedSecurity=True";
SqlConnectionconnection=newSqlConnection(strConnetion);
stringstrInsert="deletefromstudentwherestuID=" txtStuID.Text.Trim() "";
SqlCommandcommand=newSqlCommand(strInsert,connection);
connection.Open();
intcount=command.ExecuteNonQuery();
connection.Close();
Response.Write(count.ToString());
Bind();
}
protectedvoidButton4_Click(objectsender,EventArgse)
{
stringstrConnetion=@"DataSource=.\sqlexpress;InitialCatalog=School;IntegratedSecurity=True";
SqlConnectionconnection=newSqlConnection(strConnetion);
stringstrSelect="select*fromstudent";
SqlDataAdapteradapter=newSqlDataAdapter(strSelect,connection);
DataSetdataset=newDataSet();
adapter.Fill(dataset);
stringstrStuID=dataset.Tables[0].Rows[0][2].ToString();
Response.Write(strStuID);
}
protectedvoidButton5_Click(objectsender,EventArgse)
{
stringstrConnetion=@"DataSource=.\sqlexpress;InitialCatalog=School;IntegratedSecurity=True";
SqlConnectionconnection=newSqlConnection(strConnetion);
stringstrSelect="select*fromstudentwherestuid=" txtStuID.Text.Trim() "";
SqlCommandcommand=newSqlCommand(strSelect,connection);
connection.Open();
SqlDataReaderdataReader=command.ExecuteReader();//ExecuteReader()返回一个Datareader对象,这个对象的内容就是与命令匹配的所有行
stringstrStuID=null;
if(dataReader.Read())
{
Session["id"]=txtStuID.Text.Trim();//Session建立一次会话
Response.Redirect("default2.aspx");
//Response.Redirect("default2.aspx?id=" txtStuID.Text);//action为get时用户的标识显示在地址栏
}
dataReader.Close();
connection.Close();
Response.Write(strStuID);
}
}
IT专家网 > 开发平台子站 > net专区
浅谈如何更好的打开和关闭ADO.NET连接池
作者:佚名出处:IT专家网2009-07-31 13:00
MS提倡我们尽可能每次的在连接使用完成后就关闭;这样导致每次都要进行打开和关闭操作或用using(){…}写起代码比较麻烦,还有经常对池的操作似乎也带来一些性能上的问题;在Asp.net里一个WebForm经常涉及有N个数据库操作,为了方便直接在init里打开一个连接,然后在Dispose里关闭即省事又能减少对池的操作提高性能;那到底这样做能不能提高性能呢?答案是能,但有些情况估计会很让你感觉到失望…。当池的连接数能应付用户请求的时候,的确是可以减少池的操作带来性能的提升;反之就不一样了用户必须等待到获取连接后才能进行其他操作,当有更多用户在等待获取连接的时候只能让线程处于更长等待时间导致服务总体处理能力下降。
以下做一次简单的测试:
测试方式是使用VS2008自带的负载测试,测试用户数是200,数据库连接池数设置20,测度时长10分钟。
关闭ADO.NET连接池测试代码1:
protected void Page_Load(object sender, EventArgs e)
{
using (System.Data.SqlClient.SqlConnection sqlconn= new System.Data.SqlClient.SqlConnection("data source=.;initial catalog=northwind;user id=sa;pwd=;Max Pool Size=20"))
{
sqlconn.Open();
System.Threading.Thread.Sleep(300);
}
}
关闭ADO.NET连接池测试代码2:
protected void Page_Load(object sender, EventArgs e)
{
using (System.Data.SqlClient.SqlConnection sqlconn = new System.Data.SqlClient.SqlConnection("data source=.;initial catalog=northwind;user id=sa;pwd=;Max Pool Size=20"))
{
sqlconn.Open();
}
System.Threading.Thread.Sleep(50);
using (System.Data.SqlClient.SqlConnection sqlconn = new System.Data.SqlClient.SqlConnection("data source=.;initial catalog=northwind;user id=sa;pwd=;Max Pool Size=20"))
{
sqlconn.Open();
}
System.Threading.Thread.Sleep(50);
using (System.Data.SqlClient.SqlConnection sqlconn = new System.Data.SqlClient.SqlConnection("data source=.;initial catalog=northwind;user id=sa;pwd=;Max Pool Size=20"))
{
sqlconn.Open();
}
System.Threading.Thread.Sleep(50);
using (System.Data.SqlClient.SqlConnection sqlconn = new System.Data.SqlClient.SqlConnection("data source=.;initial catalog=northwind;user id=sa;pwd=;Max Pool Size=20"))
{
sqlconn.Open();
}
System.Threading.Thread.Sleep(50);
using (System.Data.SqlClient.SqlConnection sqlconn = new System.Data.SqlClient.SqlConnection("data source=.;initial catalog=northwind;user id=sa;pwd=;Max Pool Size=20"))
{
sqlconn.Open();
}
System.Threading.Thread.Sleep(100);
}
ADO.NET数据库连接、操作SQL举例
作者:佚名出处:IT专家网2009-07-23 13:00
1.创建类datasource.cs
2.加入SQL命名空间(可以使用SQL数据库语句):using System.Data.SqlClient
3.类中申明对象:(string)ConnString 连接字符串、(SqlConnection)SqlConn 连接SQL数据库、(SqlCommand)SqlCmd 调用SQL语句的方式 、(SqlDataAdapter)SqlAdapter 连接SQL数据适配器、(DataSet)ds 建立数据集、(SqlDataReader)SqlReader 读取数据库。
4.在构造函数中初始化实例对象:
public datasource()
{
ConnString = ConfigurationManager.ConnectionStrings["某数据源"].ConnectionString;SqlConn = new SqlConnection(ConnString);SqlCmd = new SqlCommand();SqlAdpter = new SqlDataAdapter();ds = new DataSet();
}
5.创建一些数据集和数据库SQL操作的方法
(1)创建一个内存数据集,使用传入的SQL查询语句参数
public DataSet Tds(string QueryString)
{
SqlAdpter.SelectCommand = new SqlCommand(QueryString, SqlConn);
SqlAdpter.Fill(ds, "temp"); //temp是一个内存数据表
return ds;
}
(2)创建一个内存数据集,不使用任何参数。
public DataSet Tds()
{
//使用SQL语句来代替这个参数
SqlAdpter.SelectCommand = new SqlCommand("select * from abc", SqlConn);
//temp是一个内存数据表
SqlAdpter.Fill(ds,"temp");
return ds;
}
(3)返回一个只读的轻量级数据源,注意,它不支持服务端的分页,而且需要数据连接必须一直打开
public SqlDataReader Tsdr()
{
//SqlCommand属性CommandText是一条SQL语句
SqlCmd.CommandText = "select * from abc";
//SqlCommand属性Connection 就是sql的连接语句
SqlCmd.Connection = SqlConn;
SqlConn.Open();
//当数据读取随着数据连接的关闭而关闭
SqlReader = SqlCmd.ExecuteReader(CommandBehavior.CloseConnection);
return SqlReader;
}
(4)SQL更新语句:
public void Update(string 字段id, string 字段1, string 字段2...string 字段N)
{
SqlCmd.CommandText = "update 数据表 set [字段名1]=@字段名1,[字段名2]='" +
字段2 + "',..,[字段名N]='" + 字段N + "' where [字段id]='" + 字段id + "'";
// SqlCmd.Parameters.AddWithValue("@lname", au_lname); 更新的字段可以有单引号
SqlCmd.Connection = SqlConn; //连接
SqlConn.Open(); //打开
SqlCmd.ExecuteNonQuery(); //执行
}
(5)SQL删除语句:(用法如上)
public void Delete(string 字段id)
{
SqlCmd.CommandText = "delete from [abc] where [字段id]='"+字段id+"'";
SqlCmd.Connection = SqlConn;
SqlConn.Open();
SqlCmd.ExecuteNonQuery();
}
6.新建一个aspx页面:加入一个GridView表
(1)在配置文件(web.config)中,加入下面一段话:
(2)为GridView新建数据源:对象 ->业务对象datasource->select\delete\update\insert 调用相关对象的方法->设置参数sql查询语句,比如select * from abc.
分页设置(属性分页中AllowPage 是否分页?、Pagesize 分页页数。属性行为中的EnableSortingAndPagingCallbacks 是否分页回传
ADO.NET数据库连接、操作SQL举例
作者:佚名出处:IT专家网2009-07-23 13:00
1.创建类datasource.cs
2.加入SQL命名空间(可以使用SQL数据库语句):using System.Data.SqlClient
3.类中申明对象:(string)ConnString 连接字符串、(SqlConnection)SqlConn 连接SQL数据库、(SqlCommand)SqlCmd 调用SQL语句的方式 、(SqlDataAdapter)SqlAdapter 连接SQL数据适配器、(DataSet)ds 建立数据集、(SqlDataReader)SqlReader 读取数据库。
4.在构造函数中初始化实例对象:
public datasource()
{
ConnString = ConfigurationManager.ConnectionStrings["某数据源"].ConnectionString;SqlConn = new SqlConnection(ConnString);SqlCmd = new SqlCommand();SqlAdpter = new SqlDataAdapter();ds = new DataSet();
}
5.创建一些数据集和数据库SQL操作的方法
(1)创建一个内存数据集,使用传入的SQL查询语句参数
public DataSet Tds(string QueryString)
{
SqlAdpter.SelectCommand = new SqlCommand(QueryString, SqlConn);
SqlAdpter.Fill(ds, "temp"); //temp是一个内存数据表
return ds;
}
(2)创建一个内存数据集,不使用任何参数。
public DataSet Tds()
{
//使用SQL语句来代替这个参数
SqlAdpter.SelectCommand = new SqlCommand("select * from abc", SqlConn);
//temp是一个内存数据表
SqlAdpter.Fill(ds,"temp");
return ds;
}
(3)返回一个只读的轻量级数据源,注意,它不支持服务端的分页,而且需要数据连接必须一直打开
public SqlDataReader Tsdr()
{
//SqlCommand属性CommandText是一条SQL语句
SqlCmd.CommandText = "select * from abc";
//SqlCommand属性Connection 就是sql的连接语句
SqlCmd.Connection = SqlConn;
SqlConn.Open();
//当数据读取随着数据连接的关闭而关闭
SqlReader = SqlCmd.ExecuteReader(CommandBehavior.CloseConnection);
return SqlReader;
}
(4)SQL更新语句:
public void Update(string 字段id, string 字段1, string 字段2...string 字段N)
{
SqlCmd.CommandText = "update 数据表 set [字段名1]=@字段名1,[字段名2]='" +
字段2 + "',..,[字段名N]='" + 字段N + "' where [字段id]='" + 字段id + "'";
// SqlCmd.Parameters.AddWithValue("@lname", au_lname); 更新的字段可以有单引号
SqlCmd.Connection = SqlConn; //连接
SqlConn.Open(); //打开
SqlCmd.ExecuteNonQuery(); //执行
}
(5)SQL删除语句:(用法如上)
public void Delete(string 字段id)
{
SqlCmd.CommandText = "delete from [abc] where [字段id]='"+字段id+"'";
SqlCmd.Connection = SqlConn;
SqlConn.Open();
SqlCmd.ExecuteNonQuery();
}
6.新建一个aspx页面:加入一个GridView表
(1)在配置文件(web.config)中,加入下面一段话:
(2)为GridView新建数据源:对象 ->业务对象datasource->select\delete\update\insert 调用相关对象的方法->设置参数sql查询语句,比如select * from abc.
分页设置(属性分页中AllowPage 是否分页?、Pagesize 分页页数。属性行为中的EnableSortingAndPagingCallbacks 是否分页回传
编辑推荐:
1. ADO.NET和LINQ中古怪的事务特性
2. 怎样用C#和ADO.NET建立一个数据绑定网格
3. 使用ADO.NET2.0提升数据交互性能