【问题标题】:Adding LINQ Where && condition in a foreach loop在 foreach 循环中添加 LINQ Where && 条件
【发布时间】:2021-08-13 00:11:01
【问题描述】:

为了简化我的问题,假设我有以下数据库模型:

// database model, the list of not supported postcodes
var postcodeExclusionArray = new List<PostcodeExclusion>() {
    new PostcodeExclusion(){
        CustomerId = 2,
        Postocde = "A11"
    },
    new PostcodeExclusion(){
        CustomerId = 2,
        Postocde = "A12"
    },
    new PostcodeExclusion(){
        CustomerId = 2,
        Postocde = "A12 4FR"
    },
    new PostcodeExclusion(){
        CustomerId = 2,
        Postocde = "AR"
    },
    new PostcodeExclusion(){
        CustomerId = 2,
        Postocde = "B11"
    },
    new PostcodeExclusion(){
        CustomerId = 2,
        Postocde = "B12"
    },
        new PostcodeExclusion(){
        CustomerId = 2,
        Postocde = "C11"
    },
    new PostcodeExclusion(){
        CustomerId = 2,
        Postocde = "C12"
    },
    // many more records...
};

它描述了不受支持的英国邮政编码。它只是一个数据源的概念,因为我们通常使用 EF Core 3.1。请记住,每条记录的格式在收集整个区域时可能会有所不同,例如EH 或仅特定属性,例如AB25HZ.

现在我想检查我的系统是否支持提供的英国邮政编码。 我的想法是只使用Where 语句在查询中执行此操作,但我不确定这是否可能。

该算法的想法是通过以下方式获取提供的邮政编码的组合

providedpostCode = "A41666";

组合:

A
A4
A41
A416
A4166
A41666

然后构建db查询如

bool isSupported = postcodeExclusionArray.Where(x => x.CustomerId == 2)
                      .Where(x=> x.Postocde == "A" ||
                      x.Postocde == "A" ||
                      x.Postocde == "A4" ||
                      x.Postocde == "A41" ||
                      x.Postocde == "A416" ||
                      x.Postocde == "A41666").First() == null;
                  

有没有办法使用 LINQ(不是原始 SQL)来实现这一点,如果没有,最佳解决方案是什么?

我尝试使用foreach 循环,但没有找到任何解决方案。 如果不可能,那么最简单的解决方案可能是生成原始 SQL 查询, 但是,这种方法的缺点是无法进行单元测试,并且只能通过集成测试进行测试。 另一种选择是仅通过 customerId 从数据库中查询所有邮政编码并进行简单的迭代。 有任何想法吗? 干杯

【问题讨论】:

  • 在内存中构建所有组合的List&lt;string&gt;,然后执行.Where(x =&gt; x.CustomerId == 2 &amp;&amp; myList.Contains(x.Postocde).FirstOrDefault()
  • 排除数组是否列出了在开始预期邮政编码或处于预期邮政编码的任何位置时不可接受的字符串?
  • @Loring 第一个选项这样:排除数组列出了在开始预期邮政编码时不可接受的字符串

标签: c# linq .net-core ef-core-3.1


【解决方案1】:

集合支持Enumerable.Contains

string providedpostCode = "A41666";
string[] providedPostCodes = Enumerable.Range(0, providedpostCode.Length)
    .Select(ix => providedpostCode.Substring(0, ix + 1))
    .ToArray();

bool isSupported = !postcodeExclusionArray
    .Any(x => x.CustomerId == 2 && providedPostCodes.Contains(x.Postocde));

不确定逻辑,如果您需要Any!Any

【讨论】:

  • 用providedpostCode.StartsWith(x.Postocde)代替providedPostCodes.Contains(x.Postocde)岂不是更方便?这样提供的邮政编码可以被删除。此外,isSupported 的逻辑似乎被翻转了——变量不应该是 notSupported 还是 !放在右边?
  • @Loring:关于你的最后一个问题,我在回答中提到我不清楚 OP 的支持是什么意思,但如有必要,很容易添加 !StartsWith 似乎也是一个好主意,不确定 Linq-To-Entities 是否支持它。但是,请注意,它的效率将低于包含,尤其是在 Postocde 上有适当的索引时。
  • @Tim Schmelter 你确定它会被 EF 完全翻译成 SQL 查询还是会在服务器端调用?
  • @GoldenAge:当然可以。如果 LINQ 无法将其转换为 SQL,您将得到一个异常,而不是在内存中执行的查询。如果您在查询中的某处添加AsEnumerable,您将在内存中执行它。但上面的查询是supportedContains 被翻译成IN(...)
  • 如果邮政编码不支持“PA41”(它描述了整个不支持的邮政编码区域)并且您想检查“A41OP5”然后您的代码(将!添加到您的右侧@Loring 建议的代码)将返回不正确的 true
【解决方案2】:

看起来你只需要用第一个字母做StartsWith

var postCode = providedpostCode[0];
var isSupported = postcodeExclusionArray
    .Where(x => x.CustomerId == 2)
    .Where(x => x.Postocde.StartsWith(postCode));

无论如何,我认为这只是对问题的错误解释。

不想重复很多,只是重用扩展可能会回答:https://stackoverflow.com/a/67666993/10646316

并编写如下代码(重复使用之前的答案代码):

var providedpostCode = "A41666";
var providedPostCodes = Enumerable.Range(0, providedpostCode.Length - 1)
    .Select(ix => providedpostCode.Substring(0, ix + 1))
    .ToArray();

var isSupported = postcodeExclusionArray
    .Where(x => x.CustomerId == 2)
    .FilterByItems(providedPostCodes, (x, c) => x.Postocde == c, true);

【讨论】:

    猜你喜欢
    • 2011-11-09
    • 2017-05-22
    • 2019-07-18
    • 2021-04-09
    • 1970-01-01
    • 2018-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多