【问题标题】:SQL LIKE in LinqLinq 中的 SQL LIKE
【发布时间】:2013-06-09 11:37:19
【问题描述】:

在添加这个问题之前,我确实在 stackoverflow 上搜索过类似的问题,但我找不到。互联网上的大多数问题都使用带有字符串的 LIKE(例如 LIKE '%ABC%'),但我需要与不同表的现有列进行比较。

我需要为 select 语句编写一个 linq 查询,如下所示 -

 select * 
from [dbo].[BaseClaim]
where WPId like (select WPId from UserProfiles where ID='1459') 

我想出了下面的 linq 查询,但它没有按预期工作 -

     var result = (from claimsRow in context.BaseClaims
                          where (from upRow in context.UserProfiles
                                 where upRow.ID == 1459
                                 select upRow.WPId).Contains(claimsRow.WPId)
                          select claimsRow);

而上面linq生成的sql如下-

   SELECT 
   [Extent1].[WPId] AS [WPId]
   FROM [dbo].[BaseClaim] AS [Extent1]
   WHERE  EXISTS (SELECT 
             1 AS [C1]
                 FROM (SELECT 
                       [UserProfiles].[ID] AS [ID], 
                       [UserProfiles].[WPId] AS [WPId]      
                       FROM [dbo].[UserProfiles] AS [UserProfiles]) AS [Extent2]
                   WHERE (1459 = [Extent2].[ID]) AND ([Extent2].[WPId] = [Extent1].[WPId]))

很明显,我的 linq 无法正常工作,因为它将 baseclaim.wpID 与 userprofiles.wpid 而不是 LIKE 进行比较。

【问题讨论】:

  • 你为什么使用likeWPId 中是否有 % 字符?
  • @Sai 看看下面我的回答。我希望它击中它。
  • @GertArnold 是的,WPId 中有 % 字符
  • 好的,我认为下面的一些答案可能对你有用。
  • @Sai 看看我编辑的答案。

标签: c# .net linq tsql sql-to-linq-conversion


【解决方案1】:

没有直接的等价物,但有些方法的工作方式类似,具体取决于模式。

  • string.Contains("pattern") 等价于 LIKE '%pattern%'
  • string.StartsWith("pattern") 等价于 LIKE 'pattern%'
  • string.EndsWith("pattern") 等价于 LIKE '%pattern'

但是,在您的 SQL 查询中,该模式是动态的,因此我认为没有将其直接转换为 Linq 的好方法。如果您在设计时知道该模式适合其中一种情况,则可以使用:

var result =
    from claimsRow in context.BaseClaims
    let wpId = context.UserProfiles.Single(upRow => upRow.ID == 1459).WPId
    where claimsRow.WPId.Contains(wpId) // or StartsWith or EndsWith
    select claimsRow;

或者可能

var wpId =
    (from upRow in context.UserProfiles
     where upRow.ID == 1459
     select upRow.WPId)
    .Single();
var result =
    from claimsRow in context.BaseClaims
    where claimsRow.WPId.Contains(wpId) // or StartsWith or EndsWith
    select claimsRow;

【讨论】:

  • Linq to SQL 说Only arguments that can be evaluated on the client are supported for the String.Contains method. :)
  • @lazyberezovsky 所以我想这意味着你必须先查询模式?
  • @p.s.w.g,感谢您的回复。您的查询仅从用户配置文件中返回一个 wpid,我需要与所有 wpid 进行比较。
  • @Sai 那么恐怕我不明白你原来的 SQL 打算做什么。你有select WPId from UserProfiles where ID='1459',它只选择一条记录,这就是我试图复制的。
  • 抱歉,我错误地认为这是另一个查询。但是我有例外说-方法“Single”和“SingleOrDefault”只能用作最终查询操作。请考虑在此实例中使用“FirstOrDefault”方法。当我将其更改为 firstordefault 时,它起作用了,但仍然没有结果。我正在查看生成的 sql 查询以找出原因。
【解决方案2】:

你必须使用包含 例如

 .Where(a => a.Name.Contains("someStrig")

当参数为常量时包含生成Like

希望对你有帮助

【讨论】:

  • OP 确实使用了Contains!这没什么好说的。
  • 你是什么意思?我们可以这样写 var query = (from name in Names where name.ToUpper().Contains(someString) select name);
  • 我说OP使用.Contains(claimsRow.WPId)。你没有指出错误,只是给出了一些一般性的评论。
  • @Microtechnie,感谢您的回复。我需要求助于其他方法而不是包含。
【解决方案3】:

此查询适用于实体框架

from claimsRow in context.BaseClaims
let wpId = context.UserProfiles.Where(upRow => upRow.ID == 1459) 
                               .Select(upRow => upRow.WPId)
                               .FirstOrDefault() 
where wpId.Contains(claimsRow.WPId)
select claimsRow

但不是LIKE,而是生成CHARINDEX操作

SELECT * FROM  [dbo].[BaseClaims] AS [Extent1]
LEFT OUTER JOIN  (SELECT TOP (1) [Extent2].[WPId] AS [WPId]
                  FROM [dbo].[UserProfiles] AS [Extent2]
                  WHERE [Extent2].[ID] = 1459 ) AS [Limit1] ON 1 = 1
WHERE (CHARINDEX([Extent1].[WPId], [Limit1].[WPId])) > 0

注意:使用 Linq to SQL 会抛出 NotSupportedException:

仅支持可以在客户端上评估的参数 String.Contains 方法。

【讨论】:

【解决方案4】:

已编辑:

提问者说他​​的 UserProfile.WPId 中有通配符 (%),应该像 SQL 中的通配符一样工作。

你可以用这个:

BaseClaims.Where(b => UserProfiles.Where(u => u.ID == "1459").Any(u => u.WPId.Contains(b.WPId))).ToList();

在这个例子中,我试图模仿你的实体/表格。

    static void Main(string[] args)
    {
        List<BaseClaim> BaseClaims = new List<BaseClaim>()
        {
            new BaseClaim(){ WPId = "11123411" }, //match 1
            new BaseClaim(){ WPId = "11123123" }, //match 2
            new BaseClaim(){ WPId = "44423411" }, //match 3
            new BaseClaim(){ WPId = "444AAAA" }, //match 3
            new BaseClaim(){ WPId = "444BBBB" }, //match 3
            new BaseClaim(){ WPId = "444QWQEQW" }, //match 3
            new BaseClaim(){ WPId = "2314" },
            new BaseClaim(){ WPId = "3214" }
        };
        List<UserProfile> UserProfiles = new List<UserProfile>()
        { 
            new UserProfile(){ WPId="%112341%", ID="1459" }, //match 1
            new UserProfile(){ WPId="%123", ID="1459" }, //match 2
            new UserProfile(){ WPId="444%", ID="1459" }, //match 3
            new UserProfile(){ WPId="5555", ID="1459" },
            new UserProfile(){ WPId="2222", ID="1459" },
            new UserProfile(){ WPId="1111", ID="4444" },
        };

        char[] asterisk = { '%' };
        List<BaseClaim> result = BaseClaims.Where(b => UserProfiles.Where(u => u.ID == "1459").Any(u => u.WPId.StartsWith("%") && u.WPId.EndsWith("%") ? b.WPId.Contains(u.WPId.Trim(asterisk)) :
                                                                                                        u.WPId.StartsWith("%") ? b.WPId.EndsWith(u.WPId.Trim(asterisk)) : 
                                                                                                        u.WPId.EndsWith("%") ? b.WPId.StartsWith(u.WPId.Trim(asterisk)) :
                                                                                                         false)).ToList();
        //this will result to getting the first 3 BaseClaims

    }

【讨论】:

  • 感谢您的回复。我在 userProfiles 中的 WPID 包含格式为“%12”的值,因此当我尝试运行您的查询时,它不返回任何值。
  • 我现在得到一个异常 - “只有在没有指定修剪字符作为参数时,LINQ to Entities 才支持方法 'System.String Trim(Char[])'。”我意识到的另一件事是,我无法从列中删除 wildchar(%),因为有时 wildchar 可能位于中间。
  • 我最终使用 context.Database.SqlQuery 来完成我的工作。感谢您的时间和帮助。
猜你喜欢
  • 1970-01-01
  • 2022-09-29
  • 2021-12-19
  • 1970-01-01
  • 1970-01-01
  • 2011-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多