繁体   English   中英

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

[英]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;
    }
}

PMDContextDbContext的实现。

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.

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