C# 使用Linq扩展查找foo或bar

C# 使用Linq扩展查找foo或bar,c#,linq,C#,Linq,我有一个对象“IdentityProvider”,而“IdentityProvider”有子域 class IdentityProvider { ... public virtual ICollection<Domain> Domains { get; set; } ... } class Domain { ... public string Name { get; set; } ... } class IdentityProvid

我有一个对象“IdentityProvider”,而“IdentityProvider”有子域

class IdentityProvider
{
    ...
    public virtual ICollection<Domain> Domains { get; set; }
    ...
}

class Domain
{
    ...
    public string Name { get; set; }
    ...
}
class IdentityProvider
{
...
公共虚拟ICollection域{get;set;}
...
}
类域
{
...
公共字符串名称{get;set;}
...
}
有一个名为“*”的全包域

使用Linq扩展,我需要找到所有具有指定域的IdentityProviders,或者具有catch all的IdentityProviders,但也不能同时具有两者


我将如何形成我的查询?

类似这样的内容应该可以:

from i in identityProviders
let hasDomain = i.Domains.Any(d => d.Name == domainName)
let hasCatchAll = i.Domains.Any(d => d.Name == "*")
where (hasDomain && !hasCatchAll) || (!hasDomain && hasCatchAll)
select i;
您可以尝试在
where
子句中使用:

from i in identityProviders
let hasDomain = i.Domains.Any(d => d.Name == domainName)
let hasCatchAll = i.Domains.Any(d => d.Name == "*")
where hasDomain ^ !hasCatchAll
select i;
但是我不确定您的提供商是否将它翻译成SQL(您没有指定您正在处理的是哪种LINQ源…。

请尝试

identityProviders.Where(ip=>ip.Domains.Any(d=>d.Name=="SearchDomain" || d.Name=="*"))

如果不迭代
集合两次,就无法使用标准LINQ函数检查您的情况,这是不必要的低效。我会使用这样一个自定义筛选函数,迭代一次,如果两者都找到,则会提前失败:

identityProviders.Where(identityProvider => {
    bool hasDomain = false, hasCatchAll = false;
    foreach (var domain in identityProvider.Domains) {
        hasDomain = hasDomain || domain.Name == domainName;
        hasCatchAll = hasCatchAll || domain.Name == "*";
        if (hasDomain && hasCatchAll) return false;
    }
    return hasDomain || hasCatchAll;
})

如果按具有“一网打尽”功能的域对数据进行分组,例如:

var grouped = ipProviders.Domains
                         .GroupBy (dm => dm.Name == "*");
然后,您可以一次返回所有捕获的域,也可以提取具有确切名称的目标域,例如

var targetDomain = "Jabberwocky";

var targets = grouped.Where (gr => gr.Key == (targetDomain == "*"))
                     .Select (gr => gr.Where (dm => dm.Name == targetDomain));
分组如下所示,数据为
Jabberwocky
OmegaMan
,两个域为
*


多亏了那些回答的人,你在其他方面帮助了我,但对于这个问题,我最后做了以下几点,可能不是最好的方法,但它很有效:

查看该域是否存在:

var domExists = db.Domains.Any(d => d.Name == domain);
查找域存在且存在domExists的所有身份提供程序,或者查找通配符而不是domExists

IdentityProviders.Where(d => 
    d.Domains.Any(n => n.Name == domain && domExists) || 
    d.Domains.Any(n => n.Name == "*" && !domExists)
).Any()

你给出的答案并没有满足你在问题中的要求。你说你想要一个或另一个但不是两个都有的提供商

首先,如果提供程序同时具有这两个条件,则此代码将选择它,因为第一个条件为true:

d.Domains.Any(n => n.Name == domain && domExists)
其次,如果某个提供程序具有“全部捕获”功能,但没有指定的域,则如果该域确实存在于其他提供程序中,则不会选择该提供程序。这是因为
domExists
将为真,因此第二次检查将失败:

d.Domains.Any(n => n.Name == "*" && !domExists)
我看不出捕获
domExists
标志对您有多大帮助。然而,我认为从搜索整个域集合开始是正确的想法。你可以试试这个:

首先,收集与“*”或名称匹配的域的所有提供者ID(我假设域必须有一个指向IdentityProvider的外键):

这就缩小了它的范围,我们有一种再次过滤它的方法:任何同时具有这两个属性的提供者都将被添加到列表中两次,因此我们只需要选择所有只出现一次的ID:

var uniqueProviderIds =
    providerIds.GroupBy(id => id)
    .Where(g => g.Count() == 1)
    .Select(g => g.Key)
    .ToList();
现在
uniqueProviderIds.Any()
将为您提供答案。如果需要,还可以使用此列表构建另一个SQL查询以获取实际的IdentityProvider对象:

db.IdentityProviders.Where(ip => uniqueProviderIds.Contains(ip.ID)).ToList()

IdentityProvider
外观如何?类型定义?您是否已经尝试过任何代码?@RahulSingh添加了类定义。。。我已经尝试了很多组合,但目前没有任何组合能在任何地方起作用。它们非常接近,但是当存在域匹配时,也会返回catchall。我可能没有正确使用它,因为我遇到了一个错误:带有语句体的lambda表达式无法转换为表达式tree@Jamie那是因为你在使用实体框架,它正在尝试将查询转换为SQL,因此不能使用此方法。
db.IdentityProviders.Where(ip => uniqueProviderIds.Contains(ip.ID)).ToList()