2013-05-26 79 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'條件的例子。 –