在 Linq 查询中的字符串扩展方法

5
如何在linq查询中使用字符串扩展方法:
public NewsType GetNewsType(string name)
{
   var newsType = db.NewsTypes.FirstOrDefault(x => x.Name.ToFriendlyUrl() ==  
                                              name.ToFriendlyUrl());
   return newsType;
}

目前不允许使用上述查询x.Name.ToFriendlyUrl()。有人知道如何实现吗?


你得到了什么错误? - SWeko
你能发布你的扩展方法吗?上面的代码看起来很好。 - Mathew Thompson
4个回答

3
扩展方法确实可以用于LINQ查询中,而且LINQ方法本身就是以扩展方法的形式实现的。
然而,在LINQ-to-SQL或LINQ-to-Entities查询中使用扩展方法(或大多数其他方法)是另一个问题。这些查询实际上并不在C#代码中运行,而是被视为表达式转换为SQL。也就是说:
db.News.Where(x => x.Published).Select(x => x.Name)

被翻译为SQL语句

Select Name 
From News
Where Published = 1

并且它的结果会被返回到C#代码。

由于没有办法将ToFriendlyUrl()方法传输到SQL,因此您的代码会抛出错误。

您基本上有两种解决方案/变通方法。其一是将调用转换为可以翻译成SQL的形式,例如如果ToFriendlyUrl()方法只是:

public static string ToFriendlyURL(this string value)
{
    return value.ToLower();
}

你可以在 LINQ 调用中嵌入该代码,这样就可以工作了。但是,如果该方法更加复杂,则您唯一的解决方案是从基础数据源获取数据,然后在 C# 侧进行处理:

var newsTypeQuery = db.NewsTypes.Where(x => // other conditions, if any);
var newsTypes = newsTypes.ToList(); //forces execution of the query
                                    // the result is now a C# list
var newsType = newsTypes.FirstOrDefault(x => 
                                 x.Name.ToFriendlyUrl() == name.ToFriendlyUrl());

1
这个。
var newsType = db.NewsTypes.FirstOrDefault(
          x => x.Name.ToFriendlyUrl() == name.ToFriendlyUrl());

无法在Entity Framework中完成。 ToFriendlyUrl是一个扩展方法。它是在“客户端”计算机上的东西。查询将在SQL服务器上执行。 SQL服务器没有ToFriendlyUrl函数。
“标准”解决方案是在名为FriendlyName的第二列中保存ToFriendlyUrl()的预计算版本,因此您的查询变为:
var friendlyName = name.ToFriendlyUrl();
var newsType = db.NewsTypes.FirstOrDefault(
          x => x.FriendlyName == friendlyName);

如果这是唯一的解决方案,那么我会继续使用它。 - Tun
@coder "是保存在名为"...第二列中。这样做的好处是可以在该列上创建索引。缺点是每次修改“Name”列时都必须修改“FriendlyName”列。还有其他解决方案,例如将“ToFriendlyName()”制作为SQL函数的等效版本,或者在SQL Server中使用C#。但它们都相当复杂(我甚至不确定您是否能够在TSQL中制作出良好的“ToFriendlyName”函数。TSQL在字符串操作方面并不是很强大)。 - xanatos

1
假设NewsTypes是一个IQueryable,这是由于Entity Framework无法将您的扩展方法转换为SQL(它应该怎么做?)所导致的结果。除非您可以将谓词重写为Entity Framework可以将其转换为SQL的内容,否则您将不得不在客户端执行查询。
public NewsType GetNewsType(string name)
{
   var newsType = db.NewsTypes.AsEnumerable().FirstOrDefault(x => x.Name.ToFriendlyUrl() == name.ToFriendlyUrl());
   return newsType;
}

注意在FirstOrDefault之前添加了AsEnumerable()。不幸的是,这可能会将从服务器返回的NewsTypes的所有行都拉到客户端,因此可能会非常昂贵。

这是一个昂贵的调用吗? - Tun
@coder:AsEnumerable 不会很耗费资源,但是如果你调用 FirstOrDefault 方法,它会在服务器端进行一次“廉价”的查询以找到所需的行并返回它。相反,你会得到一个 FirstOrDefault 方法,它会从服务器中拉取行,直到找到所需的行为止。如果只有少量行,你可能甚至不会注意到差异,但如果有许多行,你可能会突然看到明显的时间差异。这取决于你的数据和环境。 - Martin Liversage

0

不要这样尝试

public NewsType GetNewsType(string name)
{
   var newsType = db.NewsTypes.FirstOrDefault(x => x.Name == name).ToFriendlyUrl();
   return newsType;
}

但在你的情况下,**.ToFriendlyUrl()** 有什么用途? - Jagadesh
基本上,名称来自已经友好化的URL。 - Tun
那么你就不需要使用那个了。 - Jagadesh
我仍然需要使用这个x.Name.ToFriendlyUrl()。例如,参数“name”是“local-politics”。但是x.Name是“Local Politics”。所以,我必须将x.Name转换为友好的URL名称。否则,它们永远不会匹配。 - Tun

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接