我有一个简单的问题,但似乎找不到解决的办法。我使用的是EntityFramework核心版本2.0.1,希望在默认情况下加载所有实体。
示例:
public class Order
{
public int Id { get; set; }
public string Name { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public int AddressId { get; set; }
public Address Address { get; set; }
}
public class Address
{
public int Id { get; set; }
public string PostCode { get; set; }
public string City { get; set; }
}
但当我加载订单实体时,相关实体Customer及其内部地址为null
我所尝试的:
这只是一个例子,我有多个嵌套级别的实体,我想在通用存储库中加载嵌套的相关数据,因此不能使用Include和Include,因为加载时我不知道实际的实体类型。
示例:
public virtual async Task<IEnumerable<T>> GetAllAsync(Expression<Func<T, bool>> predicate = null)
{
if (predicate == null)
{
return await Context.Set<T>().ToListAsync();
}
return await Context.Set<T>().Where(predicate).ToListAsync();
}
我错过了什么?我在存储库中是否有问题?任何对更好设计的帮助或建议(如果这就是问题所在)都将不胜感激。
谢谢
使用.Include(“Order.Customer.Address”)
支持:.NETCore3.1.8,但我不知道是否更早
伊万的回答太棒了。我稍微修改了它(在这里使用Chistoph代码的帮助),这样扩展方法就可以与DbContext
本身链接起来,以防其他人觉得更方便。例如,在我的代码库中,我可以编写:
_commissionsContext.CommissionRulesetScopes.IncludeAll().ToListAsync();
这将急切地加载每个CommissionRulesetScope
的整个实体子图:
SELECT [c].[CommissionPlanId], [c].[StartPeriod], [c].[CommissionRulesetId], [c0].[Id], [c0].[Name], [c1].[Id], [c1].[CsiScoreRuleId], [c1].[DealerOptionCommissionRuleId], [c1].[EmailCaptureRuleId], [c1].[ProductCommissionRuleId], [c1].[ProductConsistencyRuleId], [c1].[UnitCommissionRulesetId], [c2].[Id], [c2].[ConsecutiveFailurePenalty], [c2].[CurrentMonthPenalty], [c2].[Enabled], [c2].[Target], [d].[Id], [e].[Id], [e].[Enabled], [e].[Penalty], [e].[Target], [p].[Id], [p0].[Id], [p0].[CommissionBonus], [p0].[Enabled], [p0].[ProductTarget], [p0].[UnitTarget], [u].[Id], [u].[AverageCsiScoreRuleId], [u].[FinancePenetrationRuleId], [u].[GuaranteePeriodCommissionLevel], [u].[MinimumRequiredCsiReturnRate], [u].[MonthlyExpectationAttainmentRuleId], [u].[UnitCommissionTable], [a].[Id], [f].[Id], [m].[Id], [d0].[DealerOptionCommissionRuleId], [d0].[MinimumValue], [d0].[Commission], [t].[ProductCommissionRuleId], [t].[ProductTypeId], [t].[Commission], [t].[Id], [t].[Description], [t].[Key], [t0].[ProductConsistencyRuleId], [t0].[ProductMinMixRangeId], [t0].[Id], [t0].[ProductTypeId], [t0].[Id0], [t0].[Description], [t0].[Key], [t0].[ProductMinMixRangeId0], [t0].[MinimumUnitsTarget], [t0].[Target], [a0].[RuleId], [a0].[Target], [a0].[Points], [f0].[RuleId], [f0].[Target], [f0].[Points], [m0].[RuleId], [m0].[Target], [m0].[Points]
FROM [CommissionRulesetScope] AS [c]
INNER JOIN [CommissionPlan] AS [c0] ON [c].[CommissionPlanId] = [c0].[Id]
INNER JOIN [CommissionRuleset] AS [c1] ON [c].[CommissionRulesetId] = [c1].[Id]
LEFT JOIN [CsiScoreRule] AS [c2] ON [c1].[CsiScoreRuleId] = [c2].[Id]
LEFT JOIN [DealerOptionCommissionRule] AS [d] ON [c1].[DealerOptionCommissionRuleId] = [d].[Id]
LEFT JOIN [EmailCaptureRule] AS [e] ON [c1].[EmailCaptureRuleId] = [e].[Id]
LEFT JOIN [ProductCommissionRule] AS [p] ON [c1].[ProductCommissionRuleId] = [p].[Id]
LEFT JOIN [ProductConsistencyRule] AS [p0] ON [c1].[ProductConsistencyRuleId] = [p0].[Id]
LEFT JOIN [UnitCommissionRuleset] AS [u] ON [c1].[UnitCommissionRulesetId] = [u].[Id]
LEFT JOIN [AverageCsiScoreRule] AS [a] ON [u].[AverageCsiScoreRuleId] = [a].[Id]
LEFT JOIN [FinancePenetrationRule] AS [f] ON [u].[FinancePenetrationRuleId] = [f].[Id]
LEFT JOIN [MonthlyExpectationAttainmentRule] AS [m] ON [u].[MonthlyExpectationAttainmentRuleId] = [m].[Id]
LEFT JOIN [DealerOptionCommission] AS [d0] ON [d].[Id] = [d0].[DealerOptionCommissionRuleId]
LEFT JOIN (
SELECT [p1].[ProductCommissionRuleId], [p1].[ProductTypeId], [p1].[Commission], [p2].[Id], [p2].[Description], [p2].[Key]
FROM [ProductCommission] AS [p1]
LEFT JOIN [ProductType] AS [p2] ON [p1].[ProductTypeId] = [p2].[Id]
) AS [t] ON [p].[Id] = [t].[ProductCommissionRuleId]
LEFT JOIN (
SELECT [p3].[ProductConsistencyRuleId], [p3].[ProductMinMixRangeId], [p4].[Id], [p4].[ProductTypeId], [p5].[Id] AS [Id0], [p5].[Description], [p5].[Key], [p6].[ProductMinMixRangeId] AS [ProductMinMixRangeId0], [p6].[MinimumUnitsTarget], [p6].[Target]
FROM [ProductMinMixRangeAssociation] AS [p3]
INNER JOIN [ProductMinMixRange] AS [p4] ON [p3].[ProductMinMixRangeId] = [p4].[Id]
INNER JOIN [ProductType] AS [p5] ON [p4].[ProductTypeId] = [p5].[Id]
LEFT JOIN [ProductMinMixTarget] AS [p6] ON [p4].[Id] = [p6].[ProductMinMixRangeId]
) AS [t0] ON [p0].[Id] = [t0].[ProductConsistencyRuleId]
LEFT JOIN [AverageCsiScoreThreshold] AS [a0] ON [a].[Id] = [a0].[RuleId]
LEFT JOIN [FinancePenetrationThreshold] AS [f0] ON [f].[Id] = [f0].[RuleId]
LEFT JOIN [MonthlyExpectationAttainmentThreshold] AS [m0] ON [m].[Id] = [m0].[RuleId]
ORDER BY [c].[CommissionPlanId], [c].[StartPeriod], [c0].[Id], [c1].[Id], [d0].[DealerOptionCommissionRuleId], [d0].[MinimumValue], [t].[ProductCommissionRuleId], [t].[ProductTypeId], [t0].[ProductConsistencyRuleId], [t0].[ProductMinMixRangeId], [t0].[Id], [t0].[Id0], [t0].[ProductMinMixRangeId0], [t0].[MinimumUnitsTarget], [a0].[RuleId], [a0].[Target], [f0].[RuleId], [f0].[Target], [m0].[RuleId], [m0].[Target]
改编如下:
public static class DbSetExtensions
{
/// <summary>
/// Ensures that all navigation properties (up to a certain depth) are eagerly loaded when entities are resolved from this
/// DbSet.
/// </summary>
/// <returns>The queryable representation of this DbSet</returns>
public static IQueryable<TEntity> IncludeAll<TEntity>(
this DbSet<TEntity> dbSet,
int maxDepth = int.MaxValue) where TEntity : class
{
IQueryable<TEntity> result = dbSet;
var context = dbSet.GetService<ICurrentDbContext>().Context;
var includePaths = GetIncludePaths<TEntity>(context, maxDepth);
foreach (var includePath in includePaths)
{
result = result.Include(includePath);
}
return result;
}
/// <remarks>
/// Adapted from https://stackoverflow.com/a/49597502/1636276
/// </remarks>
private static IEnumerable<string> GetIncludePaths<T>(DbContext context, int maxDepth = int.MaxValue)
{
if (maxDepth < 0)
throw new ArgumentOutOfRangeException(nameof(maxDepth));
var entityType = context.Model.FindEntityType(typeof(T));
var includedNavigations = new HashSet<INavigation>();
var stack = new Stack<IEnumerator<INavigation>>();
while (true)
{
var entityNavigations = new List<INavigation>();
if (stack.Count <= maxDepth)
{
foreach (var navigation in entityType.GetNavigations())
{
if (includedNavigations.Add(navigation))
entityNavigations.Add(navigation);
}
}
if (entityNavigations.Count == 0)
{
if (stack.Count > 0)
yield return string.Join(".", stack.Reverse().Select(e => e.Current!.Name));
}
else
{
foreach (var navigation in entityNavigations)
{
var inverseNavigation = navigation.FindInverse();
if (inverseNavigation != null)
includedNavigations.Add(inverseNavigation);
}
stack.Push(entityNavigations.GetEnumerator());
}
while (stack.Count > 0 && !stack.Peek().MoveNext())
stack.Pop();
if (stack.Count == 0)
break;
entityType = stack.Peek().Current!.GetTargetType();
}
}
}
目前官方不存在此类功能(EF Core 2.0.2和即将推出的2.1)。它是在“急切加载所有导航属性”(4851(已关闭)中请求的,目前由基于规则的“急切加载”(include)2953跟踪,并允许在模型中声明聚合(例如,定义包含的属性或通过其他方式)#1985(都在积压中,即没有具体的时间表)。
我可以提供以下两种自定义扩展方法:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore.Metadata;
namespace Microsoft.EntityFrameworkCore
{
public static partial class CustomExtensions
{
public static IQueryable<T> Include<T>(this IQueryable<T> source, IEnumerable<string> navigationPropertyPaths)
where T : class
{
return navigationPropertyPaths.Aggregate(source, (query, path) => query.Include(path));
}
public static IEnumerable<string> GetIncludePaths(this DbContext context, Type clrEntityType, int maxDepth = int.MaxValue)
{
if (maxDepth < 0) throw new ArgumentOutOfRangeException(nameof(maxDepth));
var entityType = context.Model.FindEntityType(clrEntityType);
var includedNavigations = new HashSet<INavigation>();
var stack = new Stack<IEnumerator<INavigation>>();
while (true)
{
var entityNavigations = new List<INavigation>();
if (stack.Count <= maxDepth)
{
foreach (var navigation in entityType.GetNavigations())
{
if (includedNavigations.Add(navigation))
entityNavigations.Add(navigation);
}
}
if (entityNavigations.Count == 0)
{
if (stack.Count > 0)
yield return string.Join(".", stack.Reverse().Select(e => e.Current.Name));
}
else
{
foreach (var navigation in entityNavigations)
{
var inverseNavigation = navigation.FindInverse();
if (inverseNavigation != null)
includedNavigations.Add(inverseNavigation);
}
stack.Push(entityNavigations.GetEnumerator());
}
while (stack.Count > 0 && !stack.Peek().MoveNext())
stack.Pop();
if (stack.Count == 0) break;
entityType = stack.Peek().Current.GetTargetType();
}
}
}
}
第一种方法只是应用多个字符串baseInclude
的一种方便方法。
第二个任务是使用EF核心提供的元数据收集类型的所有Include
路径。它基本上是从传递的实体类型开始的有向循环图处理,不包括包含路径的反向导航,只发送到“叶”节点的路径。
您的示例中的用法可以是这样的:
public virtual async Task<IEnumerable<T>> GetAllAsync(Expression<Func<T, bool>> predicate = null)
{
var query = Context.Set<T>()
.Include(Context.GetIncludePaths(typeof(T));
if (predicate != null)
query = query.Where(predicate);
return await query.ToListAsync();
}
我正在使用EF Core(v1.1.3)和C#。 在我的单元测试中,我使用代码优先db模型创建了一个sqlite内存数据库,然后用一些测试数据填充它。但是,当从dB加载实体列表时,相关实体会被填充,尽管没有使用“。包含”。 如果我使用相同的模型来创建一个实际的磁盘数据库,并进行相同的加载,那么只有在使用<代码>时,才会加载相关的实体。包括。 这是sqlite内存实现(v1.1.0)中的错误,还是我
我想使用即时加载在实体框架核心中获得多个嵌套级别的子表。我认为延迟加载还没有实现。 我找到了EF6的答案。 我的问题是,EF Core中无法识别选择 错误CS1061“雇员”不包含“选择”的定义,并且找不到接受“雇员”类型的第一个参数的扩展方法“选择”(您是否缺少使用指令或程序集引用?) 我包含的命名空间: EF Core中的选择选项是什么。
我已经找到了几个这样的帖子,但不能确定我在这里做错了什么。 我试着在两张桌子上各放一个项目。第二项包含对第一项的查找。我可以在没有问题的情况下为父级添加种子,但无法找到正确的语法来为子级添加种子。 下面是我用来种子父级的代码 下面是种子子的代码--这将失败: 根据文档的建议,我使用了一个匿名对象,而不是类本身。我也尝试过同时使用navigation属性、外键和nav道具。ID=1的组肯定已经在db
当将实体模型转换为DTO时,我对Entity Framework Core(v2.0.1)有一个问题。基本上,通过该短语的任何其他版本,当我不想加载时,它是延迟加载的。这是一个简单的。NET Core Console应用程序(带有Microsoft. EntityFrameworkCore. SqlServer(2.0.1)包)。 现在,当执行方法来检索数据时,它正在执行以下SQL 如您所见,它没
实体框架5.0首先使用现有数据库编写代码。使用电动工具对类进行逆向工程。一切都很好。数据库有两个表。一个父母和一个孩子的外键返回到父母ID。ID都是带有自动增量的int。我添加了许多父母记录,现在想将孩子记录添加到特定的父母。我能看到的唯一方法是通过在父母表中搜索名称或其他属性并返回ID来找到适当的父母ID。然后在添加孩子时在外键属性中使用该ID。我不想设置新父母,所以这是将孩子添加到现有父母的唯
我正在尝试编写一个Updatestatus方法,该方法仅在我保存对数据库的更改时更新实体的状态字段。如果实体中的任何其他字段发生了变化,我不想将这些更改保存到数据库中。对于实体自己的字段来说,这很简单,使用: 但是,我发现,如果相关实体没有键值集,则将插入通过我设置状态的实体的导航属性可访问的任何相关实体。因此,如果将新的子实体添加到实体中。子项,并且子实体ChildId属性为0,则该子项将插入到