Linq to SQL:聚合||

时间:2010-10-04 15:20:56

标签: c# linq linq-to-sql autocomplete aggregate

我正在尝试编写一个Linq查询,该查询以字符串列表中的至少一个字符串的形式提取其名字或姓氏开头的所有用户。这用于在邮件系统中自动完成收件人。

这是我第一次天真的尝试:

var users = UserRepository.ALL()
foreach (var part in new [] { 'Ha', 'Ho', 'He' })
{
    string part1 = part; // "Copy" since we're coding lazily
    users = users.Where(x => x.LastName.StartsWith(part1) ||
                             x.FirstName.StartsWith(part1));
}

这不起作用,结果变为:

users.Where(a || b).Where(c || d).Where(e || f)...

......我希望:

users.Where(a || b || c || d || e || f || ...)

我将如何做到这一点?

5 个答案:

答案 0 :(得分:0)

你需要使用2个集合 - 在你的代码中你要过滤列表......你真的需要过滤列表的集合 - 而不是已经过滤多次的列表的集合。

一个作为匹配的存储库,另一个作为循环

var userCollection = new Collection<string>();
var users = UserRepository.ALL() 
foreach (var part in new [] { 'Ha', 'Ho', 'He' }) 
{ 
    string part1 = part; // "Copy" since we're coding lazily 
    var matches = users.Where(x => x.LastName.StartsWith(part1) || 
                             x.FirstName.StartsWith(part1)); 
    foreach (var i in matches)
    {
        userCollection.Add(i);
    }
} 

我并不是说这是最优雅的解决方案 - 只是简单地指出你的逻辑失败的原因。

你可以用Contains

做点什么
var results= from i in collection 
            where idList.Contains(i.Id) 
            select i; 

只是看不出我的头顶

答案 1 :(得分:0)

当然,我应该使用Union ...

IQueryable<User> result = null;
foreach (var part in terms)
{
    string part1 = part;
    var q = users.Where(x => x.FirstName.StartsWith(part1) ||
                             x.LastName.StartsWith(part1));
    result = result == null ? q : result.Union(q);
}

使用ReSharper可以将其转换为Linq表达式:

IQueryable<User> result = terms.Select(part1 =>
    users.Where(x => x.FirstName.StartsWith(part1) ||
                     x.LastName.StartsWith(part1)))
         .Aggregate<IQueryable<User>, IQueryable<User>>(
                null, (current, q) => current == null ? q : current.Union(q));

...但这次我可能会去foreach循环。 :)

答案 2 :(得分:0)

这是一个单行(为了可读性而格式化),我认为它会返回您寻找的结果:

 var users = UserRepository.ALL()
    .ToList() //ToList called only to materialize the list
    .Where(x => new[] { 'Ha', 'Ho', 'He' }
        .Any(y => x.LastName.StartsWith(y))
    );  //Don't need it here anymore!

这可能不是您寻求的有效解决方案,但我希望它能以某种方式帮助您!

编辑:正如gaearon指出的,如果'ALL()'命令返回大量记录,我的第一个解决方案可能非常糟糕。试试这个:

var users = UserRepository.ALL()
        .Where(x => new[] { 'Ha', 'Ho', 'He' }
            .Any(y => SqlMethods.Like(x.LastName, y + "%"))
        );

答案 3 :(得分:0)

此代码用于字符串。

var users = new [] {"John", "Richard", "Jack", "Roy", "Robert", "Susan" };
var prefixes = new [] { "J", "Ro" };

var filtered = prefixes.Aggregate(Enumerable.Empty<string>(),
    (acc, pref) => acc.Union(users.Where(u => u.StartsWith(pref)).ToList()));

对于您的User课程,它看起来像

var filtered = prefixes.Aggregate(
    Enumerable.Empty<User>(),
    (acc, pref) => acc.Union(
        users.Where(
            u => u.FistName.StartsWith(pref) || u.LastName.StartsWith(pref)
            ).ToList()));

答案 4 :(得分:-3)

您可以构建表达式树:

var parts = new[] { "Ha", "Ho", "He" };

var x = Expression.Parameter(typeof(User), "x");

var body = 
    parts.Aggregate<string, Expression>(
        Expression.Constant(false), 
        (e, p) => 
            Expression.Or(e,
                Expression.Or(
                    Expression.Call(
                        Expression.Property(x, "LastName"), 
                        "StartsWith", 
                        null,
                        Expression.Constant(p)),
                    Expression.Call(
                        Expression.Property(x, "FirstName"), 
                        "StartsWith", 
                        null, 
                        Expression.Constant(p)))));

var predicate = Expression.Lambda<Func<User, bool>>(body, x);

var result = users.Where(predicate);

结果与:

相同
var result =
    users.Where(x => 
        false ||
        x.LastName.StartsWith("Ha") || x.FirstName.StartsWith("Ha") ||
        x.LastName.StartsWith("Ho") || x.FirstName.StartsWith("Ho") ||
        x.LastName.StartsWith("He") || x.FirstName.StartsWith("He") );