65.9K
CodeProject 正在变化。 阅读更多。
Home

使用属性和反射持久化业务对象的 Datalayer - 第三部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.82/5 (27投票s)

2002年3月21日

5分钟阅读

viewsIcon

216540

downloadIcon

1976

通过属性和反射将业务对象持久化。

目录

引言

这是本系列的最后一篇文章。在第一篇文章中,我们了解了如何使用属性为类提供描述性信息。在第二篇文章中,我们了解了如何使用 System.Reflection 命名空间提取这些信息。现在,我们将创建一个 DAL 库,该库能够持久化任何具有自我描述信息的对象。

设计 DAL 库

在创建此 DAL 库时,我希望它可以与 SQL Server 数据提供程序或 OleDb 数据提供程序一起使用。它也可以扩展到其他数据提供程序。我们可以将此库分为以下几个部分:

实用类

类 DALQueryBuilder

正如第一篇文章中所述,此类有助于构建 SQL 语句以更新数据库中的对象。

类 DALParameter

一个通用的参数类,用于存储过程。

类 DALException

它是一个 System.Exception 的派生类。每当我们的数据库操作发生异常时,就会抛出它。它提供了有关发生的错误的更多信息。

属性类

所有派生自 System.Attribute 的类,这些类在第一篇文章中已提到。

DAL 本身

类 DALEngine

抽象基类,负责数据库操作,使数据库编程变得更加容易。它具有虚拟和抽象方法,每个数据提供程序都有不同的实现。除了返回数据库的 DataSets、DataReaders 的方法外,它还有处理用 DAL 属性标记的对象的方法。

类 DALSqlEngine

DALEngine 的 SQL Server 实现。

类 DALOleDbEngine

DALEngine 的 OleDb 实现。

深入了解 DALEngine 类

这是 DALEngine 类的声明。

public abstract class DALEngine : IDisposable
{
    //
    // private data members
    //
    IDbConnection conn       = null;
    string connectionString  = "";
    ArrayList parameters = new ArrayList();
    bool canClose = true;


    // constructor
    public DALEngine(string connectionString);

    public bool CanClose;
    public string ConnectionString;


    protected IDbConnection Connection;
    protected ArrayList Parameters;

    public void Close();
    public void Dispose();


    //
    // methods that must be override with a specific data provider 
    // implementation please see the implementation of DALSqlEngine 
    // or DALOleDbEngine
//
protected abstract IDbConnection GetConnection(); protected abstract IDbCommand CreateCommand(string spName); public abstract void ExecSP_DataSet(string spName, DataSet dataSet, string tableName); public abstract void ExecQuery_DataSet(string query, DataSet dataSet, string tableName); // // related to stored procedure parameters // public DALParameter GetParameter(string name); void UpdateOutputParameters(IDbCommand cmd); public void AddParameter(DALParameter param); public void ClearParameters(); // // for those that use stored procedures // public IDataReader ExecSP_DataReader(string spName); public IDataReader ExecSP_DataReader(string spName,
CommandBehavior behavior); public object ExecSP_Scalar(string spName); public int ExecSP_NonQuery(string spName); // // methods for those that use plain SQL statements // public IDataReader ExecQuery_DataReader(string query,
CommandBehavior behavior); public IDataReader ExecQuery_DataReader(string query); public object ExecQuery_Scalar(string query); public int ExecQuery_NonQuery(string query); // // Business objects methods // public static object CreateFromReader(IDataReader reader, Type objType); public object RetrieveObject(object keyValue, Type objType); public int RetrieveChildObjects(object foreignKeyValue, ArrayList objects, Type childType); void UpdateObjectSql(object o, DataTableAttribute dataTable); void UpdateObjectStoredProcedure(object o, DataTableAttribute dataTable); public void UpdateObject(object o); public void UpdateObjects(IEnumerable enumObjects); }

正如您可能想到的,我们只会看最后 6 个方法。其他方法很容易理解,因为市面上有很多类似的实现。

CreateFromReader 方法返回 objType 类型的实例。由于我们使用 DataReaders 从数据库获取数据,因此这是用于返回业务对象实例的方法。它是静态的,因为它不需要数据库连接,只需要一个可以读取的 DataReader。

RetrieveObject 方法返回 objType 类型的一个实例。此实例的属性值将根据具有 keyValue 参数值等于 keyValue 的唯一表行进行设置。在此方法内部,它会创建一个带有 WHERE 子句的 SELECT 语句。RetrieveChildObjects 创建 childType 类型的对象实例,其中外键等于 foreignKeyValue 参数值。创建的这些实例将被添加到 objects 参数中。

UpdateObjectUpdateObjects 方法会在数据库中进行更新。此更新操作可以使用存储过程(如果类具有设置了 UpdateStoreProcedure 属性的 DataTable 属性)或普通 SQL 语句进行。私有方法 UpdateObjectSqlUpdateObjectStoredProcedure 负责执行这些更新。

优点

首先,使用 DAL 本身将使您的数据库编程更加容易。即使您不打算像我一样进行数据库编程,我也强烈建议您使用一个 DAL 库,该库已经实现了繁琐且重复的任务。您不必使用这个,但至少使用一个。

但是,如果您确实使用业务对象来处理数据库,那么这个库将为您节省大量时间和精力。当数据库发生更改时,只需要审查一小部分源代码。并且可以使用您自己的工具来完成,这与我们在本文第二部分创建的工具的作用相反。

为了向您展示从数据库获取/更新对象的容易程度,我包含了一个示例应用程序以及完整的 DAL 库代码。下面是该示例应用程序的代码片段,因此您可以对其功能有所了解。

当然,DAL 肯定有很多 bug,这也是我将代码发布在此处的原因之一。也许您有更好的解决方案和代码来实现该库的目标。请随时向我提问和贡献,因为我非常希望能改进这个库。

为了开发示例应用程序,我创建了自己的 DAL 类,该类继承自 DALSqlEngine,因为我将使用 SQL Server。这就是我的 DAL 类的代码。

public class DAL : DALSqlEngine
{
    const string CONN_STRING = "server=localhost;uid=sa;pwd=;database=pubs";

    public DAL() : base(CONN_STRING)
    {

    }

    public ArrayList GetCustomerDependents(Customer customer)
    {
        ArrayList result = new ArrayList();

        RetrieveChildObjects(customer.Id, result, typeof(CustomerDependent));

        return result;
    }

    public void UpdateCustomerDependents(Customer customer)
    {
        UpdateObjects(customer.Dependents);
    }
}

很简单,不是吗?现在是应用程序代码,它只是插入/更新联系人/客户和客户的从属关系。

public static void Main()
{

    DAL dal = new DAL();

    try
    {

        Contact contact = new Contact();
        contact.Name = "Joao Cardoso";
        contact.Age  = 23;
        contact.Address = "Av. Rio Branco, 202/121";
        contact.Address2 = "Centro";
        contact.PostalCode = "09029-901";
        contact.City = "Sao Paulo";
        contact.State =  "SP";
        contact.Country = "Brazil";

        dal.UpdateObject(contact);
        Console.WriteLine(contact);


        Contact joaoCardoso = (Contact)dal.RetrieveObject(1, typeof(Contact));
        joaoCardoso.Age++;
        Console.WriteLine(joaoCardoso);
        Console.WriteLine("");


        Customer customer = new Customer();
        customer.Name = "Paul Noyter";
        customer.Age  = 34;
        customer.Address = "All St, 2202/2121";
        customer.Address2 = "Downville";
        customer.PostalCode = "90931";
        customer.City = "Los Angeles";
        customer.State =  "CA";
        customer.Country = "United States";
        customer.TotalPurchased += 1900.87M;
        customer.NumberOfPurchases++;

        dal.UpdateObject(customer);


        Customer paul = (Customer)dal.RetrieveObject(1, typeof(Customer));
        Console.WriteLine(paul);

        paul.TotalPurchased += 100M;
        paul.NumberOfPurchases++;
        dal.UpdateObject(paul);

        if (paul.Dependents.Count == 0)
        {
            CustomerDependent dependent = paul.NewDependent();
            dependent.Name = "Marie Noyter";
            dependent.Age = 31;
            paul.Dependents.Add(dependent);


            dependent = paul.NewDependent();
            dependent.Name = "Mark Noyter";
            dependent.Age = 10;
            paul.Dependents.Add(dependent);


            dependent = paul.NewDependent();
            dependent.Name = "Claudia Snorg";
            dependent.Age = 32;
            dependent.Relationship = CustomerRelationship.Friend;
            paul.Dependents.Add(dependent);

            dal.UpdateCustomerDependents(paul);
        }
        else
        {
            Console.WriteLine("Dependents of {0}", paul.Name);

            foreach(CustomerDependent dependent in paul.Dependents)
            {
                Console.WriteLine("<Dependent>{0} - {1} [{2}]", dependent.Id, 
                                  dependent.Name, dependent.Relationship);
                dependent.Relationship = CustomerRelationship.Family;
            }

            dal.UpdateCustomerDependents(paul);
        }

    }
    finally
    {
        dal.Dispose();
    }
}

限制

当然,它也有局限性。有些可以通过扩展 DAL 或属性来解决。如果您不使用自动编号键列,那么更新对象的唯一方法是使用存储过程。问题是 DAL 无法确定对象应该在数据库中插入还是更新。您可以扩展 DAL,使其检查已知的属性或接口,以提供所需的信息;目前它不支持具有多个键列的表;性能不如将 SqlParameters 推送到 SqlCommand 中快,但您将有更多时间来处理您的业务类,而不是数据库编程;它不允许使用存储过程从数据库检索对象。但嘿,这很容易做到;肯定还有其他,但这些是我在写这篇文章时记得的。

结论

.NET 世界中有许多进行数据库编程的方法。Visual Studio .NET 使您可以使用强类型 DataSet,这可以为您节省大量时间,因为它会生成代码。DataSet 本身没有问题,但我更喜欢使用业务对象。当我开始开发此类类时,我开始注意到编写每个类需要花费多长时间。通过这里介绍的库,您可以看到使用业务对象开发数据库应用程序的效率有多高。

© . All rights reserved.