询问者
ASP.NET数据访问常见问题

常规讨论
-
1.1 如何在代码中设置ObjectDataSource控件的参数值?
1.2 DataSet和DataReader之间的不同是什么?
1.3 当ObjectDataSource控件和GridView控件一起使用时,我如何更改ObjectDataSource控件中的主键?
1.5 为何ObjectDataSource不能在TypeName属性里找到指定的类型?
1.6 如何在DataTable或DataSet对象中更新所有的修改?
1.7 当在类型化DataSet对象中调用存储过程时如何获得返回值?
1.9 当连接到数据库时,我为何得到错误"Timeout expired"?
1.10 我为何得到错误"Failed to enable constraints.One or more rows contain values violating non-null, unique, or foreign-key constraints."?
2.1 我如何在LINQ中执行一个事务?
2.2 我如何在LINQ中创建一个左外链接?
2.3 List<T> 和 IQueryable<T>之间的不同是什么?
2.4 我如何在LINQ中执行SQL-type LIKE 操作?
2.5 我如何通过使用LINQ去查询DataTable 对象?
If you have any feedback on this FAQ please send it to fbmsdn@microsoft.com
全部回复
-
1.1 如何在代码中设置ObjectDataSource控件的参数值? [回到顶端]
假设你想在ObjectDataSource控件的Select方法中设置参数的值,你可以处理Selecting事件去设置参数。例如:
protected void ObjectDataSource1_Selecting(object sender,
ObjectDataSourceSelectingEventArgs e)
{
e.InputParameters["accountID"] = 5;
}
1.2 DataSet 和 DataReader之间的不同之处? [回到顶端]
DataSet 对象是DataTable对象的集合,并包含了表之间关系的信息。DataSet 对象常用于从表中选择数据或者创建视图并访问其中的行。另外,DataSet提供了丰富的功能,如保存数据到XML文件并从XML文件加载数据。DataReader是一个被用于遍历通过查询得到的结果集的对象,它一次只能读取一行。如果你想只向前访问记录集,DataReader对象是最好的选择,因为它是在这种场景下的最高效方法。
同时, DataSet被用于断开连接,然而DataReader需要保持对数据库的连接。
相关资源:
1.3 当ObjectDataSource和GridView控件一起使用时,我如何更改ObjectDataSource控件中的主键? [回到顶端]
如果你需要在Business Logic Layer (BLL)更新主键,你必须在GridView控件中指定DataKeyNames属性和ObjectDataSource 控件中的OldValuesParameterFormatString属性。
例如:
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="SelectDecs"
TypeName="Job.Code.bll"
UpdateMethod="UpdateDec">
<UpdateParameters>
<asp:Parameter Name="original_id" Type="Int32" />
<asp:Parameter Name="id" Type="Int32" />
<asp:Parameter Name="value" Type="Decimal" />
</UpdateParameters>
</asp:ObjectDataSource>
位于Business Logic Layer的update方法的签名是:
[System.ComponentModel.DataObjectMethod(
System.ComponentModel.DataObjectMethodType.Update, true)]
public void UpdateDec(int original_id, int id, decimal value)
1.4 如何使用ADO.NET调用一个参数化的存储过程? [回到顶端]
当你通过使用ADO.NET调用参数化的存储过程时,你需要设置command对象的CommandType属性为"StoredProcedure", 并且你需要为每一个参数设置恰当的Direction属性。下面的例子说明了如何调用参数化的存储过程:
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter id = cmd.Parameters.Add("@id", SqlDbType.Int, 4);
id.Direction = ParameterDirection.Input;
SqlParameter uName = cmd.Parameters.Add("@uName",SqlDbType.Char,20);
uName.Direction = ParameterDirection.Output;
SqlParameter ret = cmd.Parameters.Add("@Return_value",SqlDbType.Int,4);
ret.Direction = ParameterDirection.ReturnValue;
id.Value = 2;
conn.Open();
cmd.ExecuteNonQuery();
Label1.Text = uName.Value;
Label2.Text = ret.Value;
1.5 为何ObjectDataSource不能在TypeName属性里找到指定的类型? [回到顶端]
检查项目的引用,并确保源代码或程序集包含的类型是在正确的位置,ObjectDataSource控件的TypeName属性中指定类型应当在App_Code文件夹或GAC中, 如果不能从相关目录中的程序集中装载类型,一个运行时异常将抛出。TypeName格式应当由命名空间和类型名组成。例如:
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="SelectDecs"
TypeName="Job.Code.Test" >
注意:你可以使用Fusion log viewer工具检查绑定失败。
相关资源
"Debugging Assembly Loading Failures", blog entry by Suzanne Cook
1.6 如何在DataTable或DataSet对象中更新所有的修改? [回到顶端]
你可以在CommandBuilder对象帮助下发送DataTable 或 DataSet对象的所有更新到数据库。CommandBuilder 对象帮助生成DataTable 或 DataSet 对象中的所有改变为可以被Command对象执行的SQL语句。
更新所有的变化到SQL Server, 使用如下例的代码:
da.Fill(ds, "tab1");
SqlCommandBuilder cb = new SqlCommandBuilder(da);
ds.Tables[0].Rows[0]["username"] = "Modified Name";
da.UpdateCommand = cb.GetUpdateCommand();
da.Update(ds);
1.7 当在类型化DataSet对象中调用存储过程时如何获得返回值? [回到顶端]
存储过程作为类型化DataSet对象的一个方法被调用。然而,当你从存储过程返回一个值时,你不能直接获得它。如果你想从存储过程返回值,你一定要在产生TableAdapter对象的部分类中写一个新的方法,正如下面的例子所示的。输入参数是TableAdapter实例中方法的索引,你可以在类型化DataSet对象的设计器中看到这个值。
partial class UsersTableAdapter
{
public object GetReturnValue(int commandIndex)
{
return this.CommandCollection[commandIndex].Parameters[0].Value;
}
}
那么你可以调用方法获得返回值,如下例所示:
da.CallSP(xx, xx);
int returnValue = int.Parse(da.GetReturnValue(2).ToString());
1.8 什么是SQL注入攻击(SQL Injection)及如何阻止? [回到顶端]
SQL注入攻击让恶意用户改变SQL命令或者执行任意的SQL命令。典型的SQL 注入攻击是取代或添加到那些已经作为应用程序一部分合理执行的查询。当你使用字符串连接去创建带有从终端用户输入值的SQL语句时,发动SQL注入攻击将变的可行。例如,下面SQL语句可能被用于修改用户的密码:
sql = "select * from UserInfo where password='" + password + "'";
恶意用户可以利用下面的输入绕过密码检查:
password = "' or 1=1 --";
更糟糕的是恶意用户可能利用命令分隔符(;)去添加SQL命令到用户输入,如下面的例子:
; DROP TABLE
为帮助避免SQL注入, 在SQL查询中使用命令参数可帮助验证用户的输入。单引号将代表候补格式通过验证。这个例子将不会引起SQL注入尽管密码并非有效:
cmd = new SqlCommand("select * from UserInfo where password=@ password", conn);
SqlParameter pwd = cmd.Parameters.Add("@password", SqlDbType.Char, 20);
pwd.Direction = ParameterDirection.Input;
pwd.Value = "' or 1=1 --";
1.9 当连接到数据库时,为何得到错误"Timeout expired"? [回到顶端]
SqlConnection或SqlCommand 对象的time-out值或许太小了。SqlConnection 类的默认time-out值是 15 秒。你可以通过这些方式把它设置的更长:
·设置SqlConnection对象的ConnectionTimeout属性。
·在连接字符串中设置Connect Timeout 属性。
SqlCommand 类的默认 time-out 值是30 秒。要改变它,设置 SqlCommand 对象的CommandTimeout属性。
1.10 为何得到错误 "Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints."? [回到顶端]
这个问题是由于在project的XSD schema中数据列的已定义最大值和数据库列的最大值之间的差异引起的。 当数据库中其中一个列比对应的XSD schema 中预定义的列大小大时,这个错误将发生。为解决这个问题,更新XSD schema 的表或修改数据库中列的最大值。
1.11 我如何加快数据库中的缓慢查询? [回到顶端]
缓慢的查询可能有几个原因。这儿是一些通常建议:
·表是否非常的大? 如果是,把它分拆成更小的表是否可能?
·当你构建SQL语句时,仅指定你需要的数据字段。
·添加索引到最经常使用的字段。
If you have any feedback on this FAQ please send it to fbmsdn@microsoft.com -
2 LINQ [回到顶端]
2.1 我如何在LINQ中执行一个事务? [回到顶端]
在LINQ中使用TransactionScope 类去执行内在的事务,下面的例子显示了如何在LINQ中创建事务:
using (TransactionScope scope = new TransactionScope())
{
try
{
// ...
ctx.SubmitChanges();
// ...
ctx.SubmitChanges();
}
catch (Exception ex)
{
Response.Write("An error has occurred -- transaction
will automatically be rolled backed.");
}
scope.Complete();
}
你一定要引用System.Transactions 程序集并添加命名空间System.Transactions 到项目中。你同时要确保Distributed Transaction Coordinator Service Windows 服务正在运行。
相关链接
2.2 我如何在LINQ中穿件一个左外链接? [回到顶端]
在LINQ中使用关键字join 和 into 去执行左外连接,如下面的例子所示:
var sel = from u in ctx.Tags
join p in ctx.ArticlesTags on u.TagID equals p.TagID into UP
from p in UP.DefaultIfEmpty()
select new
{
UT = u.TagID,
UT1 = u.Text,
UT2 = p.Info
};
2.3 List<T> 和 IQueryable<T>之间的不同是什么? [回到顶端]
你可以以List<T> 或 IQueryable<T>返回LINQ的查询结果。在这两种类型间有一些不同。
List<T> 将在内存中为持久型数据立即创建一个新的列表对象。如果在这些表中有任何关联,关联的信息将会是null.。IQueryable<T> 将不会提取数据直到你通过使用foreach, DataBind, ToList等遍历这些数据源。如果这些表有关联,关联信息将不为null并可以被使用。下面的例子显示了这两个类之间的差异。
// 返回List<T> 将失败当参照相关的UserInfos对象。
List users = res.ToList();
var ss = users.Where(p => p.UserInfos.ID != 3);
// 返回 IQueryable<T> 将成功。
IQueryable users = res.AsQueryable();
var ss = users.Where(p => p.UserInfos.ID != 3);
2.4 我如何在LINQ中执行SQL-type LIKE 操作? [回到顶端]
你可以以两种方式创建LIKE 功能 。你可以使用Contains, StartsWith, or EndsWith 方法,如下面的例子所示:
var dd = from p in ctx.Users
where p.email.Contains("xx") || p.userName.StartsWith("xx") ||
p.userName.EndsWith("xx")
select p;
你同时可以使用SqlMethods 类, 它包含和等价的SQL操作有相同语义的Like方法。下面的例子演示了如何使用该类:
var dd = (from p in ctx.Users
where SqlMethods.Like(p.userName, "%Jiang%") &&
SqlMethods.Like(p.email,"%WWW%")
orderby p.accountID
select p).Take(10);
2.5 我如何通过使用LINQ去查询DataTable 对象? [回到顶端]
LINQ 能够查询任何实现了IEnumerable接口的数据源。为查询DataTable 对象,首先调用DataTable类的AsEnumerable方法.。接着你可以使用LINQ去查询数据。下面的例子演示了如何去做:
var nostr = from u in dt.AsEnumerable()
where u.Field("m").ToString().ToUpper().StartsWith("3")
select new
{
MONEY = u.Field("m"),
TIME = u.Field("t"),
EXT = "Extra Column"
};
If you have any feedback on this FAQ please send it to fbmsdn@microsoft.com