无法使用“”来形成正确的Linq查询;IN”;列表

本文关键字:Linq 查询 列表 IN | 更新日期: 2023-09-27 18:27:24

我有下面的SQL查询

;with cte as(
select a.* 
from [dbo].[AccountViewModel] a
where a.COLLECTORID = 724852
and a.MONTH = 12
and a.YEAR=2015)
select *
from cte c
where c.DispCode in ('Deceased','DND','WN','WI','NC','NORESPONSE','SKIP','SHIFTED','SFU')
OR (c.DispCode in('PTP','DIB','WCE','DP') and convert(varchar(11), c.PTPDate) >=convert(varchar(11), getdate()))
OR (MONTH(c.LastPaymentDate) = 12 and YEAR(c.LastPaymentDate)=2015)

我需要将其转换为等效的Linq查询(C#)。

Cte部分与以下程序配合良好(我已经交叉检查了记录)

private List<AccountViewModel> GetAllAcountsForLoggedInAgents()
        {
            var allAcountsForLoggedInAgents = new List<AccountViewModel>();
           allAcountsForLoggedInAgents = new ViewModelDatabase()
                    .Accounts
                    .Where(a =>
                                a.COLLECTORID == 724852 && 
                                a.MONTH == DateTime.Now.Month &&
                                a.YEAR == DateTime.Now.Year
                          )
                    .ToList();
            return allAcountsForLoggedInAgents;
        }

然而,CTE之外的零件工作不正常(意味着记录不正确)

GetAllAcountsForLoggedInAgents()
.Where
(
    a =>
      ("Deceased,DND,WN,WI,NC,NORESPONSE,SKIP,SHIFTED,SFU".Split(',').Any(x => x.Contains(a.DispCode)))
      || ("PTP,DIB,WCE,DP".Split(',').Any(b => b.Contains(a.DispCode)) && a.PTPDate >= DateTime.Now)
      || (a.LastPaymentDate.Value.Month == 12 && a.LastPaymentDate.Value.Year == 2015)
)

我相信可能是我用错了"ANY"。

无法使用“”来形成正确的Linq查询;IN”;列表

此条件与IN子句不同

("Deceased,DND,WN,WI,NC,NORESPONSE,SKIP,SHIFTED,SFU".Split(',').Any(x => x.Contains(a.DispCode)))

因为它在其中一个字符串中搜索CCD_ 2。您应该使用相等:

("Deceased,DND,WN,WI,NC,NORESPONSE,SKIP,SHIFTED,SFU".Split(',').Any(x => x == a.DispCode))

这并不理想,因为Split操作不是免费的,所以您不想将其作为查询的一部分。制作字符串的static数组:

static readonly string[] DispCodeFilter = new string[] {
    "Deceased", "DND", "WN", "WI", "NC", "NORESPONSE", "SKIP", "SHIFTED", "SFU"
};
...
(DispCodeFilter.Any(x => x == a.DispCode))

您的In条件不正确。它可以通过添加扩展方法来修复。我使用的是一个泛型方法,但如果您只需要/想要字符串,则可以使其特定于类型。我使用的是params,所以你可以一个接一个地提供项目,也可以通过拆分提供。

public static bool In<T>(this T item, params T[] items) {
        return items.Any(i=> Equals(item, i));
}
GetAllAcountsForLoggedInAgents().Where( a => a.DispCode.In
  ("Deceased","DND","WN","WI","NC","NORESPONSE","SKIP","SHIFTED","SFU")
     ||  (a.DispCode.In("PTP,DIB,WCE,DP".Split(',')) &&
          a.PTPDate >= DateTime.Now)
     || (a.LastPaymentDate.Value.Month == 12 && a.LastPaymentDate.Value.Year == 2015)

)

这与sql版本之间的一个区别,也是你可能不希望它是通用的原因,是它区分大小写:"wi"不等于"wi"。

以下是将SQL转换为Linq的两条简单规则

SQL             Linq  
============    ==========
IN (...)        Contains
EXISTS (...)    Any

其中Contains是对应的Enumerable/Queryable方法(不与string.Contains混合)。

根据这个,你的Linq标准应该是这样的

var DispCodes1 = new [] { "Deceased", "DND", "WN", "WI", "NC", "NORESPONSE", "SKIP", "SHIFTED", "SFU" };
var DispCodes2 = new [] { "PTP", "DIB", "WCE", "DP" };
GetAllAcountsForLoggedInAgents()
.Where
(
    a =>
      DispCodes1.Contains(a.DispCode)
      || (DispCodes2.Contains(a.DispCode)) && a.PTPDate >= DateTime.Now)
      || (a.LastPaymentDate.Value.Month == 12 && a.LastPaymentDate.Value.Year == 2015)
)

dasblinkenlight的答案包含一个很好的观点,所以您可以使DispCodes1DispCodes2是静态的,但这不是必需的。

另一件需要提及的事情是,您执行"CTE部分"的方式并不等同于SQL查询,其中cte只是一个命名的子查询,整个查询在数据库中执行,而在您的实现中,cte部分在数据库中运行,然后在内存中具体化,并使用Linq-to对象在内存中执行附加查询。为了使其完全等效,并让整个查询在数据库中执行,请将GetAllAcountsForLoggedInAgents结果类型更改为IQueryable<AccountViewModel>并删除ToList调用。