我目前正在尝试实施一个简单的博客应用程序用于学习目的。它在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;
}
}
}
答案 0 :(得分:4)
如果我正确实施存储库,你们可以告诉我你的想法。
如评论中所述,您可能不希望在该位置实施存储库。
在DDD中,存储库抽象代表了您的域模型如何理解实体存储以及 plumbling 如何理解实体存储之间的界限。
首先,存储库模式的目的究竟是什么?回顾[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<>
是一个存储库,DbContext
是UnitOfWork
(UoW)。由于您的BlogDbContext
是一个bounded context(Martin Fowler)包裹DbContext
UnitOfWork
,这将是多余的。所以我会考虑完全放弃UoW。我还会考虑进一步分离背景的可能性。我知道它只有4个数据模型,但为了论证。Blogs
不关心Users
(假设你是唯一一个发布博客),以及Users
并Comments
不关心Categories
。如果要添加更多模型,则需要开始绘制边界
此外,如果您想要扩展到简单的CRUD操作之外,例如,您希望按Blogs
过滤Categories
,那么您的域名将推动您向不同的方向发展。如果您使用的是通用存储库,那么您可能正在执行传递机制中的公开IQueryable Find()
上执行一些LINQ(假设ASP控制器),这意味着您现在在基本上是您的UI层中拥有业务逻辑。在这种情况下,您可能希望通过使用更接近VoiceOfUnreason解决方案的内容将该逻辑移回您的域,其中您的Controller在您的存储库中调用GetBlogsByCategory(string Category)
方法。
简而言之,只要它适用,请继续使用存储库模式,当您的域需求发生变化时,请重构!