在linq select表达式中拆分字符串

时间:2017-10-31 16:53:09

标签: c# linq

geoStartLoc以这种格式“54.5,44.5”保存字符串。我试图将结果拆分并存储在纬度和经度中。我收到以下错误 LINQ to Entities无法识别方法'System.String [] Split(Char [])'方法,而且此方法无法转换为商店表达式。 (选择陈述)

var data = from UberTrip in db.UberTrips
           group UberTrip by new { UberTrip.startLoc, UberTrip.geoStartLoc } 
           into startLocGroup
           select new LocationGroup() {
               startLocation = startLocGroup.Key.startLoc,
               latitude = startLocGroup.Key.geoStartLoc.Split(',').Count().ToString(),
               //longitude= startLocGroup.Key.
               countTrips = startLocGroup.Count()
           };

3 个答案:

答案 0 :(得分:1)

这是数据库中的架构问题。在列中存储分隔值是很糟糕的。您应该有两列:一列用于geoLatitude,另一列用于geoLongitude

但是,由于您可能无法自行进行更改,因此您需要确保将数据下拉到程序代码中,然后将其拆分。现在,linq正在尝试使用此代码创建的表达式树并将其转换为SQL查询,并且它无法为Split()调用执行此操作,因为并非所有受支持的数据库目标都具有类似的Split方法。在将数据加载到程序中的内存之后,需要保存该部分。

要完成此操作,只需检索完整的geoStartLoc字符串(如果必须,请转换为匿名类型),使用.ToList()强制查询编译并检索所有数据,然后使用{ {1}}转换为.Select()个对象。

答案 1 :(得分:0)

每当收到此错误消息时,您都在执行查询AsQueryable而不是AsEnumerable

主要区别在于AsQueryable通常会在数据库等其他流程中执行,而AsEnumerable将在您的流程中在内存中执行。

IEnumerable对象知道如何创建Enumerator。一个枚举器,知道如何做两件事:"给我序列的第一个元素","给我序列的下一个元素(如果没有下一个元素,则为null)。

如果您的查询是AsEnumerable,它知道可以使用您的流程中的所有类和数据来创建枚举器。因此它可以调用String.Split等函数。

AsQueryable不包含创建枚举数的所有数据,它包含表达式和Provider。提供者知道需要在哪里执行查询(通常是数据库,但它也可以是json字符串或Web服务),并且它知道如何将Expression转换为执行者理解的格式。在您的情况下,Provider知道如何将Expression转换为适合您的数据库的SQL语句。

当然,SQL不知道你自己定义的函数。虽然有很多.NET函数可以转换为SQL,但并不是每个函数都可以。 String.Split就是其中之一。

请参阅:Supported and Unsupported LINQ Methods (LINQ to Entities)

要解决您的问题,您可以将查询的数据带到本地内存。这是使用扩展函数Enumerable.AsEnumerable()完成的。之后,您可以将您的序列用作IEnumerable。

AsEnumerable的缺点是它将查询的数据带到本地内存。如果您删除大量此类数据以创建最终结果,这将是一种浪费。因此,请确保在没有太多数据的情况下使用AsEnumerable。

幸运的是,你需要在你的最终选择中使用Split,所以你需要输入所有最终选择的数据,除非你会做Skip / Take / FirstOrDefault /等等。在这种情况下,最好限制你的选择在AsEnumerable()之前执行Skip / Take等。

我对MethodSyntax比较熟悉。 您的查询分为较小的步骤(如果需要,可以创建一个LINQ):

// still AsQueryable:
var startLocGroups = db.UberTrips
    .GroupBy(uberTrip => new {uberTip.startLoc, uberTrip.geoStartLoc)

// make asEnumerable
var localStartLocGroups = startLocGroups.AsEnumerable();

// now you can do your Select
var result = localStartLocGroups
    .Select(group => new LocationGroup()
    {
        startLocation = startLocGroup.Key.startLoc,
        latitude = group.Key.geoStartLoc.Split(',')
            .Count()
            .ToString(),
         //longitude= sgroup.Key.
         countTrips = group.Count(),
       });

答案 2 :(得分:-1)

由于底层Linq to Entities不支持,因此在获得数据后但返回结果之前,您必须执行此操作。

这样您可以在不调用字符串拆分功能的情况下进行查询以获取数据,然后将结果修改为正确的格式。

var data = from UberTrip in db.UberTrips
               group UberTrip by new { UberTrip.startLoc, UberTrip.geoStartLoc } into startLocGroup

               select new {
                   startLocation = startLocGroup.Key.startLoc,
                   geoStartLoc = startLocGroup.Key.geoStartLoc,
                   countTrips = startLocGroup.Count()
               };

               return data.Select(trip => new LocationGroup() {
                   startLocation = trip.startLocation,
                   latitude = trip.geoStartLoc.Split(',')[0],
                   longitude= trip.geoStartLoc.Split(',')[1],
                   countTrips = trip.countTrips
               }).ToList();