实体框架延迟加载通用存储库

时间:2013-03-27 13:54:47

标签: entity-framework asp.net-mvc-4 lazy-loading entity-framework-5 navigation-properties

我当前的项目使用通用存储库接口,因此:

public interface IDataSource : IDisposable
{
    void Add<T>(T newItem) where T : EntityBase;

    T Get<T>(Guid id) where T : EntityBase;
    T Get<T>(Expression<Func<T, bool>> predicate) where T : EntityBase;
    IQueryable<T> GetAll<T>(Expression<Func<T, bool>> predicate = null) where T : EntityBase;
    int Count<T>(Expression<Func<T, bool>> predicate = null) where T : EntityBase;
    bool Any<T>(Expression<Func<T, bool>> predicate = null) where T : EntityBase;

    void Update<T>(T updated) where T : EntityBase;

    void Delete<T>(Guid id) where T : EntityBase;
    void Delete<T>(T item) where T : EntityBase;

    void Commit();
}

例如,Get方法如下所示:

public T Get<T>(Expression<Func<T, bool>> predicate) where T : EntityBase
{
    return db.Set<T>().Single(predicate);
}

其中db是我的数据上下文的实例,它扩展了实体框架的DbContext。整个事情实现了IDisposable,这样我就可以在作为工作单元模式的范围块中使用它,在提交更改之前等待结束,或者在此之前出现问题时处理整个事情。

逻辑层使用此接口来处理更复杂的查询,以使业务逻辑与数据访问完全分离。因此,对该图层的查询可能如下所示:

public List<Product> ItemsBoughtByCustomer(Guid customerID)
{
    using (var db = DataAccess.GetContext())
    {
        List<Purchase> purchaseHistory = db.GetAll<Purchase>(p => p.CustomerID == customerID);
        List<int> IDs = purchaseHistory.Select(p => p.ProductID);
        return db.GetAll<Product>(p => IDs.Contains(p.ID));
    }
}

(是的,我意识到这可以压缩;它在应用程序中,但作为一个例子,这更清楚。)

我的问题是,有时我会返回一组对象,然后我可能想要了解它引用的一些东西。例如,当我显示产品时,人们可能希望这样做:

@foreach (Comment comment in Product.Comments)
{
    <div class="comment">
        <span>@Html.UserDisplay(comment.Author)</span>
        <span>@comment.Body</span>
    </div>
}

(忽略HTML的质量;再次,这是一个简单的例子)

问题是,当从我的查询返回实体时,Entity Framework的延迟加载会将这些属性保留为null,从而抛出错误。现在,我知道Include()方法,但如果我的存储库是通用的,那么很难应用它们。我可以完全关闭它,但是当我不需要它时,EF将开始检索大量链接的东西 - 模型的结构和事物对审计日志的链接意味着EF的很多链接跟随。

有没有一种方法可以以更智能的方式进行延迟加载?是否有.Single().Where()这样的方法,我可以在DbSet上调用,它也会带来子对象,这样我就可以特别要求包含子对象查询?

1 个答案:

答案 0 :(得分:1)

为include路径添加一个可选参数,然后在DbSet上调用Include(str)。 Get方法示例:

public T Get<T>(Expression<Func<T, bool>> predicate, string includePath = null) where T : EntityBase
{
    var query = db.Set<T>();

    if( !string.IsNullorWhitespace( includePath ) )
    {
        query = query.Include( includePath );
    }

    return query.Single(predicate);
}