通用存储库模式 - DDD实现

时间:2017-05-21 11:01:03

标签: c# domain-driven-design repository-pattern unit-of-work

我目前正在尝试实施一个简单的博客应用程序用于学习目的。它在DDD架构中 我关心的是如何实现通用存储库模式 如果我正确实施存储库,你能不能就我的想法给你 这是我第一次使用通用回购,看起来我根本就没有使用它 下面显示了我的用户存储库的实现 非常感谢

public interface IRepository<TEntity> where TEntity : class
{
    TEntity GetById(int id);
    IEnumerable<TEntity> GetAll();
    IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);
    void Add(TEntity entity);
    void Update(TEntity entity);
    void Remove(TEntity entity);
}

//Implementation of Generic Repo

using BA.Infrastructure.Data.Context;
using BA.Infrastructure.Data.Interfaces.Helpers;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace BA.Infrastructure.Data.Repositories.Helpers
{
    public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        protected readonly BlogDbContext _context;

        public Repository(BlogDbContext context)
        {
            _context = context;
        }

        public TEntity GetById(int id)
        {
            return _context.Set<TEntity>().Find(id);
        }

        public IEnumerable<TEntity> GetAll()
        {
            return _context.Set<TEntity>().ToList();
        }

        public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
        {
            return _context.Set<TEntity>().Where(predicate);
        }

        public void Add(TEntity entity)
        {
            _context.Set<TEntity>().Add(entity);
        }

        public void Update(TEntity entity)
        {
            _context.Entry(entity).State = EntityState.Modified;
        }

        public void Remove(TEntity entity)
        {
            _context.Set<TEntity>().Remove(entity);
        }
    }
}

//unit of work/implementation

using System;

namespace BA.Infrastructure.Data.Interfaces.Helpers
{
    public interface IUnitOfWork : IDisposable
    {
        IBlogRepository Blogs { get; }
        ICategoryRepository Categories { get; }
        ICommentRepository Comments { get; }
        IUserRepository Users { get; }
        void Complete();
    }
}

using BA.Infrastructure.Data.Context;
using BA.Infrastructure.Data.Interfaces;
using BA.Infrastructure.Data.Interfaces.Helpers;

namespace BA.Infrastructure.Data.Repositories.Helpers
{
    //use unit of work within my service. 
    public class UnitOfWork : IUnitOfWork
    {
        private readonly BlogDbContext _context;

        public IBlogRepository Blogs { get; }
        public ICategoryRepository Categories { get; }
        public ICommentRepository Comments { get; }
        public IUserRepository Users { get; }


        public UnitOfWork(BlogDbContext context)
        {
            _context = context;
            Blogs = new BlogRepository(_context);
            Categories = new CategoryRepository(_context);
            Comments = new CommentRepository(_context);
            Users = new UserRepository(_context);
        }

        public void Complete()
        {
            _context.SaveChanges();
        }

        public void Dispose()
        {
            _context.Dispose();
        }
    }
}

//IUser Repository
using BA.Domains;
using BA.Infrastructure.Data.Interfaces.Helpers;

namespace BA.Infrastructure.Data.Interfaces
{
    public interface IUserRepository : IRepository<User>
    {
        User GetUser(int userId);
        void AddUser(User user);
        void UpdateUser(User user);
    }
}

//User Repository
using BA.Domains;
using BA.Infrastructure.Data.Context;
using BA.Infrastructure.Data.Interfaces;
using BA.Infrastructure.Data.Repositories.Helpers;
using System.Data.Entity;
using System.Linq;

namespace BA.Infrastructure.Data.Repositories
{
    public class UserRepository : Repository<User>, IUserRepository
    {
        public UserRepository(BlogDbContext context)
            : base(context)
        {
        }

        public User GetUser(int userId)
        {
            return _context.Users.FirstOrDefault(x => x.Id == userId);
        }

        public void AddUser(User user)
        {
            _context.Users.Add(user);
        }

        public void UpdateUser(User user)
        {
            _context.Entry(user).State = EntityState.Modified;
        }
    }
}

2 个答案:

答案 0 :(得分:4)

  

如果我正确实施存储库,你们可以告诉我你的想法。

如评论中所述,您可能不希望在该位置实施存储库。

在DDD中,存储库抽象代表了您的域模型如何理解实体存储以及 plumbling 如何理解实体存储之间的界限。

Greg Young, in 2009,写道

  

首先,存储库模式的目的究竟是什么?回顾[DDD,Evans],我们会看到它将一系列对象表示为内存中的一个集合,这样就可以解除域中的持久性问题。换句话说,目标是在持久性中将对象语义放在对象上....

     

简单地将Repository的契约表示域的契约表示存储库支持的聚合根的持久性机制。

主要思想是存储库应该适合用途。合同传达了当前用例所需的数据,而底层的推测提供了它。

吉米·博加德使用了术语Role Specific Repository

格雷格再次:

  

这里的答案是仍然使用通用存储库,但使用组合而不是继承,而不是将其作为契约公开给域。

更一般地说:存储库受dependency inversion principle的约束。域代码定义了它需要提供的接口,您的实现确定了最佳方法,您的composition root将两者连接在一起。

因此,对于您的具体示例,合同应如下所示:

public interface IUserRepository
{
    User GetUser(int userId);
    void AddUser(User user);
    void UpdateUser(User user);
}

请注意,我们不共享公共存储库接口,因为这不是模型所需要的。

使用通用存储库的实现可能看起来像

public class UserRepository : IUserRepository
{
    Repository<User> genericRepo;

    public UserRepository(Repository<User> genericRepo)
    {
        this.genericRepo = genericRepo;
    }

    public User GetUser(int userId)
    {
        return genericRepo.getById(userId);
    }

    // ...
 }

当然,仅提供直接连接到DBContext

的实现同样正确
public class UserRepository : IUserRepository
{
    BlogDbContext context;

    public UserRepository(BlogDbContext context)
    {
        this.context = context;
    }

    public User GetUser(int userId)
    {
        return _context.Users.FirstOrDefault(x => x.Id == userId);
    }

    // ...
 }

在合同分离的情况下,实现往往会变得有用,就是在模型中支持不同类型的查询。

请注意,两种情况下的基本形状都是相同的:我们有一个定义合同的接口,一个满足某些其他合同的实现,以及一个瘦的adapter in中间。

答案 1 :(得分:0)

所以我一直在研究同样的问题,这就是我想出的答案。

  

它取决于。

在这种特定情况下,您只有四个数据模型,这些模型通过简单的CRUD操作保留,不需要特殊的域行为,因此通用存储库将适用。

现在我会分开一些头发:

您是否正确实施了存储库模式?是。

(总有一个但是)但是,你的实施是否正确?我会说不。

  

“存储库是内存中的对象集合,它还提供了在数据存储上执行查询的传递。” (朱莉勒曼)

因此DbSet<>是一个存储库,适用于单一类型的实体。在这种情况下,您只需要封装DbSet<>足以使您的持久性逻辑远离您的域。我认为您当前的代码满足了这一点。

同样地认为DbSet<>是一个存储库,DbContextUnitOfWork(UoW)。由于您的BlogDbContext是一个bounded context(Martin Fowler)包裹DbContext UnitOfWork,这将是多余的。所以我会考虑完全放弃UoW。我还会考虑进一步分离背景的可能性。我知道它只有4个数据模型,但为了论证。Blogs不关心Users(假设你是唯一一个发布博客),以及UsersComments不关心Categories。如果要添加更多模型,则需要开始绘制边界

此外,如果您想要扩展到简单的CRUD操作之外,例如,您希望按Blogs过滤Categories,那么您的域名将推动您向不同的方向发展。如果您使用的是通用存储库,那么您可能正在执行传递机制中的公开IQueryable Find()上执行一些LINQ(假设ASP控制器),这意味着您现在在基本上是您的UI层中拥有业务逻辑。在这种情况下,您可能希望通过使用更接近VoiceOfUnreason解决方案的内容将该逻辑移回您的域,其中您的Controller在您的存储库中调用GetBlogsByCategory(string Category)方法。

简而言之,只要它适用,请继续使用存储库模式,当您的域需求发生变化时,请重构!