如果第一个linq查询返回空,如何从另一个查询列表中获取结果?

时间:2020-11-06 01:42:53

标签: entity-framework linq

在此EF查询中,对于联系人列表,我试图查询以获取ContactTypeA的记录并在联系人列表中填充结果。如果没有ContactTypeA的记录,那么我希望它查询ContactTypeB的记录并在联系人列表中填充结果。我尝试使用DefaultIfEmpty,但是该方法仅接受单个值,而不接受列表。 “联系人”是一个列表对象。有什么想法,甚至可以替代DefaultIfEmpty吗?谢谢。

 select(i => new transaction{
....
contacts = contactRepository.All.Where(c => c.AccountId == i.Account.Id && contactTypeRepository.All.Any(ct => ct.ContactId == c.Id && ct.Type == ContactType.ContactTypeA)).ToList().DefaultIfEmpty((contactRepository.All.Where(c => c.AccountId == i.Account.Id && contactTypeRepository.All.Any(ct => ct.ContactId == c.Id && ct.Type == ContactType.ContactTypeB)).ToList()
}
)

1 个答案:

答案 0 :(得分:0)

首先,请绝对确保您的contactRepository.All()方法返回IQueryable<Contact>而不返回IEnumerable<Contact>IList<Contact>等,否则您将自动加载 all 联系人进入内存。

从那里开始,不要害怕跨多个语句简化查询以使其更容易理解。您还应该利用导航属性,而不是依赖完全断开连接的实体和通用存储库,并手动将它们全部组合成巨大的表达式。

理想情况下,一个帐户可以具有一组联系人,但如果没有,则至少联系人应该具有ContactType引用:

var accountContactsQuery = contactRepository.All
    .Where(c => c.AccountId == i.AccountId); //Remains IQueryable
var contacts = accountContactsQuery
    .Where(c => c.ContactTypes.Any(ct => c.Type == ContactType.ContactTypeA)
    .ToList(); // Gets List of Contacts where contains at least 1 ContactTypeA type.
// If we have none, replace with results for ContactTypeB
if (!contacts.Any())
    contacts = accountContactsQuery
        .Where(c => c.ContactTypes.Any(ct => c.Type == ContactType.ContactTypeB)
        .ToList();

这看起来有点奇怪,因为您的ContactType似乎有一个ContactId,(相对于Contact中包含一个ContactTypeId?),但这在您的示例中反映了这种关系。

对于包含联系人集合的帐户:

var accountContactsQuery = accountRepoitory.All
    .Where(a => true /* replace with relevant criteria */);
var contacts = accountContactsQuery
    .SelectMany(a => a.Contacts)
    .Where(c => c.ContactTypes.Any(ct => c.Type == ContactType.ContactTypeA)
    .ToList(); // Gets List of Contacts where contains at least 1 ContactTypeA type.
// If we have none, replace with results for ContactTypeB
if (!contacts.Any())
    contacts = accountContactsQuery
        .SelectMany(a => a.Contacts)
        .Where(c => c.ContactTypes.Any(ct => c.Type == ContactType.ContactTypeB)
        .ToList();

在处理表达式中的条件时,我建议返回所有相关详细信息,然后根据条件构建最终的“有效载荷”。

例如,如果查询帐户以建立交易,但希望加载ContactA(如果可用),并希望为每个帐户加载B(如果不可用):

var transactionData = accountRepoitory.All
    .Where(a => true /* replace with relevant criteria */);
    .Select(a => new 
    {
        a.AccountId, 
        /* populate account and common details.. */
        ContactAs = a.Contacts
            .Where(c => c.ContactTypes.Any(ct => c.Type == ContactType.ContactTypeA).ToList(), // Consider a Select to get relevant details...
        ContactBs = a.Contacts
            .Where(c => c.ContactTypes.Any(ct => c.Type == ContactType.ContactTypeB).ToList()
   }).ToList(); // Executes query against DB to load relevant data...

var transactions = transactionData
    .Select( t => new Transaction
    {
        AccountId = t.AccountId,
        /* Other fields */
        Contacts = t.ContactAs.Any() ? t.ContactAs : t.ContactBs
    }).ToList();

基本上使用EF Linq表达式加载可能的数据,因此同时包含ContactA和ContactB的结果,然后再使用该数据构建最终投影,并有条件地使用ContactA或B。通常,我不建议回传实体(实际联系实体),而是在使用Select的第一个EF查询中将其投影到最低可行的视图模型中。