在Linq查询中调用方法

时间:2012-04-02 02:00:43

标签: c# linq linq-to-sql

我想在我的表中插入一个名为“S”的列,它将根据从表列中获取的值获得一些字符串值。

例如:for each ID (a.z)我想让它的字符串值存储在另一个表中。字符串值从另一个通过Linq查询获取它的方法返回。

  • 是否可以从Linq调用方法?
  • 我应该在同一个查询中执行所有操作吗?

这是我需要获得的信息的结构:

  

az是表#1中第一个方块中的ID,从这个ID我得到表#2中的另一个id,从中我可以得到我需要在'S'列下显示的字符串值。 />   enter image description here

var q = (from a in v.A join b in v.B
    on a.i equals b.j
    where a.k == "aaa" && a.h == 0
    select new {T = a.i, S = someMethod(a.z).ToString()})
    return q;

导致以下错误的行S = someMethod(a.z).ToString()

  

无法转换'System.Data.Linq.SqlClient.SqlColumn'类型的对象   输入'System.Data.Linq.SqlClient.SqlMethodCall'。

3 个答案:

答案 0 :(得分:52)

您必须在Linq-to-Objects上下文中执行方法调用,因为在数据库端方法调用没有意义 - 您可以使用AsEnumerable()执行此操作 - 基本上其余的查询将会使用Linq-to-Objects评估为内存集合,您可以按预期使用方法调用:

var q = (from a in v.A join b in v.B
        on a.i equals b.j
        where a.k == "aaa" && a.h == 0
        select new {T = a.i, Z = a.z })
        .AsEnumerable()
        .Select(x => new { T = x.T, S = someMethod(x.Z).ToString() })

答案 1 :(得分:10)

您需要将其拆分为两个语句。返回查询中的结果(这将是命中数据库的结果),然后在单独的步骤中第二次枚举结果,以将转换转换为新的对象列表。第二个“查询”不会访问数据库,因此您可以使用其中的someMethod()

Linq-to-Entities有点奇怪,因为它使从C#查询数据库的过渡非常无缝:但你总是要提醒自己,“这个C#将被翻译成一些SQL。 “结果,你必须问自己,“所有这些C#实际上都可以作为SQL执行吗?”如果它不能 - 如果你在其中调用someMethod() - 你的查询将遇到问题。通常的解决方案是拆分它。

(来自@BrokenGlass的另一个答案,使用.AsEnumerable(),基本上是另一种方法。)

答案 2 :(得分:0)

这是一个老问题,但是我没有提到一个" hack",它允许在select期间调用方法而不重复。想法是使用构造函数,在构造函数中你可以调用你想要的任何东西(至少它在LINQ中使用NHibernate工作正常,不确定LINQ2SQL或EF,但我想它应该是相同的)。 下面我有基准程序的源代码,看起来在我的情况下重复方法比构造函数方法慢两倍,我想也就是这样 - 我的业务逻辑很小,所以迭代和内存分配等问题很重要。

我还希望有更好的方式来说,不应该尝试在数据库上执行这个或那个,

// Here are the results of selecting sum of 1 million ints on my machine:
// Name    Iterations      Percent    
// reiterate       294     53.3575317604356%      
// constructor     551     100%

public class A
{
    public A()
    {            
    }

    public A(int b, int c)
    {
        Result = Sum(b, c);
    }

    public int Result { get; set; }

    public static int Sum(int source1, int source2)
    {
        return source1 + source2;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var range = Enumerable.Range(1, 1000000).ToList();

        BenchmarkIt.Benchmark.This("reiterate", () =>
            {
                var tst = range
                    .Select(x => new { b = x, c = x })
                    .AsEnumerable()
                    .Select(x => new A
                    {
                        Result = A.Sum(x.b, x.c)
                    })
                    .ToList();
            })
            .Against.This("constructor", () =>
            {
                var tst = range
                    .Select(x => new A(x, x))
                    .ToList();
            })
            .For(60)
            .Seconds()
            .PrintComparison();

        Console.ReadKey();
    }
}