title: How to deal with many-to-many relationships in the general repository, unit of work pattern?
body: <p>For my thesis I decided to create something in MVC and to challenge myself I added a DAL and BL layer. I created "services" in BL that allow me to work with my Entities.</p>
  
  <p>I am really wondering if I understood the pattern correctly, because I am having issues dealing with many-to-many relationships - and especially how to use them properly.</p>
  
  <p>This is my current implementation (simplified, to get the general idea):</p>
  
  <p><strong>PersonService</strong>: this class is my abstraction for using my entities (I have several entity factories as well). Whenever I need to add a Person to my DB, I use my service. I just noticed that mPersonRepository should probably be named differently. </p>
  
  <pre><code>public class PersonService : IService&lt;Person&gt; {
      private UnitOfWork mPersonRepository;
      public PersonService() =&gt; 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&lt;Func&lt;Person, bool&gt;&gt; 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&lt;TEntity&gt; where TEntity : class {
      void Add(TEntity aEntity);
      void Update(TEntity aEntity);
      void Delete(Guid aGuid);
      TEntity Find(Expression&lt;Func&lt;TEntity, bool&gt;&gt; aExpression);
      TEntity FindByOid(Guid aGuid);
      IEnumerable&lt;TEntity&gt; FindAll(Expression&lt;Func&lt;TEntity, bool&gt;&gt; aExpression);
      int Count();
  }
  </code></pre>
  
  <p><strong>UnitOfWork</strong>: pretty much similar as the way Microsoft implemented it.</p>
  
  <pre><code>public class UnitOfWork : IUnitOfWork {
  
      private readonly DbContextOptions&lt;PMDContext&gt; mDbContextOptions = new DbContextOptions&lt;PMDContext&gt;();
      public PMDContext mContext;
      public UnitOfWork() =&gt; mContext = new PMDContext(mDbContextOptions);
      public void Safe() =&gt; 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&lt;Person&gt; mPersonRepository;
      private GenericRepository&lt;Project&gt; mProjectRepository;
  
      public GenericRepository&lt;Person&gt; PersonRepository =&gt; mPersonRepository ?? new GenericRepository&lt;Person&gt;(mContext);
      public GenericRepository&lt;Project&gt; ProjectRepository =&gt; mProjectRepository ?? new GenericRepository&lt;Project&gt;(mContext);
  </code></pre>
  
  <p><strong>GenericRepository</strong>: just as before, it is very similar.</p>
  
  <pre><code>public class GenericRepository&lt;TEntity&gt; : IGenericRepository&lt;TEntity&gt; where TEntity : class {
      internal PMDContext mContext;
      internal DbSet&lt;TEntity&gt; mDbSet;
  
      public GenericRepository(PMDContext aContext) {
          mContext = aContext;
          mDbSet = aContext.Set&lt;TEntity&gt;();
      }
  
      public virtual IEnumerable&lt;TEntity&gt; Get(
          Expression&lt;Func&lt;TEntity, bool&gt;&gt; aFilter = null,
          Func&lt;IQueryable&lt;TEntity&gt;, IOrderedQueryable&lt;TEntity&gt;&gt; aOrderBy = null,
          string aProperties = "") {
          var lQuery = (IQueryable&lt;TEntity&gt;)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) =&gt; mDbSet.Find(aId);
      public virtual void Insert(TEntity aEntity) =&gt; 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;
      }
  }
  </code></pre>
  
  <p><strong>PMDContext</strong>: an implementation of DbContext.</p>
  
  <pre><code>public class PMDContext : DbContext {
      public PMDContext(DbContextOptions&lt;PMDContext&gt; aOptions) : base(aOptions) { }
      public DbSet&lt;Person&gt; Persons { get; set; }
      public DbSet&lt;Project&gt; Projects { get; set; }
  
  protected override void OnConfiguring(DbContextOptionsBuilder aOptions) {
          if (!aOptions.IsConfigured) aOptions.UseSqlServer("&lt;snip&gt;");
      }
  }
  </code></pre>
  
  <p><strong>Entities</strong></p>
  
  <pre><code>public class Person {
      public Person(&lt;args&gt;) {}
  
      public Guid Oid { get; set; }
      public string FirstName { get; set; }
      public string LastName { get; set; }
  }
  
  public class Project {
      public Project(&lt;args&gt;) {}
  
      public Guid Oid { get; set; }
      public string Name { get; set; }
  }
  </code></pre>
  
  <p>I use it all like the following:</p>
  
  <pre><code>var lPerson = Factory.CreatePerson(&lt;args&gt;);
  var lPersonService = new PersonService();
  lPersonService.Add(lPerson);
  &lt;..do some work..&gt;
  lPersonService.Update(lPerson)
  </code></pre>
  
  <p>Now I do not need to worry about calling Safe, or whatever. It works just fine, but now I ran into an issue: how do I deal with many-to-many relations in my Entities. For example my Person can have multiple Projects and my Project can have multiple Persons. </p>
  
  <p>I updated my PMDContext to get a link table:</p>
  
  <pre><code>protected override void OnModelCreating(ModelBuilder aModelBuilder) {
          aModelBuilder.Entity&lt;PersonProject&gt;().HasKey(x =&gt; new { x.PersonOid, x.ProjectOid });
      }
  </code></pre>
  
  <p><strong>Link table</strong></p>
  
  <pre><code>public class PersonProject {
      public Guid PersonOid { get; set; }
      public Guid ProjectOid { get; set; }
  }
  </code></pre>
  
  <p>And updated both my entities with the following property.</p>
  
  <pre><code>public ICollection&lt;PersonProject&gt; PersonProjects { get; } = new List&lt;PersonProject&gt;();
  </code></pre>
  
  <p>Now I am confused on how to use my linked table. I thought I could follow a similar approach like this:</p>
  
  <pre><code>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); 
  </code></pre>
  
  <p>But this ends up not doing anything to the PersonProject table in my DB. My guess is that I lack the code to actually write to that table, since I do not have a PersonProject service that handles this. I am confused. </p>
  
  <p>How would I advance using my current approach, or what do I have to change? I am only a beginner w/ entity frameworks and already happy I got this far. </p>
  
  <p>Any input is appreciated especially on the services -> pattern implementation. I must be doing something wrong.</p>
  
  <p>Thanks!</p>