使用Linq扩展查找foo或bar

时间:2014-12-01 17:54:03

标签: c# linq

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

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

class Domain
{
    ...
    public string Name { get; set; }
    ...
}

有一个名为“*”的所有域名

使用Linq Extensions,我需要找到所有具有指定域的IdentityProviders,或者找到具有catch all的所有IdentityProviders,但也不是两者。

我如何形成我的查询?

6 个答案:

答案 0 :(得分:2)

这样的事情应该成功:

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子句中使用XOR (^)

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源......)。

答案 1 :(得分:0)

使用标准LINQ函数无法检查您的条件,而不会两次迭代Domains集合,这是不必要的低效率。我会使用像这样的自定义过滤器函数,迭代一次,如果找到两者就提前失败:

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;
})

答案 2 :(得分:0)

如果您按照包含所有内容的域对数据进行分组,例如:

var grouped = ipProviders.Domains
                         .GroupBy (dm => dm.Name == "*");

然后,您可以立即返回所有catch alls或提取具有确切名称的目标域,例如

var targetDomain = "Jabberwocky";

var targets = grouped.Where (gr => gr.Key == (targetDomain == "*"))
                     .Select (gr => gr.Where (dm => dm.Name == targetDomain));

对于JabberwockyOmegaMan以及*

的两个域的数据,分组看起来像这样

enter image description here

答案 3 :(得分:0)

感谢那些回答的人,你在其他领域帮助了我,但是对于这个问题,我最终做了以下,可能不是最好的方法,但它有效:

查看域名是否存在:

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()

答案 4 :(得分:0)

您给出的答案并不能解决您在问题中提出的要求。你说你想要的提供者有一个或另一个但不是两个。

首先,如果提供者同时拥有这两者,则此代码将选择 ,因为第一个条件为真:

d.Domains.Any(n => n.Name == domain && domExists)

其次,如果提供程序具有catch-all但不是指定的域,则如果该域存在于不同的提供程序中,则将被选中。这是因为domExists将为真,因此第二次检查将失败:

d.Domains.Any(n => n.Name == "*" && !domExists)

我不知道捕捉domExists旗帜如何真正帮助你。但是,我认为从搜索整个域名集合开始是正确的想法。你可以试试这个:

首先,收集与&#34; *&#34;匹配的域的提供商的所有ID。或者名称(我假设Domain必须有一个IdentityProvider的外键):

var providerIds =
    db.Domains.Where(d => d.Name == domain || d.Name == "*")
    .Select(d => d.IdentityProviderID)
    .ToList();

这缩小了很多,我们有一种方法可以再次过滤它:任何两个的提供者都会被添加到列表中两次,所以我们只需要选择所有只出现一次的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()

答案 5 :(得分:-1)

请尝试

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