英文:
How to eager load and include the children of an ICollection only using include?
问题
你遇到的问题涉及虚拟的 ICollections
集合在使用 include 时不被视为属性,但在运行时仍然可以访问。例如,LoanClass
类中的 LoanStatuses
属性无法使用 include 进行预加载,但仍可以在运行时访问。你想知道如何预加载 Loan statuses 的子属性。
以下是你提供的代码的翻译部分:
// 问题出在这里,我无法通过 include 来预加载 LoanStatuses 表/对象的属性,例如:
Loan loan = LoanUnitOfWork.LoanRepository.LoadByLoanNumberWithRequiredData(loanNumber, l => l.LoanStatuses.*Status.Code*);
// 这将不允许我这样做。
// 如果我在 include 查询中省略上面斜体代码,那么属性将不会被预加载,EF 将多次调用以收集这些 Status.Codes。
希望这有助于解决你的问题。
英文:
The issue I am having involves virtual ICollections of collections not being considered as properties when using include, while still being able to be accessed as such at runtime.
For example 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);
}
The issue here is that I can eager load other properties of the loan class because they directly refer to the loan table in the db.
But when I go to eager, load properties on the LoanStatuses table/object like so:
Loan loan = LoanUnitOfWork.LoanRepository.LoadByLoanNumberWithRequiredData(loanNumber,l => l.LoanStatuses.*Status.Code*);
It will not allow me.
And if I leave the italicised code above out of the include query, then the properties are not eager loaded and EF makes many calls out to collect these 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)
How can I eager load the child properties of Loan statuses?
答案1
得分: 0
在EF6中,您可以使用ICollection.Select()
来包含ICollection的子项,这在EF Core中仍然是有效的语法,但会引发InvalidOperationException:
> InvalidOperationException: 在“Include”操作中,表达式'l.LoanStatuses.AsQueryable().Select(ls=> ls.Status)
'是无效的,因为它不代表属性访问:'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。
我曾经遇到过相同的问题,但我找到了一种解决方案,虽然不完全符合您的要求,但能够完成工作。
我使用了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));
我知道这不是您要求的方式,但这是一个巧妙的解决方法。
英文:
Well I don't think you can. In EF6 you could Include the children of an ICollection using ICollection.Select()
, which is still valid Syntax in EF Core, but you get an InvalidOperationException:
> InvalidOperationException: The expression 'l.LoanStatuses.AsQueryable().Select(ls=> ls.Status)' is invalid inside an 'Include' operation, since it does not represent a property access: 't => t.MyProperty'. To target navigations declared on derived types, use casting ('t => ((Derived)t).MyProperty') or the 'as' operator ('t => (t as Derived).MyProperty'). Collection navigation access can be filtered by composing Where, OrderBy(Descending), ThenBy(Descending), Skip or Take operations. For more information on including related data, see http://go.microsoft.com/fwlink/?LinkID=746393.
I had the same Problem, but I found a Solution, that is not exactly what you wanted, but gets the Job done.
Instead of params Expression<Func<Loan, object>>[] includes
I used Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include
which would look something like that:
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));
I know it's not what you asked for, but it's a neat workaround.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论