EF +通用存储库+加载相关实体:仅显式加载工作

时间:2011-07-06 18:36:18

标签: c# entity-framework-4.1 ef-code-first dbcontext

我有两个简单的POCO课程;我试图通过MyY的实例使Y属性保持水分。我已经尝试了很多方法来做到这一点,并认为我可能会遗漏一些明显或简单的东西。

public class X
{
     public int Id { get; set;}
     public virtual Y MyY { get; set; }
}

public class Y
{
     public int Id { get; set; }
     // ...
}

我在DbContext的构造函数的子类中通过此调用关闭了延迟加载:

Configuration.LazyLoadingEnabled = false;

检索X时,我尝试了

context.Set<X>.Include("MyY").FirstOrDefault(x => ....);

哪个不起作用。我试过了

var result = context.Set<X>.FirstOrDefault(x => ....);
context.Entry(result).Reference("MyY").Load();

有效,但需要两次往返数据库。我试过了

context.Set<X>.Select(x => new { X = x, Y = x.MyY }).FirstOrDefault(x => ...);

这也有效,但“弱化”我的模型(通常投射到新类型并不是那么糟糕,但这些EF POCO的“形状”适用于我稍后将通过WCF发送的DTO)。< / p>

我最后尝试将virtual属性MyY中的public class EFRepository : IRepository { public T Get<T>(Specification<T> specification) where T : class, IEntity { var result = ApplyEagerLoading(context.Set<T>()).FirstOrDefault(specification.IsMatch); ApplyPostQueryLoading(new List<T> { result }); return result; } // doesn't really seem to work yet... private DbSet<T> ApplyEagerLoading<T>(DbSet<T> set) where T : class, IEntity { var ls = loadSpecs.GetOrAdd(typeof(T), () => new List<LoadSpec>()); foreach (var spec in ls.Where(s => !s.ExplicitLoad)) set.Include(spec.PropertyName); return set; } // works, but wrong on so many levels... private void ApplyPostQueryLoading<T>(IEnumerable<T> entities) where T : class, IEntity { var ls = loadSpecs.GetOrAdd(typeof(T), () => new List<LoadSpec>()); foreach (var e in entities) foreach (var spec in ls.Where(s => s.ExplicitLoad)) if (spec.IsCollection) context.Entry(e).Collection(spec.PropertyName).Load(); else context.Entry(e).Reference(spec.PropertyName).Load(); } private readonly IDictionary<Type, IList<LoadSpec>> loadSpecs = new Dictionary<Type, IList<LoadSpec>>(); private class LoadSpec { internal string PropertyName; internal bool ExplicitLoad; internal bool IsCollection; } } // add a rule to load MyY explicitly repository.AddLoadRule<X>(x => x.MyY, explicit:true, isCollection:false) ... var x = repository.Get<X>(new Specification<X>(x => x.Id == 5)); // add a rule to load MyY with X repository.AddLoadRule<X>(x => x.MyY, explicit:false) ... // x.MyY will be null! Doesn't work! var x = repository.Get<X>(new Specification<X>(x => x.Id == 5)); 移至另一个问题,但这根本没有效果。

最后,我想使用通用存储库模式。我最终得到的是以下设计,部分显示,支持显式加载(非首选)和eager-load ,修改后可正常工作。如何修改它以获得单个数据库往返急切加载?

.Include

示例用途:

.FirstOrDefault

基于answer的更新:

事实证明我的临时代码示例(上面的那些单行)。我实际上已将.Set<X>的结果缓存在局部变量中,但将.Include应用于ApplyEagerLoading而不是 private IQueryable<T> ApplyEagerLoading<T>(IEnumerable<T> set) where T : class, IEntity { var ls = loadSpecs.GetOrAdd(typeof(T), () => new List<LoadSpec>()); var query = set.AsQueryable(); return ls.Where(s => !s.ExplicitLoad).Aggregate(query, (current, spec) => current.Include(spec.PropertyName)); } 的结果。以下是对{{1}}的修复,它反映了其他人在相关问题中提出的建议:

{{1}}

1 个答案:

答案 0 :(得分:1)

这应该有效:

X entity = context.Set<X>().Include(x => x.MyY).FirstOrDefault();

如果不是问题必须在其他地方。

如果您需要一些急切的加载策略,请检查this answer