Implementing a Repository<T> that returns a domain model mapped from an EF Entity

做~自己de王妃 提交于 2019-12-22 22:19:57

问题


I have the following application structure, based on the onion architecture by Jeffery Palermo (ref link). So my Core is not dependant on anything, my Infrastructure is dependent on my Core

My Core has the Repository Contract and my Infrastructure implements it. The Implementation gets Injected by my IoC Container

Core
-Interfaces
--IRepository<TDomainModel>
-Domain
--Person

Infrastructure
-Data
--Repository<TDomainModel> (Implementation)
-Entities
--Ef.edmx

So this wouldn't be a problem if I wrote out a concrete repository implementation (e.g a PersonRepository) because I know what type to project / map too.

Example Concrete Implementation:

public class PersonRepository
{
 ...
   public IQueryable<PersonDomainClass> GetByName(string name)
   {
      return Dbcontext.Person.Where(x => x.name == name).Select(x => new Person());
   }
  ...
}

What I would like:

 public class Repository<TDomainModel> : IRepository<TDomainModel>
 {
     //Problem 1. We can't set the DbSet to a Domain Model
     private DbSet<TDomainModel> dbEntity; 
     ...
       public IQueryable<TDomainModel> GetWhere((Expression<Func<TDomainModel, bool>> predicate))
       {
           //Problem 2. I Don't think this will work because the predicate is ofType TDomainModel    
           //and not an EF Entity!? 
           var entities = dbEntity.Where(predicate);

        var domainObjects = Mapper.Map <IQueryable<TDomainModel>, IQueryable<TEntityModel>> (entities);

        return domainObjects;
       }
      ...
}

I might be going about this the wrong way so I open other implementations.

UPDATE

Well thank you all for your thoughts and advice. usr made a very good point that I had overlooked- If I abstract over my ORM I will lose all benefits that the ORM provides.

I am using EF Database First development. So my Entities are in my infrastructure along with my repository implementations.

The Domain is separate to this as I am building my application based on the onion architecture.

If I was doing Code First it seems the thing to do is build your Domain first and using EF Code first translate this to a Database.

I can't do code first :(

So, in steps the Entity Framework DbCotnext POCO generator from the EF team @ Microsoft. This generates persistent ignorant POCO classes based on my edmx file

This seems great so far, I have all the benefits of Lazy Loading and Change Tracking and even better my Domain is generated for me and Entity Framework handles the mapping internally. This has simplified my application :)

So this is no a high level view of my architecture

Core
-Interfaces
--IRepository<TEntity>
---IPersonRepository<Person>
---IFooRepository<Foo>
-Domain
--Person (Auto Generated)
--Foo (Auto Generated)

Infrastructure
-Data
--Repository<TEntity> (Implementation)
---PersonRepository<Person>
---FooRepository<Foo>
-Entities
--Ef.edmx 

回答1:


The repository pattern is used to provide an abstraction to the data layer? Right?

With that in mind, let's think about LINQ to SQL (no matter if it's through EF, nhibernate or anything else). There is no LINQ to SQL provider which is 100% fully compatible with LINQ. There are always cases which cannot be used. Hence LINQ to SQL is a leaky abstraction.

That means that if you use a repository interface which exposes IQueryable<TDomainModel> or Expression<Func<TDb, bool>> where you have to be aware of those limitations. It's therefore not a complete abstraction.

Instead I recommend that you just provide a basic generic repository like this:

interface IRepository<TEntity, TKey>
{
    TEntity Get(TKey key);
    void Save(TEntity entity);
    void Delete(TEntity entity);
}

And then create root aggregate specific interfaces:

interface IUserRepository : IRepository<User, int>
{
    User GetByUserName(string userName);
    IEnumerable FindByLastName(string lastName);
}

which means that the implementation would look like:

public class UserRepository : EfRepository<User>, IUserRepository
{
    //implement the interfaces declared in IUserRepository here
}

It's now a 100% working abstraction where it's much easier to tell what functions the repository provides. You have to write a little bit more code now, but you don't have to struggle with a leaky abstraction later on.

You could also switch to queries like I demonstrate here: http://blog.gauffin.org/2012/10/griffin-decoupled-the-queries/




回答2:


take a look at AutoMapper. This may help with the generic mapping.




回答3:


You are going in a right way, but I will suggest to add additional abstractions into your solution:

public abstract class RepositoryBase<T, TDb> : where T : new() where TDb : class, new()
{
    protected IQueryable<T> GetBy(Expression<Func<TDb, bool>> where = null,
                                  PagingSortOptions? pagingSortOptions = null)
    {
        //GetDbSet basic method to get DbSet in generic way 
        IQueryable<TDb> query = GetDbSet();

        if (where != null)
        {
            query = query.Where(where);
        }

        if (pagingSortOptions != null)
        {
            query = query.InjectPagingSortQueryable(pagingSortOptions);
        }

        return query.Select(GetConverter());
    }

    protected virtual Expression<Func<TDb, T>> GetConverter()
    {
        return dbEntity => Mapper.Map<TDb, T>(dbEntity);
    }
}

public class CountryRepository : RepositoryBase<CountryDomainModel, CountryDb>, ICountryRepository
{
    public Country GetByName(string countryName)
    {
        return GetBy(_ => _.Name == countryName).First();
    }
}

public interface ICountryRepository : IRepository<CountryDomainModel>
{
    Country GetByName(string countryName);
}

public interface IRepository<TDomainModel>
{
    //some basic metods like GetById
}

than outside database layer you will use ICountryRepository.



来源:https://stackoverflow.com/questions/13387858/implementing-a-repositoryt-that-returns-a-domain-model-mapped-from-an-ef-entit

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!