在Using语句中从DataLayer返回DataReader
我们有很多数据层代码遵循这个非常通用的模式:
public DataTable GetSomeData(string filter) { string sql = "SELECT * FROM [SomeTable] WHERE SomeColumn= @Filter"; DataTable result = new DataTable(); using (SqlConnection cn = new SqlConnection(GetConnectionString())) using (SqlCommand cmd = new SqlCommand(sql, cn)) { cmd.Parameters.Add("@Filter", SqlDbType.NVarChar, 255).Value = filter; result.Load(cmd.ExecuteReader()); } return result; }
我想我们可以做得更好一点。 我现在的主要抱怨是它强制将所有记录加载到内存中,即使对于大型集合也是如此。 我希望能够利用DataReader的能力,一次只能在ram中保留一条记录,但如果我直接返回DataReader,则在离开using块时会切断连接。
如何改进这一点以允许一次返回一行?
再一次,为问题撰写我的想法的行为揭示了答案。 具体来说,我写的“一次一行”的最后一句话。 我意识到我并不在乎它是一个datareader,只要我可以逐行枚举它。 这导致我这样:
public IEnumerable GetSomeData(string filter) { string sql = "SELECT * FROM [SomeTable] WHERE SomeColumn= @Filter"; using (SqlConnection cn = new SqlConnection(GetConnectionString())) using (SqlCommand cmd = new SqlCommand(sql, cn)) { cmd.Parameters.Add("@Filter", SqlDbType.NVarChar, 255).Value = filter; cn.Open(); using (IDataReader rdr = cmd.ExecuteReader()) { while (rdr.Read()) { yield return (IDataRecord)rdr; } } } }
一旦我们转到3.5并且可以开始在结果上使用其他linq运算符,这将更好地工作,我喜欢它,因为它让我们开始考虑每个层之间的“管道”,以便返回大量的查询结果。
缺点是对于持有多个结果集的读者来说会很尴尬,但这种情况非常罕见。
更新
自从我在2009年第一次开始使用这种模式以来,我已经了解到最好是我还使它成为通用的IEnumerable
返回类型,并添加一个Func
参数来将DataReader状态转换为业务对象。环。 否则,延迟迭代可能会出现问题,因此您每次都会在查询中看到最后一个对象。
您想要的是支持的模式,您必须使用
cmd.ExecuteReader(CommandBehavior.CloseConnection);
并using()
从GetSomeData()方法中删除它们。 呼叫者必须提供例外安全保证,以确保读者关闭。
在这些时候,我发现lambda可以很有用。 考虑一下,而不是给我们提供数据的数据层,让我们给数据层我们的数据处理方法:
public void GetSomeData(string filter, Action processor) { ... using (IDataReader reader = cmd.ExecuteReader()) { processor(reader); } }
然后业务层会调用它:
GetSomeData("my filter", (IDataReader reader) => { while (reader.Read()) { ... } });
关键是yield
关键字。
与Joel的原始答案类似,更加充实:
public IEnumerable Get(string query, Action parameterizer, Func selector) { using (var conn = new T()) //your connection object { using (var cmd = conn.CreateCommand()) { if (parameterizer != null) parameterizer(cmd); cmd.CommandText = query; cmd.Connection.ConnectionString = _connectionString; cmd.Connection.Open(); using (var r = cmd.ExecuteReader()) while (r.Read()) yield return selector(r); } } }
我有这个扩展方法:
public static void Parameterize(this IDbCommand command, string name, object value) { var parameter = command.CreateParameter(); parameter.ParameterName = name; parameter.Value = value; command.Parameters.Add(parameter); }
所以我打电话给:
foreach(var user in Get(query, cmd => cmd.Parameterize("saved", 1), userSelector)) { }
这是完全通用的,适合任何符合ado.net接口的模型。 在枚举集合之后处理连接和读取器对象。 无论如何使用IDataAdapter
的Fill
方法填充DataTable
可能比DataTable.Load
更快
我从来不是让数据层返回通用数据对象的忠实粉丝,因为这几乎解决了将代码分离到自己的层中的问题(如果未定义接口,如何切换数据层? )。
我认为最好的办法是让所有这样的函数返回您自己创建的自定义对象列表,稍后在数据中将您的过程/查询调用到datareader中,然后遍历创建列表。
这将使一般处理更容易(尽管创建自定义类的初始时间),使得更容易处理您的连接(因为您不会返回与之关联的任何对象),并且应该更快。 唯一的缺点是所有内容都会像你提到的那样加载到内存中,但我不认为这会引起关注(如果是的话,我认为需要调整查询)。
上述就是C#学习教程:在Using语句中从DataLayer返回DataReader分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/1016638.html