有没有办法装饰你的POCO类来自动加载子实体而不必每次加载时都使用Include()
?
说我有一辆轿车,车轮,门,发动机,保险杠,Windows,排气等的复杂类型属性。在我的应用程序中,我需要从我的DbContext 20个不同的地方加载我的汽车与不同的查询,我不想指定我想在每次加载汽车时都包含所有属性。
我想说
List<Car> cars = db.Car
.Where(x => c.Make == "Ford").ToList();
//NOT .Include(x => x.Wheels).Include(x => x.Doors).Include(x => x.Engine).Include(x => x.Bumper).Include(x => x.Windows)
foreach(Car car in cars)
{
//I don't want a null reference here.
String myString = car.**Bumper**.Title;
}
我可以以某种方式装饰我的POCO课程或我的OnModelCreating()
或在EF中设置一个配置,告诉它在我装车时只装载我车的所有部件吗?我想热切地这样做,所以我的理解是让我的导航属性变得虚拟。我知道NHibernate支持类似的功能。
只是想知道我是否遗漏了什么。提前谢谢!
干杯,
森
我喜欢下面的解决方案,但是我想知道我是否可以将调用嵌套到扩展方法。例如,假设我有类似的情况,它有很多部分我没有希望包括无处不在。我可以这样做吗? (我还没有找到一种工作方式)。这样,如果以后我发现Engine需要FuelInjectors,我只能在BuildEngine中添加它,而不必在BuildCar中添加它。 此外,如果我可以嵌套呼叫,如何将呼叫嵌套到集合中?想从我的BuildCar()中为每个轮子调用BuildWheel()?
public static IQueryable<Car> BuildCar(this IQueryable<Car> query) {
return query.Include(x => x.Wheels).BuildWheel()
.Include(x => x.Doors)
.Include(x => x.Engine).BuildEngine()
.Include(x => x.Bumper)
.Include(x => x.Windows);
}
public static IQueryable<Engine> BuildEngine(this IQueryable<Engine> query) {
return query.Include(x => x.Pistons)
.Include(x => x.Cylendars);
}
//Or to handle a collection e.g.
public static IQueryable<Wheel> BuildWheel(this IQueryable<Wheel> query) {
return query.Include(x => x.Rim)
.Include(x => x.Tire);
}
这是另一个非常类似的线程,以防它在这种情况下对其他人有帮助,但它仍然无法处理能够对扩展方法进行nexted调用。
Entity framework linq query Include() multiple children entities
答案 0 :(得分:61)
不,你cannot do that in mapping。典型的解决方法是简单的扩展方法:
public static IQueryable<Car> BuildCar(this IQueryable<Car> query) {
return query.Include(x => x.Wheels)
.Include(x => x.Doors)
.Include(x => x.Engine)
.Include(x => x.Bumper)
.Include(x => x.Windows);
}
现在,每当您想要使用所有关系查询Car
时,您都会这样做:
var query = from car in db.Cars.BuildCar()
where car.Make == "Ford"
select car;
编辑:
你无法以这种方式嵌套。包含适用于您正在使用的核心实体 - 该实体定义查询的形状,因此在您致电Include(x => Wheels)
后,您仍在使用IQueryable<Car>
并且无法调用IQueryable<Engine>
的扩展方法。您必须再次从Car
开始:
public static IQueryable<Car> BuildCarWheels(this IQuerable<Car> query) {
// This also answers how to eager load nested collections
// Btw. only Select is supported - you cannot use Where, OrderBy or anything else
return query.Include(x => x.Wheels.Select(y => y.Rim))
.Include(x => x.Wheels.Select(y => y.Tire));
}
您将以这种方式使用该方法:
public static IQueryable<Car> BuildCar(this IQueryable<Car> query) {
return query.BuildCarWheels()
.Include(x => x.Doors)
.Include(x => x.Engine)
.Include(x => x.Bumper)
.Include(x => x.Windows);
}
该用法不会调用Include(x => x.Wheels)
,因为当您请求加载其嵌套实体时,它应自动添加。
注意由这种复杂的急切加载结构产生的复杂查询。它可能会导致性能非常差,并且会导致数据库a lot of duplicate data transferred。
答案 1 :(得分:3)
遇到同样的问题,看到另一个提到Include
属性的链接。我的解决方案假设您创建了一个名为IncludeAttribute
的属性。使用以下扩展方法和utility method:
public static IQueryable<T> LoadRelated<T>(this IQueryable<T> originalQuery)
{
Func<IQueryable<T>, IQueryable<T>> includeFunc = f => f;
foreach (var prop in typeof(T).GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(IncludeAttribute))))
{
Func<IQueryable<T>, IQueryable<T>> chainedIncludeFunc = f => f.Include(prop.Name);
includeFunc = Compose(includeFunc, chainedIncludeFunc);
}
return includeFunc(originalQuery);
}
private static Func<T, T> Compose<T>(Func<T, T> innerFunc, Func<T, T> outerFunc)
{
return arg => outerFunc(innerFunc(arg));
}
答案 2 :(得分:0)
如果您确实需要包括所有导航属性(可能会导致性能问题),则可以使用这段代码。
如果您还急于加载集合(甚至是最坏的主意),则可以删除代码中的continue语句。
上下文就是您的上下文(如果您在上下文中插入此代码,则为
public IQueryable<T> IncludeAllNavigationProperties<T>(IQueryable<T> queryable)
{
if (queryable == null)
throw new ArgumentNullException("queryable");
ObjectContext objectContext = ((IObjectContextAdapter)Context).ObjectContext;
var metadataWorkspace = ((EntityConnection)objectContext.Connection).GetMetadataWorkspace();
EntitySetMapping[] entitySetMappingCollection = metadataWorkspace.GetItems<EntityContainerMapping>(DataSpace.CSSpace).Single().EntitySetMappings.ToArray();
var entitySetMappings = entitySetMappingCollection.First(o => o.EntityTypeMappings.Select(e => e.EntityType.Name).Contains(typeof(T).Name));
var entityTypeMapping = entitySetMappings.EntityTypeMappings[0];
foreach (var navigationProperty in entityTypeMapping.EntityType.NavigationProperties)
{
PropertyInfo propertyInfo = typeof(T).GetProperty(navigationProperty.Name);
if (propertyInfo == null)
throw new InvalidOperationException("propertyInfo == null");
if (typeof(System.Collections.IEnumerable).IsAssignableFrom(propertyInfo.PropertyType))
continue;
queryable = queryable.Include(navigationProperty.Name);
}
return queryable;
}
答案 3 :(得分:0)
进一步研究Lunyx的答案,我添加了一个重载以使其可以处理字符串集合(例如,字符串是诸如“ propertyA”或“ propertyA.propertyOfAA”之类的属性名称):
public static IQueryable<T> LoadRelated<T>(this IQueryable<T> originalQuery, ICollection<string> includes)
{
if (includes == null || !includes.Any()) return originalQuery;
Func<IQueryable<T>, IQueryable<T>> includeFunc = f => f;
foreach (var include in includes)
{
IQueryable<T> ChainedFunc(IQueryable<T> f) => f.Include(include);
includeFunc = Compose(includeFunc, ChainedFunc);
}
return includeFunc(originalQuery);
}