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

Entity framework 如果第一个linq查询返回空,如何从另一个查询列表中获取结果?,entity-framework,linq,Entity Framework,Linq,在这个EF查询中,对于联系人列表,我试图查询以获取ContactTypeA的记录,并在联系人列表中填充结果。若ContactTypeA并没有记录,那个么我希望它查询ContactTypeB的记录并将结果填充到contacts列表中。我尝试使用DefaultIfEmpty,但该方法只接受单个值,不接受列表“联系人”是一个列表对象。有什么想法,甚至是DefaultIfEmpty的替代方案吗?谢谢 select(i => new transaction{ .... contacts = con

在这个EF查询中,对于联系人列表,我试图查询以获取ContactTypeA的记录,并在联系人列表中填充结果。若ContactTypeA并没有记录,那个么我希望它查询ContactTypeB的记录并将结果填充到contacts列表中。我尝试使用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()
}
)

首先,确保您的
contactRepository.All()
方法返回
IQueryable
而不是
IEnumerable
IList
等,否则您将自动将所有联系人加载到内存中

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

理想情况下,帐户可以有一组联系人,但如果没有,则联系人至少应具有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(与包含ContactTypeId的Contact相反?),但上面反映了示例中的关系

使用包含联系人集合的帐户:

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();
在处理表达式中的条件时,我可以建议返回所有相关细节,然后根据条件构建最终的“有效载荷”

例如,如果查询帐户以生成事务,但希望加载ContactAs(如果可用)和Bs(如果不可用):

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查询中投影到最小可行的视图模型中。谢谢您的回答,但正如您所看到的,我的代码在linq Select语句中(请注意顶行)。因此,我不能使用语句或变量。在构建条件种群时,我已经扩展了要考虑的选项的答案,以选择两种场景的细节,然后从这些结果构建最终的投影。(在内存中)使用中间
Select
(作为LINQ查询语法
let
)捕获
ContactTypeA
,然后使用条件表达式:
。选择(i=>new{i,ContactAs=ContactTypeAs})。选择(ica=>new transaction{…,contacts=ica.ContactAs.Any()?ContactAs:ContactTypeBs})
-我不保证这可以由EF翻译成SQL(取决于EF的版本)