减少此LINQ查询的代码行

时间:2013-05-26 05:43:15

标签: c# winforms linq

我在我的代码中使用LINQ查询,需要在where where条件下进行多次写入。我的问题是

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;

这里我需要每次都改变条件,剩下的部分保持不变,即选择新的部分。我可以在代码中编写一次这个查询并通过更改where where condition来调用它吗?

4 个答案:

答案 0 :(得分: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条件而不是bool的函数;

    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成为小时数在4到20之间的条件。

答案 1 :(得分:1)

只需制作一个像这样的新功能:

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)

您可能需要将Object中的Predicate<Object>更改为您的实际类型,以便您可以访问.Field值。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

创建一个接收Predicate的函数。类似的东西:

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