【问题标题】:Return records that match all the supplied parameters skip if list is null如果列表为空,则返回与所有提供的参数匹配的记录
【发布时间】:2021-08-14 01:14:17
【问题描述】:

我想按过滤列表条件搜索列表。

例如,我将contact_id、姓名、州和城市列表作为字符串列表。

public async Task<List<ContactRequestListModel>> SearchByMultipleAsync(
    List<string> contact_id, 
    List<string> email,
    List<string> phone, 
    List<string> name, 
    List<string> accountnumber, 
    List<string> city, 
    List<string> state)
{


var line = (from x in filtered
            where city.Contains(x.City)
         || contact_id.Contains(x.Id.ToString())
         || state.Contains(x.State)
         || name.Contains(x.FirstName + " " + x.MiddleName ?? "" + " " + x.LastName) select x).ToList();

return line;
}

上述 LINQ 查询返回与传递的任何参数匹配的所有记录。它还返回 citystates 匹配但没有匹配 contact_id 的记录

如何搜索与所有提供的参数匹配的记录,如果提供的参数列表为空或未找到,则应跳过搜索。

【问题讨论】:

  • (city == null || city.Contains(x.City))?如果是null,我们跳过city,如果不是null,我们想要匹配
  • 列表可以为空但不能为空
  • (city.Count &lt;= 0 || city.Contains(x.City)) 那么呢?如果城市为空,我们会跳过它,并在它有值时想要匹配
  • 只有contact_id = 61的记录,但是因为匹配城市和州,所以返回两条记录。
  • 如果您只想要一条带有contact_id = 61 且需要城市 州的记录,请将条件与&amp;&amp; 结合起来,而不是||,或者将每个条件放在自己的位置where

标签: c# linq


【解决方案1】:

最好通过过滤器的存在来过滤:

    public class ContactRequestSearchRequest
    {
        public List<string> ContactIds { get; set; }
        public List<string> Emails { get; set; }
        public List<string> Phones { get; set; }
        public List<string> Names { get; set; }
        public List<string> AccountNumbers { get; set; }
        public List<string> Cities { get; set; }
        public List<string> States { get; set; }
    }

    public static async Task<List<ContactRequestListModel>> SearchAsync(ContactRequestSearchRequest request, CancellationToken cancellationToken = default)
    {
        var query = filtered.AsQueryable();
        if (request.ContactIds != null)
            query = query.Where(x => request.ContactIds.Contains(x.Id));
        if (request.Emails != null)
            query = query.Where(x => request.Emails.Contains(x.mail.address));

        //all other filterings...

        if (request.Names != null)
            query = query.Where(x => request.Names.Contains(x.FirstName + " " + x.MiddleName ?? "" + " " + x.LastName));

        return await query.ToListAsync(cancellationToken).ConfigureAwait(false);
    }

这有两个原因:

  • 如您所见,它们是相似的,以后如果替换属性,将很容易重构为常用方法,例如FilterBy

  • EF 查询是愚蠢的,如果你在其中包含一些东西,它仍然会产生像1=1 这样的垃圾,从而扼杀查询计划和性能。因此,最好在添加要查询的内容之前检查代码。

考虑您的用例

假设如果我尝试使用州、城市进行过滤并且没有通过名称,则返回的列表将为空

不清楚您想要什么,如果您的任何过滤器为空/空,则不返回任何内容 - 然后只需在函数开头检查它:

        if (request.ContactIds == null && !request.ContactIds.Any() || 
            request.Emails == null || !request.Emails.Any() ||
            ...)
            return new List<ContactRequestListModel>();

【讨论】:

  • 难道没有办法在一行中完成所有过滤器吗?没有多个 if 条件?
  • 没有。为什么你还是想这样做? en.wikipedia.org/wiki/KISS_principle
  • 我在你的查询中发现了一些缺点,当我必须使用带有这样.Where(x =&gt; (name?.Count ?? 0) &lt;= 0 || name.Contains(string.Join(" ", new string[] { x.FirstName, x.MiddleName, x.LastName} .Where(s =&gt; !string.IsNullOrEmpty(s))) )); 名称的 string.join 时,它无法转换为表达式树。
  • 这不是我的缺点,它是 IQueryable 的一般方法。 EF 不支持任何东西,也不需要/不需要与 C# 中的所有内容等效的 SQL,因此,如果您想要查询中的特定内容,例如转换/RPC/计算 - 只需将其置于查询之外并在查询中使用结果。使您的查询看起来尽可能简单,而无需在 SQL 中进行 woo-doo 转换。
【解决方案2】:

我建议是这样的:

var line = filtered
  .Where(x => (city?.Count ?? 0) <= 0 || city.Contains(x.City))
  .Where(x => (state?.Count ?? 0) <= 0 || state.Contains(x.State))
  .Where(x => (name?.Count ?? 0) <= 0 || name.Contains($"{x.FirstName} {x.MiddleName} {x.LastName}"))
  .ToList();

具有典型的模式

x => (city?.Count ?? 0) <= 0 || city.Contains(x.City)

如果city 为空或为空,我们跳过city 过滤,否则我们需要匹配。您可以根据需要添加任意数量的Where(每个列表一个用于比较)。

对于name 过滤,我保留了您当前的实现,但请注意,如果记录没有 MiddleName 组合名称将有空间:

John  Smith  

要摆脱双空格,您可以加入不为空的名称:

string.Join(" ", new string[] {
   x.FirstName, x.MiddleName, x.LastName}
  .Where(s => !string.IsNullOrEmpty(s))) 

.Where(x => (name?.Count ?? 0) <= 0 ||
   name.Contains(string.Join(" ", new string[] {
     x.FirstName, x.MiddleName, x.LastName}
    .Where(s => !string.IsNullOrEmpty(s)))
 ));

【讨论】:

  • for middlename {MiddleName ?? ""} 可以正确吗?为了避免双空格?
  • 加入名字是最好的。谢谢
【解决方案3】:

使用 and (&amp;&amp;) 代替 or (||)

var line = (from x in filtered
            where city.Contains(x.City)
         && contact_id.Contains(x.Id.ToString())
         && state.Contains(x.State)
         && name.Contains(x.FirstName + " " + x.MiddleName ?? "" + " " + x.LastName) select x).ToList();

if (line == null)
    //do what you want
else
    return line;

【讨论】:

  • 不,它不会工作,如果任何参数为空,它将返回空列表
  • 您正在寻找返回数据或传递参数的解决方案?
  • 我想用多个字符串列表过滤
  • 假设如果我尝试使用州、市进行过滤并且没有通过名称,则返回的列表将为空
  • 所以,先检查列表计数,然后再应用。喜欢(name.Count != 0 &amp;&amp; name.Contains(x.FirstName + " " + x.MiddleName ?? "" + " " + x.LastName) )
【解决方案4】:

试试这个

var line = (from x in filtered
            where (!city.Any() || city.Contains(x.City))
            && (!contact_id.Any() || contact_id.Contains(x.Id.ToString()))
            && (!state.Any() || state.Contains(x.State))
            && name.Contains(x.FirstName + " " + x.MiddleName ?? "" + " " + x.LastName) 
            select x).ToList();

return line;

【讨论】:

    猜你喜欢
    • 2015-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多