存储库模式和导航属性
我只想知道编写存储库方法的最佳实践。 问题在于决定编写其中上下文没有延迟加载的存储库。 你如何命名你的方法,如果它是GetById,但不清楚哪些导航包含在实体中。
所以我正在考虑编写像GetUserByIdIncludedPosts这样的方法名或者使用延迟加载激活的上下文更好?
如果我在方法名称中写入包含的属性,那么对于很少导航属性来说,真的很烦人。
我在我的存储库基类中使用以下内容以允许检索实体以及用户指定的依赖关系/关系列表:
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 );
}
该代码使用EF4 / CTP5,因此使用db *类,但实在是微不足道转换回正常EF4类(如对象集,而不是DbSet)。
这将使用像这样:
var product = productsRepository.GetByID( 42, "Category", "Orders.OrderLines" );
这会为您提供填充了“类别”和“订单”的产品以及所有热切加载OrderLines的订单。
使用存储库模式并不意味着您将无法使用延迟加载。 您仍然可以返回将能够延迟加载其相关实体的实体。 唯一的要求是用于加载实体的DbContext
必须是“活着的”。
但让我们看看Martin Fowler的存储库定义:
存储库在域和数据映射层之间进行中介,就像内存域对象集合一样。 客户端对象以声明方式构造查询规范并将其提交给Repository以满足需求。 对象可以添加到Repository中,也可以从对象中移除,因为它们可以从一个简单的对象集合中获取,并且由Repository封装的映射代码将在幕后执行适当的操作。 从概念上讲,存储库封装了保存在数据存储中的一组对象以及对它们执行的操作,提供了持久层的更加面向对象的视图。 Repository还支持在域和数据映射层之间实现干净的分离和单向依赖的目标。
我认为有趣的部分是:客户端对象以声明的方式构造查询规范并将其提交给Repository以满足需求。 库也通常用于提供聚合根。 因此,您要么始终提供完整的根(不总是可能的),要么您将满足所提到的语句,并且您将通过在IQueryable
Include
扩展方法来定义存储库之外的预加载。 因此,你永远不需要像GetUserByIdIncludeSomething
这样的专门方法。
如果您希望用户存储库以所有查询的此方法启动:
public interface IRepository<T>
{
IQueryable<T> GetQuery();
}
顺便说一句。 我不认为用户是帖子的聚合根。 在这种情况下,大多数应用程序将只有一个聚合根 - 一个用户。
编辑:
小澄清:默认情况下IQueryable
不提供Include
方法。 它在CTP5程序集中作为扩展方法提供,但如果使用它,则会使上层依赖于EntityFramework.dll。 这是你通常不想要的东西(你使用存储库的原因)。 所以,要走的路是定义你自己的扩展方法,用你的仓库包装提供的程序集扩展。