title: Mapping database entities to domain entities via repositories - static property or separate service?
body: <p>I am working on an application where my database schema does not match up well to my domain model, and modifications to the database schema are not under my control.  Because of this, I end up doing a lot of mapping in my repository calls, and I have many concrete repos to handle the mapping to and from the database (using entity framework database-first).  What I am looking for is an elegant way to make calls to my repositories based on the domain entity object type.  Thus far, the domain model itself is still very anemic, as we are still in the process of defining business rules.</p>
  
  <p>I have seen an example elsewhere (can't recall the link) where the repository calls were passed through the domain entities via a static property, but I do not know if this will present threading issues or whether it violates any domain model principles, especially if we decide to implement DI/IoC down the road.</p>
  
  <p>Here is an example of what I have so far. For the sake of brevity, I have simplified the mapping calls to the database, as the mapping in the actual application is more complex.</p>
  
  <p><strong>Repository example:</strong></p>
  
  <pre><code>public interface IRepository&lt;T&gt;
  {
      T GetById(int id);
      void Save(T entity);
  }
  
  public abstract class RepositoryFactory&lt;T&gt; : IRepository&lt;T&gt;
  {
      protected MyDbContext db;
      protected int userId;
  
      protected RepositoryFactory()
      {
          this.db = new MyDbContext();
          this.userId = WebSecurity.GetCurrentUser().UserId;
      }
  
      public abstract T GetById(int id);
      public abstract void Save(T entity);
  }
  
  public class CustomerRepository : RepositoryFactory&lt;Customer&gt;
  {
      public override void Save(Customer customer)
      {
          var entity = db.customers.FirstOrDefault(p =&gt; p.customerid == customer.Id &amp;&amp; p.users.userid == userId);
  
          if (entity == null) return;  // TODO: Add error trapping
  
          // Mapping (lots of reshaping happening here)
          entity.customername = customer.Name;
          entity.customeraddress = customer.Address;
          // ...
  
          // Save changes to DB
          db.Entry(entity).State = EntityState.Modified;
          db.SaveChanges();
      }
  
      public override Customer GetById(int id)
      {
          var entity = db.customers.FirstOrDefault(p =&gt; p.customerid == id &amp;&amp; p.users.userid == userId);
          if (entity == null) return null;  // TODO: Add error trapping
          return new Customer
          {
              Name = entity.customername,
              Address = entity.customeraddress,
              // ...
          };
      }
  }
  </code></pre>
  
  <p><strong>Domain Entity example:</strong></p>
  
  <pre><code>public class Entity { public int Id { get; set; } }
  
  public class Customer : Entity
  {
      public string Name { get; set; }
      public string Address { get; set; }
  
      // Does this violate any domain principles?
      public static IRepository&lt;Customer&gt; Repository
      {
          get { return new CustomerRepository(); }
      }
  }
  </code></pre>
  
  <p>With this code, from my controller I can do something like:</p>
  
  <pre><code>Customer customer = Customer.Repository.GetById(id);
  </code></pre>
  
  <p>Instead of: </p>
  
  <pre><code>IRepository&lt;Customer&gt; repo = new CustomerRepository();
  Customer customer = repo.GetById(id);
  </code></pre>
  
  <p>This seems like a very elegant solution to my problem, and it also keeps me from needing to include the Repository namespace in my controllers (MVC).  If this smells funny and there is a better way to handle this, I'd love to learn.  The only other thing I can think of is creating a separate crud service to handle my repository calls, but I imagine for that I would need a dictionary or hash table to map my concrete repos to my domain model objects, and that seems like it would be a maintenance nightmare.</p>