如何处理通用存储库中的多对多关系,工作单元模式?

时间:2018-02-15 20:10:11

标签: c# design-patterns repository-pattern asp.net-core-2.0 unit-of-work

对于我的论文,我决定在MVC中创建一些东西并挑战自己,我添加了一个DAL和BL层。我创建了"服务"在BL,允许我与我的实体合作。

我真的很想知道我是否正确理解了这种模式,因为我遇到了处理多对多关系的问题 - 特别是如何正确使用它们。

这是我目前的实现(简化,以获得一般的想法):

PersonService :这个类是我使用我的实体的抽象(我也有几个实体工厂)。每当我需要向我的数据库添加一个人时,我就会使用我的服务。我只是注意到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)

现在我不需要担心叫安全,或者其他什么。它工作正常,但现在我遇到了一个问题:如何处理我的实体中的多对多关系。例如,我的人可以有多个项目,我的项目可以有多个人。

我更新了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); 

但是这最终没有对我的DB中的PersonProject表做任何事情。我的猜测是我缺少实际写入该表的代码,因为我没有处理此问题的PersonProject服务。我很迷惑。

我如何推进使用我目前的方法,或者我需要改变什么?我只是一个初学者w /实体框架,我已经很开心了。

任何输入都会受到赞赏,特别是在服务上 - &gt;模式实施。我一定是做错了。

谢谢!

1 个答案:

答案 0 :(得分:0)

您并未真正使用服务层模式。您的服务&#34;只是一个存储库,然后使用您的工作单元来访问另一个存储库。简而言之,你在这里有多层无意义的抽象,这绝对会在你需要维护的应用程序中 kill 你。

通常,您应该将工作单元/存储库模式与Entity Framework等ORM一起使用。原因很简单:这些ORM 已经实现了这些模式。对于EF,DbContext是您的工作单位,每个DbSet都是一个存储库。

如果你要使用像Entity Framework这样的东西,我最好的建议是只使用它。在您的应用程序中引用它,将您的上下文注入您的控制器等,并实际使用EF API来完成您需要执行的操作。这不是一种紧密耦合吗?是。是的。然而,很多人错过了这一点(即使是我自己很长一段时间),因为耦合已经已经了。即使你抽象一切,你仍然在处理一个你永远无法完全抽象的特定领域。如果您更改了数据库,那么 会在某个时刻冒泡到您的应用程序,即使它是您正在更改而不是实体的DTO。当然,您仍然需要更改这些实体。这些层只会为您的应用程序添加更多维护和熵,这实际上是&#34;清洁代码的对立面。架构抽象应该是关于。

但是如果你需要用别的东西换掉EF呢?你不必重写一堆代码吗?嗯,是的然而,这几乎从未发生过。对ORM这样的东西做出选择有足够的动力,无论你做什么,你都不可能扭转这个过程,无论你使用多少层抽象。它只需要花费太多时间和精力,永远不会成为业务优先事项。而且,重要的是,无论如何都必须重写一堆代码。这只是它将要完成的层的问题。

现在,所有这一切,在某些模式中都有价值,例如CQRS(命令查询责任隔离),它是一种抽象(而不是无意义的抽象)。但是,这只适用于大型项目或领域,在这些项目或领域中,您需要在读取和写入和/或事件源之间进行明确的分离(这与CQRS自然相关)。对于大多数应用程序来说,这太过分了。

如果您想从主应用程序中抽象EF,我建议除此之外的其他内容是实际创建微服务。这些微服务基本上只是一些API(尽管它们不具备)只能处理应用程序的单个功能单元。然后,您的应用程序发出请求或以其他方式访问微服务以获取所需的数据。微服务只是直接使用EF,而应用程序根本不依赖于EF(圣杯开发人员认为他们想要)。

使用微服务架构,您可以实际检查您认为这种虚拟抽象带给您的所有框。想用别的东西换掉EF吗?没问题。由于每个微服务仅适用于域的有限子集,因此通常不会有大量代码。即使直接使用EF,重写这些部分也是相对微不足道的。更好的是,每个微服务都是完全独立的,因此您可以将EF切换为一个,但继续在另一个上使用EF。一切都在继续,应用程序也不会那么在乎。这使您能够以可管理的速度处理迁移。

多与短,不要过度工程。即使是那些已经在这个行业工作了一段时间的开发人员,尤其是新开发人员,这也是他们头脑中充满了代码模式愿景的新鲜开发人员的祸根。请记住,这些模式是推荐解决特定问题的方法。首先,您需要确保实际遇到问题,然后您需要关注该模式是否实际解决该问题的最佳方式您的具体情况。这是一项技能 - 您将随着时间的推移学习。到达那里的最好方法是从小做起。尽可能以最直接的方式构建最低限度的功能。然后,重构。测试,剖析,将它扔到狼群中并拖回血浸的残骸。然后,重构。最终,您最终可能会实现各种不同的图层和模式,但您也可能不会。那些&#34;可能不会&#34;重要的是,因为在这些情况下,您可以使用简单,毫不费力地维护的代码,这些代码可以正常工作并且不会浪费大量的开发时间。