使用LINQ to Entities一不小心就会碰上类似的错误:
LINQ to Entities 不识别方法“XX”,因此该方法无法转换为存储表达式,这是因为LINQ语句最后都是要转为sql语句来执行的,当它转换后,发现sql语句中要执行的方法“XX”,并不是一个在数据库中的存储过程函数,也无法进行转换为存储表达式。
主要分为三种情况,下面用各自的解决方式区别应对:
一.作为判断的局部变量在linq语句中调用了函数:
string date = "2018-10-01 00:00:00";
var query = from item in context.Goods
where item.SaleTime == DateTime.Parse(date)
select item;
这种处理方式最为简单,也就是保证linq语句中尽量不用系统语言的函数,所以上面直接将date设置为DateTime就可以了,保证都是数据库的时间类型。
DateTime date = DateTime.Parse("2018-10-01 00:00:00");
var query = from item in context.Goods
where item.SaleTime == date
select item;
二.要得到数据处理结果而不得不调用方法:
var query = from item in context.Goods
group item by item.Created.ToString() into g
select new { SaleTime = g.Key.ToString(), Count = g.Count() };
这里排序和查询结果中都用到了ToString方法,外部需要的SaleTime是一个字符串类型而不是时间类型。多说一句,有的可能会说这里直接用时间类型,等到外部需要时再ToString()转为字符串。实际是考虑到字符串可为空,那样的话使用ToString时就会出错。
这个时候解决思路始终一条就是,先在内存中使用LINQ to Objects,然后对数据集进行第二次处理。当数据集在内存中时,调用系统的函数处理数据就很合理了。只是数据量大的时候,对内存和处理速度是一个考验。
var query = from item in context.Goods.ToList()
group item by item.Created.ToString() into g
select new { SaleTime = g.Key.ToString(), Count = g.Count() };
三.转变为使用数据库能支持的调用方法:
var query = from item in context.Goods.ToList()
select new { SaleTime = g.Key.ToString(), Count = g.Count() };
这里,跟上面的实例代码类似,我们是在处理时间字段时,有些数据库是有自己的时间处理函数的,C#在系统层通过使用SqlFunctions,是可以做一个桥接转换的,实际还是调用的数据库内置函数。所以解决方式如下:
var query = from item in context.Goods.ToList()
select new { SaleTime = SqlFunctions.DateName("year", g.Key) + "-"
+ SqlFunctions.DateName("month", g.Key) +"-"
+ SqlFunctions.DateName("day",g.Key), Count = g.Count() };
总结,三种情况,遇到最多且使用最多的应该是第二种情况,第一种基本可忽略,第三种不具有通用性。所以,最好的处理方式就是Linq-to-entities 转换到Linq-to-objects,然后在内存中调用函数进行二次处理。