问题
Suppose I have a entity class with one to one relationship as below:
public class Transaction
{
public int TransactionID { get; set; }
public Double Amount { get; set; }
public int TransactionDetailID { get; set; }
public virtual TransactionDetail TransactionDetailFk { get; set; }
}
public class TransactionDetail
{
public int TransactionDetailID { get; set; }
public DateTime PostedDate { get; set; }
public int TransactionTypeID { get; set; }
public int TransactionCategoryID { get; set; }
public int PaymentMethodID { get; set; }
public int PaymentPayorID { get; set; }
public virtual TransactionType TransactionTypeFk { get; set; }
public virtual TransactionCategory TransactionCategoryFk { get; set; }
public virtual PaymentMethod PaymentMethodFk { get; set; }
public virtual PaymentPayor PaymentPayorFk { get; set; }
public virtual Transaction TransactionFk { get; set; }
}
Now I would like to get a Transaction Object based on TransactionID, in addition I would like to get all my related object from Transaction to TransactionDetail to (TransactionType/TransactionCategory/PaymentMethod/PaymentPayor), which is a two-level data mapping and my function would be like:
public async Task<Transaction> GetSingleFullTransactionByIDAsync(int transactionID)
=> await GetSingleOrDefaultAsync(
predicate: tr => (tr.TransactionID == transactionID),
include: (obj => (
obj
.Include(entity => entity.TransactionDetailFk)
.ThenInclude(td => td.PaymentPayorFk)
.Include(entity => entity.TransactionDetailFk)
.ThenInclude(td => td.PaymentMethodFk)
.Include(entity => entity.TransactionDetailFk)
.ThenInclude(td => td.TransactionTypeFk)
.Include(entity => entity.TransactionDetailFk)
.ThenInclude(td => td.TransactionCategoryFk)
))
);
I feel my code is not that clean and tidy, since for each related entity of Transaction Detail, I am actually including multiple instance of transaction detail...I would like to do something like below which only include one instance of Transaction Detail but entity framework does not allow me to do that:
public async Task<Transaction> GetSingleFullTransactionByIDAsync(int transactionID)
=> await GetSingleOrDefaultAsync(
predicate: tr => (tr.TransactionID == transactionID),
include: (obj => (
obj
.Include(entity => entity.TransactionDetailFk)
.ThenInclude(td => td.PaymentPayorFk)
.ThenInclude(td => td.PaymentMethodFk)
.ThenInclude(td => td.TransactionTypeFk)
.ThenInclude(td => td.TransactionCategoryFk)
))
);
So what would be the efficient way for doing that using EF core? Note here I am using repository pattern so I can't use sql-to-linq expression, but only have to use "Include"/"ThenInclude" operation...
回答1:
I feel my code is not that clean and tidy, since for each related entity of Transaction Detail, I am actually including multiple instance of transaction detail...
This is exactly the intended ("by design")way of including multiple related entities in EF Core. It is explained (with example) in the Loading Related Data - Including multiple levels section of the EF Core documentation:
You may want to include multiple related entities for one of the entities that is being included. For example, when querying Blogs, you include Posts and then want to include both the Author and Tags of the Posts. To do this, you need to specify each include path starting at the root. For example, Blog -> Posts -> Author and Blog -> Posts -> Tags. This does not mean you will get redundant joins, in most cases EF will consolidate the joins when generating SQL.
Note the last paragraph. To recap, every Include
/ ThenInclude
chain represents an entity path to be loaded. Each entity contained in the include paths is included just once.
来源:https://stackoverflow.com/questions/51128340/get-object-with-deep-level-of-related-entity