Linq to SQL:通过 || 聚合

发布于 2024-09-26 04:24:00 字数 603 浏览 4 评论 0原文

我正在尝试编写一个 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 || ...)

我将如何去做呢?

I'm trying to write a Linq query which fetches all users whose first or last name begins with at least one string in a list of strings. This is used for auto-completion of recipients in a messaging system.

This was my first naive attempt:

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

This doesn't work though, as the result becomes:

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

...whereas I want:

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

How would I go about doing this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

倒数 2024-10-03 04:24:00

您需要使用 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; 

,只是看不出我的想法

you need to use 2 collections - in your code you are filtering down a list ... really you need the collection of filtered lists - not a collection of a list that has been filtered multiple times.

one as a repository for matches and the other witin your loop

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

I am not claiming this is the most elegant solution - just simply trying to point at why your logic is failing.

you can probably do something with Contains

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

just cant see how off the top of my head

最近可好 2024-10-03 04:24:00

当然,我应该使用 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 循环这次。 :)

Of course, I should use 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);
}

Using ReSharper this could be turned into a Linq expression:

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

...but I'm probably going to go for the foreach loop this time. :)

调妓 2024-10-03 04:24:00

此代码对字符串执行此操作。

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

This code does it for strings.

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

For your User class it would look like

var filtered = prefixes.Aggregate(
    Enumerable.Empty<User>(),
    (acc, pref) => acc.Union(
        users.Where(
            u => u.FistName.StartsWith(pref) || u.LastName.StartsWith(pref)
            ).ToList()));
安静被遗忘 2024-10-03 04:24:00

这是一行行(为了便于阅读而格式化),我相信它会返回您寻求的结果:

 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 + "%"))
        );

Here is a one-liner (formatted for readability) that I believe returns the result you seek:

 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!

It is probably not the efficient solution you were seeking, but I hope it helps you in some way!

EDIT: As gaearon pointed out, if the 'ALL()' command returns a lot of records, my first solution is probably really bad. Try this:

var users = UserRepository.ALL()
        .Where(x => new[] { 'Ha', 'Ho', 'He' }
            .Any(y => SqlMethods.Like(x.LastName, y + "%"))
        );
情徒 2024-10-03 04:24:00

您可以构造一个表达式树:

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

You can construct an expression tree:

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

The result is the same as:

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") );
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文