[英]Entity Framework Saving Many-to-Many Relationship with Codefirst, Generic Repository, Unit of Work Pattern
[英]How to deal with many-to-many relationships in the general repository, unit of work pattern?
对于我的论文,我决定在MVC中创建一些东西,为了挑战自己,我添加了DAL和BL层。 我在BL中创建了“服务”,使我可以与实体合作。
我真的很想知道我是否正确理解了这种模式,因为我在处理多对多关系时遇到问题,尤其是如何正确使用它们。
这是我当前的实现(简化后得到的总体思路):
PersonService :此类是我使用实体的抽象(我也有几个实体工厂)。 每当我需要将Person添加到数据库时,我都会使用我的服务。 我只是注意到mPersonRepository的名称可能应该不同。
public class PersonService : IService<Person> {
private UnitOfWork mPersonRepository;
public PersonService() => mPersonRepository = new UnitOfWork();
public void Add(Person aPerson) {
mPersonRepository.PersonRepository.Insert(aPerson);
mPersonRepository.Safe();
}
public void Delete(Guid aGuid) {
mPersonRepository.PersonRepository.Delete(aGuid);
mPersonRepository.Safe();
}
public Person Find(Expression<Func<Person, bool>> aFilter = null) {
var lPerson = mPersonRepository.PersonRepository.Get(aFilter).FirstOrDefault();
return lPerson;
}
public void Update(Person aPerson) {
mPersonRepository.PersonRepository.Update(aPerson);
mPersonRepository.Safe();
}
}
public interface IService<TEntity> where TEntity : class {
void Add(TEntity aEntity);
void Update(TEntity aEntity);
void Delete(Guid aGuid);
TEntity Find(Expression<Func<TEntity, bool>> aExpression);
TEntity FindByOid(Guid aGuid);
IEnumerable<TEntity> FindAll(Expression<Func<TEntity, bool>> aExpression);
int Count();
}
UnitOfWork :与Microsoft实现它的方式非常相似。
public class UnitOfWork : IUnitOfWork {
private readonly DbContextOptions<PMDContext> mDbContextOptions = new DbContextOptions<PMDContext>();
public PMDContext mContext;
public UnitOfWork() => mContext = new PMDContext(mDbContextOptions);
public void Safe() => mContext.SaveChanges();
private bool mDisposed = false;
protected virtual void Dispose(bool aDisposed) {
if (!mDisposed)
if (aDisposed) mContext.Dispose();
mDisposed = true;
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
private GenericRepository<Person> mPersonRepository;
private GenericRepository<Project> mProjectRepository;
public GenericRepository<Person> PersonRepository => mPersonRepository ?? new GenericRepository<Person>(mContext);
public GenericRepository<Project> ProjectRepository => mProjectRepository ?? new GenericRepository<Project>(mContext);
GenericRepository :和以前一样,它非常相似。
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class {
internal PMDContext mContext;
internal DbSet<TEntity> mDbSet;
public GenericRepository(PMDContext aContext) {
mContext = aContext;
mDbSet = aContext.Set<TEntity>();
}
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> aFilter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> aOrderBy = null,
string aProperties = "") {
var lQuery = (IQueryable<TEntity>)mDbSet;
if (aFilter != null) lQuery = lQuery.Where(aFilter);
foreach (var lProperty in aProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) {
lQuery = lQuery.Include(lProperty);
}
return aOrderBy != null ? aOrderBy(lQuery).ToList() : lQuery.ToList();
}
public virtual TEntity GetById(object aId) => mDbSet.Find(aId);
public virtual void Insert(TEntity aEntity) => mDbSet.Add(aEntity);
public virtual void Delete(object aId) {
var lEntity = mDbSet.Find(aId);
Delete(lEntity);
}
public virtual void Delete(TEntity aEntity) {
if (mContext.Entry(aEntity).State == EntityState.Detached) mDbSet.Attach(aEntity);
mDbSet.Remove(aEntity);
}
public virtual void Update(TEntity aEntity) {
mDbSet.Attach(aEntity);
mContext.Entry(aEntity).State = EntityState.Modified;
}
}
PMDContext : DbContext的实现。
public class PMDContext : DbContext {
public PMDContext(DbContextOptions<PMDContext> aOptions) : base(aOptions) { }
public DbSet<Person> Persons { get; set; }
public DbSet<Project> Projects { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder aOptions) {
if (!aOptions.IsConfigured) aOptions.UseSqlServer("<snip>");
}
}
实体
public class Person {
public Person(<args>) {}
public Guid Oid { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Project {
public Project(<args>) {}
public Guid Oid { get; set; }
public string Name { get; set; }
}
我将其全部用作以下内容:
var lPerson = Factory.CreatePerson(<args>);
var lPersonService = new PersonService();
lPersonService.Add(lPerson);
<..do some work..>
lPersonService.Update(lPerson)
现在,我无需担心致电Safe或其他问题。 一切正常,但现在我遇到了一个问题:我该如何处理实体中的多对多关系。 例如,我的个人可以有多个项目,而我的项目可以有多个人。
我更新了PMDContext以获得链接表:
protected override void OnModelCreating(ModelBuilder aModelBuilder) {
aModelBuilder.Entity<PersonProject>().HasKey(x => new { x.PersonOid, x.ProjectOid });
}
链接表
public class PersonProject {
public Guid PersonOid { get; set; }
public Guid ProjectOid { get; set; }
}
并使用以下属性更新了我的两个实体。
public ICollection<PersonProject> PersonProjects { get; } = new List<PersonProject>();
现在,我对如何使用链接表感到困惑。 我以为我可以遵循类似的方法:
var lPerson = PersonService.FindByOid(aPersonOid);
var lProject = ProjectService.FindByOid(aProjectOid);
var lPersonProject = new PersonProject() { PersonOid = aPersonOid,
ProjectOid = aProjectOid };
lPerson.PersonProjects.Add(lPersonProject);
lProject.PersonProjects.Add(lPersonProject);
PersonService.Update(lPerson);
ProjectService.Update(lProject);
但这最终对我的数据库中的PersonProject表不做任何事情。 我的猜测是我缺少实际写入该表的代码,因为我没有处理该问题的PersonProject服务。 我很困惑。
我将如何使用当前的方法前进,或者我必须更改什么? 我只是拥有实体框架的初学者,已经很高兴实现了这一目标。
任何输入,尤其是在服务->模式实现上,都应得到赞赏。 我一定做错了什么。
谢谢!
您并没有真正使用服务层模式。 您的“服务”只是一个存储库,然后使用您的工作单元访问另一个存储库。 简而言之,您在这里拥有多层毫无意义的抽象,这绝对会在必须维护任何时间的应用程序中将您杀死 。
一般情况下,你不应该像实体框架的ORM使用的工作中心/存储库模式的单元。 原因很简单:这些ORM 已经实现了这些模式。 对于EF, DbContext
是您的工作单元,每个DbSet
是一个存储库。
如果您要使用诸如Entity Framework之类的东西,我最好的建议是仅使用它 。 在您的应用程序中引用它,将您的上下文注入到控制器等中,并实际使用EF API来完成您需要做的事情。 这不是造成紧密的联系吗? 是。 是的。 但是,如此之多的小姐(甚至我很长时间)的重点是,耦合已经存在。 即使您对所有内容都进行了抽象,您仍在处理永远无法完全抽象的特定领域。 如果你改变你的数据库,这将泡到你在某些时候的应用,即使是你改变,而不是实体的DTO。 而且,当然,您仍然必须更改这些实体。 这些层只会为您的应用程序增加更多的维护和熵,这实际上是“干净代码”体系结构抽象的对立面。
但是,如果您需要以其他方式关闭EF,该怎么办? 您是否需要重写一堆代码? 是的,是的。 但是,这几乎永远不会发生。 在诸如ORM之类的东西上进行选择具有足够的动力,无论您使用什么抽象层,无论您做什么,都不可能将其逆转。 这将只是需要太多的时间和精力,并且永远不会成为业务优先事项。 而且,重要的是,无论如何都必须重写一堆代码。 这仅取决于要完成的层。
现在,所有这些都说明了某些模式的价值,例如CQRS(命令查询责任隔离),这是一种抽象(而不是毫无意义的抽象)。 但是,这仅在大型项目或领域中才有意义,在大型项目或领域中,您需要在读写和/或事件源(在CQRS中自然而然)之间进行清晰的分隔。 对于大多数应用程序来说,这太过分了。
如果您想从主应用程序中提取EF,那么我所建议的就是实际创建微服务。 这些微服务基本上只是很少的API(尽管不是必须的 ),它们仅处理应用程序的单个功能单元。 然后,您的应用程序发出请求或以其他方式访问微服务以获取所需的数据。 微服务将只直接使用EF,而应用程序则完全不依赖EF(圣杯开发人员认为他们想要的)。
使用微服务架构,您实际上可以选中所有您认为这种虚假抽象正在使您受益的框。 是否想将EF换成其他东西? 没问题。 由于每个微服务仅适用于域的有限子集,因此通常没有大量代码。 即使直接使用EF,重写那些部分也是相对琐碎的。 更好的是,每个微服务都是完全独立的,因此您可以将EF切换到一个,而在另一个上继续使用EF。 一切都正常工作,应用程序也可以轻松处理。 这使您能够以可管理的速度处理随时间推移的迁移。
总之,不要过度设计。 这甚至是从事了一段时间业务的开发人员的祸根,但尤其是对于新开发人员而言,他们对代码模式的幻想在脑海中崭露头角。 请记住,这些模式是解决特定问题的推荐方法。 首先,您需要确保确实有问题,然后您需要关注该模式是否确实是解决您的具体情况的最佳方法。 这是一项技能-您会随着时间的推移而学习。 到达那里的最好方法是从小处着手。 以最直接的方式构建最低限度的功能。 然后,重构。 测试,剖析,扔给狼,然后将沾满鲜血的遗骸拖回去。 然后,重构。 最终,您可能最终实现了各种不同的层和模式,但也可能没有。 关键是那些“可能不重要”的时间,因为在这种情况下,您可以获得简单,轻松可维护的代码,这些代码可以正常工作,并且不会浪费大量的开发时间。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.