从数据库行创建对象
假设我正在为应用程序构建数据访问层。通常,对于存储在数据库中的每种对象,我都有一个类定义。当然,实际的数据访问会以数据读取器,类型化或者非类型化数据集或者类似形式检索数据,通常使用在结果中每行创建一个对象所需的数据。
我们将如何在数据层中创建对象实例?是否会有一个接受数据行的构造函数?如果是这样,我们将如何确保其类型安全?还是让构造函数为要实例化的每个字段列出一个参数,即使可能有很多字段也是如此?我们会将此构造函数标记为"内部"吗?
解决方案
回答
如果我们对DataRow或者SqlDataReader不满意,则应该查看像Linq to Sql或者nHibernate这样的ORM系统,而不是自己重新发明轮子。
(顺便说一下,这称为" ActiveRecord"模式)
回答
我强烈建议我们使用ORM工具。即使是简单的项目,也可以快速,安静地使用ORM ...特别是看看Castle的ActiveRecord工具(它位于NHibernate之上以简化模型声明)。
回答
那些会支持更复杂的查询作为数据源吗?例如,这可能会联接到其他几个表中,即使只是确定在特定情况下应返回哪些记录?他们会允许我们将自己的方法添加到他们使用的类中吗?
回答
我已经通过反射来实现了。我从对象的Select语句中命名列的位置。
假定我们有一个模板化的帮助程序类。如果我们想自己将其放置在对象上,则只需将所有T替换为该对象即可。
这是一个例子:
private T ObjectFromRow(DataRow row) { Type t = typeof(T); T newObj = (T)Activator.CreateInstance(t); System.Reflection.PropertyInfo[] properties = t.GetProperties(); for (int i = 0; i < properties.Length; i++) { if (!properties[i].CanWrite) { continue; } if (!row.Table.Columns.Contains(properties[i].Name)) { continue; } if (row[properties[i].Name] == DBNull.Value) { continue; } if (properties[i].PropertyType == typeof(string)) { properties[i].SetValue(newObj, row[properties[i].Name], null); } else if (properties[i].PropertyType == typeof(double)) { properties[i].SetValue(newObj, double.Parse(row[properties[i].Name].ToString()), null); } else if (properties[i].PropertyType == typeof(int)) { properties[i].SetValue(newObj, int.Parse(row[properties[i].Name].ToString()), null); } else if (properties[i].PropertyType == typeof(DateTime)) { properties[i].SetValue(newObj, DateTime.Parse(row[properties[i].Name].ToString()), null); } else if (properties[i].PropertyType == typeof(bool)) { properties[i].SetValue(newObj, bool.Parse(row[properties[i].Name].ToString()), null); } } return newObj; }
回答
@Joel(例如:复杂的查询,联接等)
NHibernate和Castle ActiveRecord工具可以处理非常复杂的查询,并通过类关系和一个完整的" Expression"类(可以将其添加到查询方法中)或者使用" Hibernate Query Language"(HQL)进行联接。
我们可以通过Google浏览这些详细信息中的任何一个,查看官方文档,或者查看NHibernate夏季真棒截屏。
回答
作为NHibernate和Castle的替代产品,我们可以看看SubSonic。这也使用ActiveRecord,但比NHibernate更像是瑞士军刀。
编辑:
这是SubSonic文档中的一个示例:
Simple Select with string columns int records = new Select("productID"). From("Products").GetRecordCount(); Assert.IsTrue(records == 77); Simple Select with typed columns int records = new Select(Product.ProductIDColumn, Product.ProductNameColumn). From<Product>().GetRecordCount(); Assert.IsTrue(records == 77);
还有一些其他示例:
Standard Deviation const double expected = 42.7698669325723; // overload #1 double result = new Select(Aggregate.StandardDeviation("UnitPrice")) .From(Product.Schema) .ExecuteScalar<double>(); Assert.AreEqual(expected, result); // overload #2 result = new Select(Aggregate.StandardDeviation(Product.UnitPriceColumn)) .From(Product.Schema) .ExecuteScalar<double>(); Assert.AreEqual(expected, result); // overload #3 result = new Select(Aggregate.StandardDeviation("UnitPrice", "CheapestProduct")) .From(Product.Schema) .ExecuteScalar<double>(); Assert.AreEqual(expected, result); // overload #4 result = new Select(Aggregate.StandardDeviation(Product.UnitPriceColumn, "CheapestProduct")) .From(Product.Schema) .ExecuteScalar<double>(); Assert.AreEqual(expected, result);
还有一些通配符方法:
[Test] public void Select_Using_StartsWith_C_ShouldReturn_9_Records() { int records = new Select().From<Product>() .Where(Northwind.Product.ProductNameColumn).StartsWith("c") .GetRecordCount(); Assert.AreEqual(9, records); }