从数据库行创建对象

时间:2020-03-05 18:46:39  来源:igfitidea点击:

假设我正在为应用程序构建数据访问层。通常,对于存储在数据库中的每种对象,我都有一个类定义。当然,实际的数据访问会以数据读取器,类型化或者非类型化数据集或者类似形式检索数据,通常使用在结果中每行创建一个对象所需的数据。

我们将如何在数据层中创建对象实例?是否会有一个接受数据行的构造函数?如果是这样,我们将如何确保其类型安全?还是让构造函数为要实例化的每个字段列出一个参数,即使可能有很多字段也是如此?我们会将此构造函数标记为"内部"吗?

解决方案

回答

如果我们对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);
        }