2013-05-26 80 views
1

我在我的代码中使用LINQ查询,需要多次写入条件的小改动。我的查询是减少此LINQ查询的代码行

var sdata = from r in dt.AsEnumerable() 
where r.Field<DateTime>("DAT_START").TimeOfDay.Hours < 20 && 
      r.Field<DateTime>("DAT_START").TimeOfDay.Hours >= 4 
    group r by r["TXT_TARGET_CELL_ID"] into g 
    select new 
    {   

     CellID = g.Key, 
     TotalCommCount = g.Count(), 
     TotalDuration = g.Sum(r => r.Field<int>("LNG_DURATION")), 
     InSMSCount = g.Count(r => r.Field<Int16>("INT_DIRECTION") == 1 && 
            r.Field<Int16>("INT_CALL_DATA_TYPE") == 5), 
     OutSMSCount = g.Count(r => r.Field<Int16>("INT_DIRECTION") == 2 && 
            r.Field<Int16>("INT_CALL_DATA_TYPE") == 5), 
     InVoiceCount = g.Count(r => r.Field<Int16>("INT_DIRECTION") == 1 && 
            r.Field<Int16>("INT_CALL_DATA_TYPE") == 1), 
     OutVoiceCount = g.Count(r => r.Field<Int16>("INT_DIRECTION") == 2 && 
            r.Field<Int16>("INT_CALL_DATA_TYPE") == 1), 
     InVoiceDuration = g.Where(r => r.Field<Int16>("INT_DIRECTION") == 1 && 
            r.Field<Int16>("INT_CALL_DATA_TYPE") == 1) 
          .Sum(r => r.Field<int>("lNG_DURATION")), 
     OutVoiceDuration = g.Where(r => r.Field<Int16>("INT_DIRECTION") == 2 && 
             r.Field<Int16>("INT_CALL_DATA_TYPE") == 1) 
          .Sum(r => r.Field<int>("LNG_DURATION")), 
     Latitude = g.Any(s => s.Field<string> 
         ("TXT_TARGET_BTS_LOCATION_ADDRESS") != null && s.Field<string> 
         ("TXT_TARGET_BTS_LOCATION_ADDRESS").Trim() != "") ? g.First(s => s.Field<string> 
         ("TXT_TARGET_BTS_LOCATION_ADDRESS") != null && s.Field<string> 
         ("TXT_TARGET_BTS_LOCATION_ADDRESS").Trim() != "").Field<string>("TXT_LATITUDE") : "", 
     Longitude = g.Any(s => s.Field<string> 
         ("TXT_TARGET_BTS_LOCATION_ADDRESS") != null && s.Field<string> 
         ("TXT_TARGET_BTS_LOCATION_ADDRESS").Trim() != "") ? g.First(s => s.Field<string> 
         ("TXT_TARGET_BTS_LOCATION_ADDRESS") != null && s.Field<string> 
         ("TXT_TARGET_BTS_LOCATION_ADDRESS").Trim() != "").Field<string>("TXT_LONGITUDE") : "", 
     BTS_Address = g.Any(s => s.Field<string> 
         ("TXT_TARGET_BTS_LOCATION_ADDRESS") != null && s.Field<string> 
         ("TXT_TARGET_BTS_LOCATION_ADDRESS").Trim() != "") ? g.First(s => s.Field<string> 
         ("TXT_TARGET_BTS_LOCATION_ADDRESS") != null && s.Field<string> 
         ("TXT_TARGET_BTS_LOCATION_ADDRESS").Trim() != "").Field<string>("TXT_TARGET_BTS_LOCATION_ADDRESS") : "", 
     Azimuth = g.Any(s => s.Field<string> 
         ("TXT_TARGET_BTS_LOCATION_ADDRESS") != null && s.Field<string> 
         ("TXT_TARGET_BTS_LOCATION_ADDRESS").Trim() != "") ? g.First(s => s.Field<string> 
         ("TXT_TARGET_BTS_LOCATION_ADDRESS") != null && s.Field<string> 
         ("TXT_TARGET_BTS_LOCATION_ADDRESS").Trim() != "").Field<string>("TXT_AZIMUTH_DEG") : "" 

    } into summary 
orderby summary.TotalCommCount descending 
select summary; 

在这里我需要改变哪里条件只有每一次,其余部分保持不变,即选择新的部分。我可以在代码中编写一次这个查询,并在哪里调整条件?

回答

1

4和20之间就做出这样的新功能的条件:

public dynamic MyLinq(IEnumerable r, Predicate<Object> whereClause) 
{ 
    return from r 
    where whereClause(r) 
     group r by r["TXT_TARGET_CELL_ID"] into g 
     select new 
     {   

      CellID = g.Key, 
      TotalCommCount = g.Count(), 
      TotalDuration = g.Sum(r => r.Field<int>("LNG_DURATION")), 
      InSMSCount = g.Count(r => r.Field<Int16>("INT_DIRECTION") == 1 && 
             r.Field<Int16>("INT_CALL_DATA_TYPE") == 5), 
      OutSMSCount = g.Count(r => r.Field<Int16>("INT_DIRECTION") == 2 && 
             r.Field<Int16>("INT_CALL_DATA_TYPE") == 5), 
      InVoiceCount = g.Count(r => r.Field<Int16>("INT_DIRECTION") == 1 && 
             r.Field<Int16>("INT_CALL_DATA_TYPE") == 1), 
      OutVoiceCount = g.Count(r => r.Field<Int16>("INT_DIRECTION") == 2 && 
             r.Field<Int16>("INT_CALL_DATA_TYPE") == 1), 
      InVoiceDuration = g.Where(r => r.Field<Int16>("INT_DIRECTION") == 1 && 
             r.Field<Int16>("INT_CALL_DATA_TYPE") == 1) 
           .Sum(r => r.Field<int>("lNG_DURATION")), 
      OutVoiceDuration = g.Where(r => r.Field<Int16>("INT_DIRECTION") == 2 && 
              r.Field<Int16>("INT_CALL_DATA_TYPE") == 1) 
           .Sum(r => r.Field<int>("LNG_DURATION")), 
      Latitude = g.Any(s => s.Field<string> 
          ("TXT_TARGET_BTS_LOCATION_ADDRESS") != null && s.Field<string> 
          ("TXT_TARGET_BTS_LOCATION_ADDRESS").Trim() != "") ? g.First(s => s.Field<string> 
          ("TXT_TARGET_BTS_LOCATION_ADDRESS") != null && s.Field<string> 
          ("TXT_TARGET_BTS_LOCATION_ADDRESS").Trim() != "").Field<string>("TXT_LATITUDE") : "", 
      Longitude = g.Any(s => s.Field<string> 
          ("TXT_TARGET_BTS_LOCATION_ADDRESS") != null && s.Field<string> 
          ("TXT_TARGET_BTS_LOCATION_ADDRESS").Trim() != "") ? g.First(s => s.Field<string> 
          ("TXT_TARGET_BTS_LOCATION_ADDRESS") != null && s.Field<string> 
          ("TXT_TARGET_BTS_LOCATION_ADDRESS").Trim() != "").Field<string>("TXT_LONGITUDE") : "", 
      BTS_Address = g.Any(s => s.Field<string> 
          ("TXT_TARGET_BTS_LOCATION_ADDRESS") != null && s.Field<string> 
          ("TXT_TARGET_BTS_LOCATION_ADDRESS").Trim() != "") ? g.First(s => s.Field<string> 
          ("TXT_TARGET_BTS_LOCATION_ADDRESS") != null && s.Field<string> 
          ("TXT_TARGET_BTS_LOCATION_ADDRESS").Trim() != "").Field<string>("TXT_TARGET_BTS_LOCATION_ADDRESS") : "", 
      Azimuth = g.Any(s => s.Field<string> 
          ("TXT_TARGET_BTS_LOCATION_ADDRESS") != null && s.Field<string> 
          ("TXT_TARGET_BTS_LOCATION_ADDRESS").Trim() != "") ? g.First(s => s.Field<string> 
          ("TXT_TARGET_BTS_LOCATION_ADDRESS") != null && s.Field<string> 
          ("TXT_TARGET_BTS_LOCATION_ADDRESS").Trim() != "").Field<string>("TXT_AZIMUTH_DEG") : "" 

     } into summary 
    orderby summary.TotalCommCount descending 
    select summary; 
} 

而且你应该使用常数像"TXT_TARGET_BTS_LOCATION_ADDRESS"之类的东西,因为它可以帮你避免一个简单的错误,比如编写:"TXT_TARGET_BTS_LOCAITON_ADDRESS"并且让它在编译时安全。

编辑:你会调用这个函数是这样的:

var sdata = MyLinq(dt.AsEnumerable(), r => r.Field<DateTime>("DAT_START").TimeOfDay.Hours < 20 && r.Field<DateTime>("DAT_START").TimeOfDay.Hours >= 4) 

你可能需要更改Predicate<Object>Object您的实际类型,这样就可以访问点域值。

+0

如何编写谓词函数。对不起,但我对此一无所知! –

+0

@RajeevKumar看到我更新的编辑。 – Seph

+0

这一切都建立,但给我一个错误'错误不能分配void到隐式类型的本地变量\t sdata' –

0

是的,您可以通过将where零件提取到单独的表达式,然后在较大的表达式中使用它来重构表达式。

+3

你应该展示一个例子 - 目前你的回答只不过是一条评论。 – slugster

0

创建一个接收Predicate的函数。例如:

dynamic MyLinq(Predicate<Object> Check) 
{ 
    return from r in dt.AsEnumerable() 
    where Check(r) 
    select r; 
} 
4

您可以将您的谓词分解为单独的方法;

private static bool Where1(DT r) 
{ 
    return r.Field<DateTime>("DAT_START").TimeOfDay.Hours < 20 && 
         r.Field<DateTime>("DAT_START").TimeOfDay.Hours >= 4 
} 

这可以分配给你可以在你的表达式中正确使用的Func;

Func<DT, bool> myWhere 
if(whereCase1)        // Decide which Where predicate to use 
    myWhere = Where1; 
else 
    myWhere = Where2; 

var sdata = from r in dt.AsEnumerable() 
      where myWhere(r)    // Use the chosen Where predicate. 
      group r by r["TXT_TARGET_CELL_ID"] 
      into g 
      select new... 

要建立在一个稍微更动态的方式Where条件,可以使返回WHERE条件,而不是一个布尔值的函数;

private static Func<DT, bool> WhereHoursAreBetween(int min, int max) 
    { 
     return r => r.Field<DateTime>("DAT_START").TimeOfDay.Hours < max && 
        r.Field<DateTime>("DAT_START").TimeOfDay.Hours >= min; 
    } 

......它可以在上面的例子中用作;

myWhere = WhereHoursAreBetween(4, 20); 

...这使得myWhere该时间是

+0

我是否需要为每个Where条件制作单独的函数? –

+0

@RajeevKumar你可以使用lambdas或函数或从其他Funcs构建你的Func,都是可行的。通常,将它们定义为单独的函数以使代码可读是一种好方法,称为'WhereOrdersAreMax14DaysOld'的函数比'x => x.Age.Days <= 14'更具描述性。尽管你可以让它们变得有些动态,但我会在几分钟内添加一个例子。 –

+0

@RajeevKumar增加了一个更动态的方式来构建'Where'条件的例子。 –