【问题标题】:Method Name(int, DateTime) has no supported translation to SQL方法名称(int, DateTime) 没有支持的 SQL 转换
【发布时间】:2011-04-23 04:45:11
【问题描述】:

我有一个函数,Name(int, DateTime),我想在 linq2sql 查询中使用它。

它是一个简单的函数,使用TimeSpan 计算传入的 DateTime 对象和 DateTime.Now 之间的差异。然后我用 .TotalHours 和传入的 int 做一些基本的数学运算。最复杂的函数是 Math.Pow(double,double)。

public static double Name(int num, DateTime Timestamp)
{
        DateTime now = DateTime.Now;
        DateTime then = Timestamp;

        TimeSpan elapsedSpan = new TimeSpan(now.Ticks - posted.Ticks);
        double t = elapsedSpan.TotalHours;

        return System.Math.Pow(t, num);
}

我收到此错误:

Method 'Double Name(Int32, System.DateTime)' has no supported translation to SQL. 

运行这样的查询时:

var q = from k in db.Stuff
        select new {Name=Name(k.num, k.Timestamp)};

我了解该函数无法转换为当前编写的 SQL。有没有办法将我拥有的东西转换成 SQL 可用的东西?

我应该注意 ToList() 或 ToArray() 方法不可用,因为数据表很大。

【问题讨论】:

  • 不是答案,但如果您在数据库中使用 Datetime 值,您确实应该使用 UTC 时间,以避免任何与夏令时更改相关的问题,即时间可以向后或向前跳跃。
  • 如果重要的话,我在数据库中使用 DateTime2(7)。如何在我的数据库中使用 UTC 时间?
  • 有关 SqlServer 2008 中 DateTimeOffset 的有趣信息:blogs.msdn.com/b/bartd/archive/2009/03/31/…
  • 只要您想要“现在”,无论是需要存储的值还是需要进行的比较,都只需在代码中使用 DateTime.UtcNow。

标签: c# linq


【解决方案1】:

由于您的转换仅在投影时使用,我建议您只需在 .NET 中而不是在 SQL 中执行该操作:

var q = db.Stuff.Select(k => new { k.num, k.Timestamp })
                .AsEnumerable() // Do further processing in .NET
                .Select(k => Name(k.num, k.Timestamp));

请注意,我也从匿名类型中取出了结果,因此 q 的类型将只是 IEnumerable<double>

如果您真的需要在数据库中完成处理,我建议您自己在 SQL 中编写一个用户定义的函数,然后告诉 LINQ to SQL(或将其作为视图的一部分)您从中选择)。

【讨论】:

  • 感谢您的回复。这是我打算做的,但我担心 AsEnumerable 的性能。什么时候,即k中的记录数,不应该使用AsEnumerable()。
  • @Shawn:嗯,传输的数据量不会有太大变化——你仍然要获取每条记录的“一些东西”,所以没关系太 很多地方已经完成了。如果您一直在执行 Sum 或 Where 子句之类的操作,那显然应该在数据库中完成,因为它可以比在客户端获取所有数据更有效……但在这种情况下,我认为不是可能会有很大的不同。
  • 感谢您的信息!我实施了这个解决方案。很简单,但我了解到 AsEnumerable 的性能并不糟糕,出于某种原因,我之前怀疑过。
  • @Jon AsEnumerable 在 l2s 中使用时会导致这些值与数据上下文一起加载,这使得对 Attach 的任何调用都会引发异常,即已经添加了具有相同键的项目。我们如何解决仍然使用 AsEnumerable 的问题
【解决方案2】:

LINQ-to-SQL 不能转换任意代码方法,因为它们是 IL(它需要“表达式树”,这是 lambda 和 LINQ 查询语法生成的内容)。如果您的 .NET 代码中只需要此逻辑,请在调用之前切换到 LINQ-to-Objects,即

var qry = some complex LINQ query

var data = from row in qry.AsEnumerable()
            select new {
               row.Id,
               Name = YourType.Name(row.Foo, row.Bar)
            };

AsEnumerable() 将 LINQ 组合分成两部分,允许您的托管代码正常运行。

如果您需要在数据库中执行类似的操作,则将代码作为 UDF 编写在 TSQL 中,然后通过将 UDF 拖到设计器上将其映射到 LINQ-to-SQL。然后,您可以将其作为数据上下文中的新方法访问,即

 select db.Name(row.Foo, row.Bar)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多