Repository pattern and Navigation properties
I just want know best practice about writing repository methods. Problem is deciding to write repository in which context has no lazy loading. How do you naming your method if it is GetById but it is not clear that which the navigations included in entity.
So I am thinking to write method names like GetUserByIdIncludedPosts Or it is better to use lazy loading activated context ?
If I write included properties in method names so will be really annoying long method names for few navigation property.
I am using the following in my repository base class to allow retrieval of entities along with a user-specified list of dependencies/relations:
protected DbSet<T> Objects { get; private set; }
protected YourDatabaseContext Context { get; private set; }
public virtual T GetByID( int id, params string[] children )
{
if( children == null || children.Length == 0 )
{
return Objects.SingleOrDefault( e => e.ID == id );
}
DbQuery<T> query = children.Aggregate<string, DbQuery<T>>( Objects, ( current, child ) => current.Include( child ) );
return query.SingleOrDefault( e => e.ID == id );
}
The code uses EF4/CTP5 and therefore uses Db* classes, but is trivial to convert back to the normal EF4 classes (eg ObjectSet instead of DbSet).
This would be used like so:
var product = productsRepository.GetByID( 42, "Category", "Orders.OrderLines" );
which would fetch you a product with Category and Orders populated as well as all orders having their OrderLines eagerly loaded.
Using repository pattern doesn't mean that you will not be able to use lazy loading. You can still return entity which will be able to lazy load its related entities. The only requirement is that DbContext
used for loading entity must be "alive".
But let's have a look on definition of repository by Martin Fowler:
A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes. Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer. Repository also supports the objective of achieving a clean separation and one-way dependency between the domain and data mapping layers.
I think the interesting part is: Client objects construct query specifications declaratively and submit them to Repository for satisfaction. Also repository is usually used to provide aggregate roots. So you will either always provide whole root (not always possible) or you will satisfy the mentioned statement and you will define eager loading outside of the repository by Include
extension method on IQueryable
. Because of that you will never need specialized methods like GetUserByIdIncludeSomething
.
If you want to user repository start with this method for all queries:
public interface IRepository<T>
{
IQueryable<T> GetQuery();
}
Btw. I don't think that a user is aggregate root for posts. In such case the most of applications will have only single aggregate root - a user.
Edit:
Small clarification: IQueryable
by default doesn't provide Include
method. It is provided as extension method in CTP5 assembly but if you use it you will make your upper layer dependent on EntityFramework.dll. It is something you usually don't want (the reason why you are using repository). So the way to go is define your own extension method wrapping provided extension in assembly with your repository.
上一篇: 调用DrawImage时,帮助解决“内存不足”异常
下一篇: 存储库模式和导航属性