问题
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