Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/310.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何处理通用存储库、工作单元模式中的多对多关系?_C#_Design Patterns_Repository Pattern_Asp.net Core 2.0_Unit Of Work - Fatal编程技术网

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

C# 如何处理通用存储库、工作单元模式中的多对多关系?,c#,design-patterns,repository-pattern,asp.net-core-2.0,unit-of-work,C#,Design Patterns,Repository Pattern,Asp.net Core 2.0,Unit Of Work,在我的论文中,我决定在MVC中创建一些东西,为了挑战自己,我添加了DAL和BL层。我在BL中创建了“服务”,允许我与我的实体一起工作 我真的很想知道我是否正确地理解了这个模式,因为我在处理多对多关系时遇到了一些问题,尤其是如何正确地使用它们 这是我当前的实现(简化,以获得总体思路): PersonService:这个类是我使用实体的抽象(我也有几个实体工厂)。每当我需要向数据库中添加一个人时,我都会使用我的服务。我刚刚注意到,mPersonRepository的名称可能应该有所不同 public

在我的论文中,我决定在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();
}
并用以下属性更新了我的两个实体

public ICollection<PersonProject> PersonProjects { get; } = new List<PersonProject>();
但这最终不会对我数据库中的PersonProject表做任何事情。我的猜测是,我缺少实际写入该表的代码,因为我没有处理此问题的PersonProject服务。我很困惑

我将如何使用我目前的方法,或者我必须改变什么?我只是一个实体框架的初学者,已经很高兴我走到了这一步

任何输入都值得赞赏,尤其是在服务->模式实现方面。我一定是做错了什么


谢谢

您没有真正使用服务层模式。您的“服务”只是一个存储库,然后它使用您的工作单元访问另一个存储库。简言之,这里有多个无意义的抽象层,这绝对会让你在一个需要维护任何时间的应用程序中丧命

一般来说,您不应该将工作单元/存储库模式与类似于ORMs的实体框架一起使用。原因很简单:这些orm已经实现了这些模式。在EF的情况下,
DbContext
是您的工作单元,每个
DbSet
都是一个存储库

如果您打算使用实体框架之类的东西,我的最佳建议就是直接使用它。在你的应用程序中引用它,将你的上下文注入你的控制器等等,并实际使用EF API来做你需要做的事情。这不是在制造紧密耦合吗?对是的。然而,很多人(甚至我自己也有很长一段时间)忽略的一点是,耦合已经存在。即使你把一切都抽象了,你仍然在处理一个你永远无法完全抽象的特定领域。如果您更改了数据库,那么在某个时刻,应用程序将出现气泡,即使您更改的是DTO而不是实体。当然,您还必须更改这些实体。这些层只是为您的应用程序添加了更多的维护和熵,这实际上是“干净代码”架构抽象的对立面

但是如果你需要用别的东西来替换EF呢?你不需要重写一堆代码吗?嗯,是的。然而,这几乎从未发生过。选择ORM之类的东西有足够的动力,无论你做什么,无论你使用多少层抽象,你都不可能逆转这一过程。这将需要太多的时间和精力,而且永远不会成为业务重点。而且,重要的是,无论如何都必须重写一堆代码。这只是在哪一层中完成的问题

现在,所有这些都表明,在某些模式中有价值,比如CQRS(命令查询责任分离),这是一种抽象(而不是毫无意义的抽象)。然而,这只有在大型项目或领域中才有意义,在这些项目或领域中,您需要在读写和/或事件搜索之间进行明确的分离(这与CQR很自然)。对于大多数应用程序来说,这是一种过度杀伤力

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

有了微服务体系结构,您实际上可以勾选您认为这个人造抽象将带给您的所有框。想用别的东西换掉EF吗?没问题。由于每个微服务只与域的有限子集一起工作,因此通常没有大量代码。即使直接使用EF,重写这些部分也相对简单。更好的是,每个微服务都是完全独立的,所以您可以在其中一个上切换EF,但在另一个上继续使用EF。一切都在运行,应用程序对此毫不在意。这使您能够以可管理的速度随时间推移处理迁移

长短不一,不要过度设计。即使是已经从事这项业务一段时间的开发人员,这也是他们的祸根,尤其是那些刚刚走出大门,脑子里充满了代码模式幻觉的新开发人员。请记住,这些模式是解决特定问题的推荐方法。首先,你需要确保你确实遇到了问题,然后你需要关注这种模式是否是解决问题的最佳方式——你的具体情况。这是一项技能,你将随着时间的推移而学习。实现这一目标的最好方法是从小处着手。以最直接的方式构建最简单的功能。然后,重构。测试,轮廓,扔给狼群,把血淋淋的尸体拖回来。然后,重构。最终,您可能会实现各种不同的层和模式,但也可能不会。贝卡,重要的是那些“可能不会”的时刻
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;
    }
}
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)
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);