将数据库表建模为类

时间:2009-06-06 05:55:50

标签: c# .net database

我正在设计一个主要由数据库驱动的个人项目。我正在尝试为如何处理数据库互操作提出一个好的设计,我想在StackOverflow上有经验的思想输入。

以下是好的设计,还是有更标准的方式来处理与数据库的通信?

修改
我主要是在寻找关于是否通常将连接细节分解为自己的基类的反馈,以及逻辑单元/类的子类。
为此,我修改了下面的代码,添加了一个检索方法,以补充原始问题中的插入方法。我还修改了它以显示它需要/返回(取决于方法)业务对象 例如,在示例中,我们在整个应用程序中使用Employee类,但是有一个EmployeeDb类(继承自Database),它处理与数据库之间的持久性。

我喜欢这个,因为它将存储实现细节保留在业务对象之外,但不喜欢它,因为它强烈地耦合了Employee和EmployeeDB类。

// Abstract Base Class to handle specifics of the database connection
abstract class Database : IDisposable  
{  
    protected OleDbConnection m_Conn;  

    public bool Open()  
    {  
        // Open a connection to the database  
    }  

    public void Dispose()  
    {  
        if (m_Conn != null)  
        {  
           m_Conn.Dispose();  
        }
    }  
}  

// Specific classes for each table, with methods for CRUD functions
class EmployeeDB : Database  
{  
    public bool AddTestData(Employee emp)  
    {  
        // Construct SQL to add Employee class members to the DB, breaking
        // them out into their component tables as needed  
    }  

    public List<Employee> GetEmployeeByProject(string project)  
    {  
        // Retrieve recordset of all employees on the project,
        // breaking them out into instances of the Employee class

        // Add each new Employee object to a list, and return the list
        // to the caller.
    }  
}  

// Specific classes for each table (or logical unit, since obviously 
// most of the time we'll need to join a few tables to get what 
// we want), with methods for CRUD functions
    void AddSomethingToTheDatabase()
    {
        using (TestDataDB td = new TestDataDB())
        {
            td.Open(Application.StartupPath);
            string NewID = td.AddTestData(txtAddMe.Text);
        }
    }

// Abstract Base Class to handle specifics of the database connection abstract class Database : IDisposable { protected OleDbConnection m_Conn; public bool Open() { // Open a connection to the database } public void Dispose() { if (m_Conn != null) { m_Conn.Dispose(); } } } // Specific classes for each table, with methods for CRUD functions class EmployeeDB : Database { public bool AddTestData(Employee emp) { // Construct SQL to add Employee class members to the DB, breaking // them out into their component tables as needed } public List<Employee> GetEmployeeByProject(string project) { // Retrieve recordset of all employees on the project, // breaking them out into instances of the Employee class // Add each new Employee object to a list, and return the list // to the caller. } } // Specific classes for each table (or logical unit, since obviously // most of the time we'll need to join a few tables to get what // we want), with methods for CRUD functions void AddSomethingToTheDatabase() { using (TestDataDB td = new TestDataDB()) { td.Open(Application.StartupPath); string NewID = td.AddTestData(txtAddMe.Text); } }

3 个答案:

答案 0 :(得分:7)

您确定不想尝试Object Relational Mapper吗?

可能Linq to SQLnHibernate

答案 1 :(得分:1)

有很多方法可以做到这一点,绝大多数OO人都不同意我的意见。

我建议你采取一种更多的关系方法,而不是为表提供类,有关系的类。在关系理论中,查询的结果就像查询中使用的关系一样;实际上你从来没有看到任何查询结果,因为即使要获取表格的内容,你需要SELECT * FROM x,它正在应用身份函数。

所以使用您已经拥有的关系语言进行查询(构建一个框架来为您编写查询,如果您需要那么远),查询结果应该是您的类,或者,如果你不是在一种易于创建新类的语言,一些其他类型的结构(如对象)。然后,您只需直接使用这些结果。

答案 2 :(得分:0)

我会自发地想要将数据库依赖关系从对象中断开。您可以从创建一个定义TestData对象的存储方法的接口开始(在我的示例中,只有Add方法,但这当然可以扩展):

public interface ITestDataRepository
{
    void Add(TestData data);
}

下一步是创建TestData类。该类的一个特性可能是接受ITestDataRepository作为构造函数中的参数,并公开Add方法:

public class TestData
{
    public TestData(ITestDataRepository repository)
    {
        Repository = repository;
    }

    public ITestDataRepository Repository { get; private set; }

    public string SomeData { get; set; }

    public void Add()
    {
        if (Repository != null)
        {
            Repository.Add(this);
        }
    }
}

最后要做的是实现您要使用的数据机制。在这种情况下OleDb,但它也可以是Xml,SqlServer或任何其他形式的数据存储:

public class DbTestDataRepository : ITestDataRepository
{
    const string InsertionQuery = @"INSERT INTO TEST (TESTDATA) VALUES (?)";

    public void Add(TestData data)
    {

        using (OleDbConnection conn = DbUtil.GetConnection())
        using (OleDbCommand command = new OleDbCommand(InsertionQuery, conn))
        {
            command.Parameters.Add("@p1", OleDbType.BSTR).Value = data.SomeData;

            using (OleDbDataReader reader = command.ExecuteReader())
            {
                // perhaps log something?
            }
        }
    }

}

上述设计在您的问题设计中提供了几个不同的优势:

  • 您的对象模型与具体存储实现之间没有硬性关系;例如,您可以在不更改TestData对象的情况下更改存储机制。
  • 可以测试;由于对数据存储机制的唯一要求是它实现了存储库接口,因此可以轻松地模拟数据存储,这使得单元测试更容易。
  • 在这个具体的数据库代码中,连接对象仅存在很短的时间,这通常是推荐的方式(DbUtil.GetConnection方法是我“发明”的东西;它不存在,但我通常喜欢集中设置连接的代码,所以我不会将它全部乘以我的代码)。

这些只是一些简单的想法(例如,我没有测试代码而不是编译它。)