提问人:sebastian whiffen 提问时间:6/9/2023 最后编辑:Palle Duesebastian whiffen 更新时间:6/23/2023 访问量:125
如何仅使用 include 预先加载和包含 ICollection 的子项?
How to eager load and include the children of an ICollection only using include?
问:
我遇到的问题涉及在使用 include 时未被视为属性的集合的虚拟 ICollections,同时仍然能够在运行时进行访问。
例如:LoanClass
public class Loan : BaseEntity
{
private ICollection<LoanStatus> _LoanStatuses;
public virtual ICollection<LoanStatus> LoanStatuses
{
get { return _LoanStatuses ?? (_LoanStatuses = new Collection<LoanStatus>()); }
set { _LoanStatuses = value; }
}
}
public class LoanStatus : BaseEntity
{
public int ID { get; set; }
public int LoanID { get; set; }
public int StatusID { get; set; }
[ForeignKey("LoanID")]
public virtual Loan Loan { get; set; }
[ForeignKey("StatusID")]
public virtual CodeType Status { get; set; }
}
public class CodeType : BaseEntity
{
public int ID { get; set; }
public string Code { get; set; }
public string Description { get; set; }
public string Category { get; set; }
public int? Sequence { get; set; }
public bool IsCodeType(CodeTypeCategory category, string code)
{
return Category.Equals(category.ToString()) && Code.Equals(code);
}
}
service layer
:
Loan loan = LoanUnitOfWork.LoanRepository.LoadByLoanNumberWithRequiredData(loanNumber,l => l.LoanStatuses);
Repository
:
public Loan LoadByLoanNumberWithRequiredData(string loanNumber, params Expression<Func<Loan, object>>[] includes)
{
return includes.Aggregate(Context.Loans.AsQueryable()
,(current, include) => current.Include(include)).FirstOrDefault(l => l.LoanNumber == loanNumber);
}
这里的问题是,我可以急切地加载 loan 类的其他属性,因为它们直接引用数据库中的 loan 表。
但是当我去急切时,像这样加载 LoanStatuses 表/对象上的属性:
Loan loan = LoanUnitOfWork.LoanRepository.LoadByLoanNumberWithRequiredData(loanNumber,l => l.LoanStatuses.*Status.Code*);
它不会允许我。
如果我将上面的斜体代码排除在包含查询之外,则属性不会预先加载,并且 EF 会发出许多调用来收集这些 Status.Codes:
Microsoft.EntityFrameworkCore.Database.Command: Information: Executed DbCommand (20ms) [Parameters=[@__p_0='?' (DbType = Int32)], CommandType='Text', CommandTimeout='600']
SELECT [c].[Ct_ID], [c].[Active], [c].[Ct_Category], [c].[Ct_Code], [c].[Ct_CreatedBy], [c].[Ct_DateCreated], [c].[Ct_DateUpdated], [c].[Ct_Description], [c].[Ct_Sequence], [c].[Ct_UpdatedBy]
FROM [Code_Types] AS [c]
WHERE ([c].[Active] = CAST(1 AS bit)) AND ([c].[Ct_ID] = @__p_0)
Microsoft.EntityFrameworkCore.Database.Command: Information: Executed DbCommand (11ms) [Parameters=[@__p_0='?' (DbType = Int32)], CommandType='Text', CommandTimeout='600']
SELECT [c].[Ct_ID], [c].[Active], [c].[Ct_Category], [c].[Ct_Code], [c].[Ct_CreatedBy], [c].[Ct_DateCreated], [c].[Ct_DateUpdated], [c].[Ct_Description], [c].[Ct_Sequence], [c].[Ct_UpdatedBy]
FROM [Code_Types] AS [c]
WHERE ([c].[Active] = CAST(1 AS bit)) AND ([c].[Ct_ID] = @__p_0)
Microsoft.EntityFrameworkCore.Database.Command: Information: Executed DbCommand (10ms) [Parameters=[@__p_0='?' (DbType = Int32)], CommandType='Text', CommandTimeout='600']
SELECT [c].[Ct_ID], [c].[Active], [c].[Ct_Category], [c].[Ct_Code], [c].[Ct_CreatedBy], [c].[Ct_DateCreated], [c].[Ct_DateUpdated], [c].[Ct_Description], [c].[Ct_Sequence], [c].[Ct_UpdatedBy]
FROM [Code_Types] AS [c]
WHERE ([c].[Active] = CAST(1 AS bit)) AND ([c].[Ct_ID] = @__p_0)
Microsoft.EntityFrameworkCore.Database.Command: Information: Executed DbCommand (312ms) [Parameters=[@__p_0='?' (DbType = Int32)], CommandType='Text', CommandTimeout='600']
SELECT [c].[Ct_ID], [c].[Active], [c].[Ct_Category], [c].[Ct_Code], [c].[Ct_CreatedBy], [c].[Ct_DateCreated], [c].[Ct_DateUpdated], [c].[Ct_Description], [c].[Ct_Sequence], [c].[Ct_UpdatedBy]
FROM [Code_Types] AS [c]
WHERE ([c].[Active] = CAST(1 AS bit)) AND ([c].[Ct_ID] = @__p_0)
如何快速加载贷款状态的子属性?
答:
好吧,我认为你不能。在 EF6 中,可以使用 包含 ICollection 的子级,这在 EF Core 中仍然有效,但会收到 InvalidOperationException:ICollection.Select()
InvalidOperationException:表达式 'l.LoanStatuses.AsQueryable()。选择 (ls=> ls。Status)' 在 'Include' 操作中无效,因为它不表示属性访问: 't => t.MyProperty'。若要以在派生类型上声明的导航为目标,请使用强制转换 ('t => ((Derived)t)。MyProperty') 或 'as' 运算符 ('t => (t as Derived)。MyProperty“)。可以通过组合 Where、OrderBy(Descending)、ThenBy(Descending)、Skip 或 Take 操作来筛选集合导航访问。有关包含相关数据的详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkID=746393。
我有同样的问题,但我找到了一个解决方案,这并不完全是你想要的,但可以完成工作。
而不是我使用看起来像这样的东西:params Expression<Func<Loan, object>>[] includes
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include
public Loan LoadByLoanNumberWithRequiredData(string loanNumber, Func<IQueryable<Loan>, IIncludableQueryable<Loan, object>> include)
{
return include(Context.Loans.AsQueryable())
.FirstOrDefault(l => l.LoanNumber == loanNumber);
}
Loan loan = LoanUnitOfWork.LoanRepository.LoadByLoanNumberWithRequiredData(loanNumber, loan => loan
.Include(l => l.LoanStatuses).ThenInclude(ls => ls.Status));
我知道这不是你要求的,但这是一个巧妙的解决方法。
评论
DbContext
DbSet
DbContext
IQueryable
IQueryable
GetByLoanNumberIncludingLoadStatus