简体   繁体   English

数据访问层的设计模式

[英]Design pattern for Data Access Layer

You might feel this is homework, for that I am sorry. 您可能会觉得这是家庭作业,对不起。 I have searched but couldn't find a proper answer. 我已经搜索过,但是找不到正确的答案。

So my question is: 所以我的问题是:

I have several classes and each class has a method to save. 我有几个类,每个类都有一个保存方法。 So I created a separate class for database handling. 因此,我为数据库处理创建了一个单独的类。

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;
        }

    }
}

However, I don't think it's suitable to implement all the classes with a DBConnection class. 但是,我认为用DBConnection类实现所有类并不适合。

My question : 我的问题 :

  1. What design pattern is suited to overcome this problem? 哪种设计模式适合克服此问题?
  2. Is it good practise to create DBConnection as class? 将DBConnection创建为类是一种好习惯吗? (Or should it be an Interface) (或者应该是一个接口)

I found a few articles about DA layers using the Factory method, but according to my knowledge, that pattern does not suit my situation. 我使用Factory方法发现了一些有关DA层的文章,但是据我所知,这种模式不适合我的情况。

Normally, if I can't use any existing framework, I use both the Repository and Active patterns. 通常,如果我不能使用任何现有框架,那么我会同时使用存储库和活动模式。

For simplicity, you could use only the Repository pattern. 为简单起见,您只能使用存储库模式。 I normally define it like this: 我通常这样定义:

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
    }
}

Obviously, when doing your own implementations, you must take into account thread safety making good using of transactions, specially across diferent entity repositories. 显然,在执行自己的实现时,必须考虑到线程安全性,尤其是在不同的实体存储库之间,要善加使用事务。

//  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();
}

Just be sure that you really want to create your own DAL since it can end beeing extremelly complex, specially trying to develop the most generic solution. 只需确保您确实要创建自己的DAL,因为它可能会变得极其复杂,特别是尝试开发最通用的解决方案。

I suggest using an ORM, Entity Framework or NHibernate will do nicely. 我建议使用ORM,Entity Framework或NHibernate会很好。 Then you do not have to worry about a db context or create SQL statements. 然后,您不必担心数据库上下文或创建SQL语句。

First of all, I would like to recommend you the article Design Patterns for Data Persistence by Jeremy Miller . 首先,我想向您推荐Jeremy Miller的文章Data Persistence的设计模式

There are some data access layer patterns: 有一些数据访问层模式:

  1. Active record pattern ( wiki , Detailed info ). 活动记录模式Wiki详细信息 )。
  2. Repository pattern ( Detailed info ). 存储库模式详细信息 )。

I suggest you to use a RepositoryBase for all this common operations. 我建议您对所有这些常用操作使用RepositoryBase。 If you decide to use an ORM for data access it's good to think in a implementation of repositories based on a Generic Type repository. 如果您决定使用ORM进行数据访问,最好考虑基于通用类型存储库的存储库实现。

Here is a good article about it: 这是一篇很好的文章:

http://lostechies.com/jimmybogard/2009/09/03/ddd-repository-implementation-patterns/ http://lostechies.com/jimmybogard/2009/09/03/ddd-repository-implementation-patterns/

Its too old but just came around this question and could not resist to post my thoughts. 它太旧了,但是只是围绕这个问题而已,无法抗拒发表我的想法。

I found Repository with UnitOfWork with some descent ORM is good approach. 我发现带有UnitOfWork的 仓库以及一些下降的ORM是很好的方法。 This minimizes most of the issues. 这样可以最大程度地减少大多数问题。

The UoW mentioned in above link can be injected in Repository. 可以将以上链接中提到的UoW注入存储库中。 That increases the flexibility of usage. 这增加了使用的灵活性。 Also, all DB Communication code is centralized at one place. 同样,所有DB Communication代码都集中在一个地方。 The example is not complete but a startup point. 该示例不是完整的,而是一个启动点。

Repository pattern mentioned in above link is actually a generic base class. 上面链接中提到的存储库模式实际上是一个通用基类。 You can create new class for each of your concrete Repository that derives from it. 您可以为每个派生自其的具体存储库创建新类。

Generic repository is considered an anti pattern; 通用存储库被视为反模式; there are lot many articles on internet that explains it. 互联网上有很多文章对此进行了解释。

Why generic repository is anti-pattern? 为什么通用存储库是反模式的?

  1. A repository is a part of the domain being modeled, and that domain is not generic. 存储库是要建模的域的一部分,并且该域不是通用的。
    • Not every entity can be deleted. 并非每个实体都可以删除。
    • Not every entity can be added 并非每个实体都可以添加
    • Not every entity has a repository. 并非每个实体都有一个存储库。
    • Queries vary wildly; 查询差异很大。 the repository API becomes as unique as the entity itself. 存储库API变得与实体本身一样独特。
    • For GetById() , identifier types may be different. 对于GetById() ,标识符类型可能不同。
    • Updating specific fields (DML) not possible. 无法更新特定字段(DML)。
  2. Generic query mechanism is the responsibility of an ORM. 通用查询机制是ORM的职责。
    • Most of the ORMs expose an implementation that closely resemble with Generic Repository. 大多数ORM都公开了与通用存储库非常相似的实现。
    • Repositories should be implementing the SPECIFIC queries for entities by using the generic query mechanism exposed by ORM. 存储库应该通过使用ORM公开的通用查询机制为实体实现SPECIFIC查询。
  3. Working with composite keys is not possible. 无法使用复合键。
  4. It leaks DAL logic in Services anyway. 无论如何,它将泄漏服务中的DAL逻辑。
    • Predicate criteria if you accept as parameter needs to be provided from Service layer. 如果您接受作为参数的谓词条件,则需要从服务层提供。 If this is ORM specific class, it leaks ORM into Services. 如果这是ORM特定的类,则会将ORM泄漏到服务中。

I suggest you read these ( 1 , 2 , 3 , 4 , 5 ) articles explaining why generic repository is an anit-pattern. 我建议你阅读这些( 12345 )的文章解释了为什么通用存储库是一个ANIT模式。

Solution: 解:

  1. Write an abstract Generic Repository that is wrapped by a concrete repository. 编写由具体存储库包装的抽象通用存储库。 That way you can control the public interface but still have the advantage of code-reuse that comes from generic repository. 这样,您可以控制公共接口,但仍具有来自通用存储库的代码重用的优势。
  2. Use a Generic Repository but do use composition instead of inheritance and do not expose it to the domain as a contract. 使用通用存储库,但不要使用合成而不是继承,也不要将其作为合同公开给域。

In any case, do not expose Generic Repository to calling code. 无论如何,请勿将通用存储库暴露给调用代码。 Also, do not expose IQueryable from concrete repositories. 另外,请勿从具体存储库中公开IQueryable

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM