数据访问层的设计模式

12

你可能会觉得这是作业,对此我很抱歉。我已经搜索过了,但找不到合适的答案。

所以我的问题是:

我有几个类,每个类都有一个保存方法。因此我创建了一个独立的数据库处理类。

namespace HospitalMgt.Data
{
    public static class DBConnection
    {
        public static string constr = "Data Source=ABD;Initial Catalog=HospitalMgt;User Id=sa;Password=123";
        public static SqlConnection con;
      //  public static SqlCommand com;

        public static SqlConnection OpenConnection()
        {
            con= new SqlConnection(constr);
            con.Open();
            return con;
        }

    }
}

然而,我认为不应该使用DBConnection类来实现所有的类。

我的问题:

  1. 哪种设计模式适合解决这个问题?
  2. 将DBConnection创建为类好吗?(还是应该作为接口)

我找到了一些关于使用工厂方法的DA层的文章,但根据我的知识,那种模式并不适合我的情况。


看一下仓储模式,它听起来可能会对你有所帮助。 - BenjaminPaul
如果你代码中的用户 ID 和密码是真实的,那么你应该立即隐藏它们。 - Steve Wellens
你正在尝试创建一个单例吗?如果是的话,你做错了。 - Steve Wellens
不,我不想尝试使用单例模式。我仍然找不到解决方案。 - DevT
@TilT 如果你可以使用其他库,我建议使用Spring应用程序框架。它卸载了很多数据库连接处理的东西,从而使你的实现类看起来简单而干净。 - user320587
5个回答

14

通常情况下,如果我无法使用任何现有的框架,我会同时使用仓储和活动模式。

为了简化起见,您可以仅使用仓储模式。我通常这样定义:

public interface IEntity<T> { }

//  Define a generic repository interface
public interface IRepository<TKey, TEntity>
    where TEntity : IEntity<TKey>
{
    void Add(TEntity entity);
    void AddRange(IEnumerable<TEntity> entities);
    IEntity<TKey> Get(TKey key);
    IEnumerable<TEntity> GetRange(IEnumerable<TKey> keys);
    IEnumerable<TEntity> GetAll();
    //  ..., Update, Delete methods
}

//  Create an abstract class that will encapsulate the generic code
public abstract class Repository<TKey, TEntity> : IRepository<TKey, TEntity>
    where TEntity : IEntity<TKey>
{
    protected Repository(/*parameter you may need to implement the generic methods, like a ConnectionFactory,  table name, entity type for casts, etc */) { }

    public override void Insert(IEntity<TKey> entity)
    {
        //  do the insert, treat exceptions accordingly and encapsulate them in your own and more concise Exceptions, etc
    }
    //  ...
}

//  Create the entities classes, one for each table, that will represent a row of that table
public class Car : IEntity<string> {/* Properties */}

//  Create a specific repository for each table
//  If the table have a composed key, just create a class representing it
public class CarRepository : Repository<string, Car>
{
    public CarRepository() {/* pass the base parameters */}

    // offer here your specific operations to this table entity
    public IEnumerable<Car> GetByOwner(PersonKey ownerKey)
    {
        //  do stuff
    }
}

显然,当你进行自己的实现时,必须考虑线程安全,并充分利用事务,特别是在不同实体仓库之间。

//  simple example
ITransaction t = TransactionFactory.GetNewTransaction();
t.begin();
try{
    //  create person entity
    personRepository.Add(person, t);
    //  create cars assigned to person
    carRepository.AddRange(cars, t);
    t.commit();
}catch(Exception){
    t.rollback();
}

请确保您真的想要创建自己的DAL,因为它可能变得非常复杂,尤其是在尝试开发最通用的解决方案时。


5

4

我建议使用ORM,Entity Framework或NHibernate都可以。这样您就不必担心数据库上下文或创建SQL语句。


ORM或EF不是设计模式 - 它们是数据层库。 - Ian

3

虽然这篇文章有些老,但我还是忍不住想发表一下我的想法。

我发现使用带有ORM的RepositoryUnitOfWork是一个不错的方法。这可以最大程度地减少问题。

上面链接中提到的UoW可以注入到Repository中。这增加了使用的灵活性。此外,所有数据库通信代码都集中在一个地方。该示例并不完整,但是是一个起点。

上面链接中提到的Repository模式实际上是一个通用基类。您可以为每个具体的Repository创建一个派生自它的新类。

通用Repository被认为是反模式;互联网上有很多文章对此进行了解释。

为什么通用Repository是反模式?

  1. Repository是正在建模的领域的一部分,并且该领域不是通用的。
    • 并非每个实体都可以被删除。
    • 并非每个实体都可以添加。
    • 并非每个实体都有存储库。
    • 查询差异巨大;存储库API变得与实体本身一样独特。
    • 对于GetById(),标识符类型可能不同。
    • 更新特定字段(DML)是不可能的。
  2. 通用查询机制是ORM的责任。
    • 大多数ORM公开的实现与通用存储库非常相似。
    • 存储库应该使用ORM公开的通用查询机制来实现实体的SPECIFIC查询。
  3. 无法使用复合键。
  4. 它在服务中泄漏了DAL逻辑。
    • 如果您接受谓词条件作为参数,则需要从服务层提供。如果这是ORM特定的类,则会将ORM泄漏到服务中。

我建议您阅读这些文章(12345),解释为什么通用存储库是反模式。

解决方案:

  1. 编写一个抽象的通用仓储库,由具体的仓储库进行包装。这样,您可以控制公共接口,同时获得来自通用仓储库的代码重用优势。
  2. 使用通用仓储库,但使用组合而不是继承,并且不将其作为契约暴露给域。

无论哪种情况,都不要将通用仓储库暴露给调用代码。此外,也不要从具体仓储库中暴露IQueryable


2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接