1

我在我的代码中使用 LINQ 查询,该查询需要多次编写,在 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 条件来调用它吗?

4

4 回答 4

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 之间。

于 2013-05-26T06:01:12.440 回答
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)

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

于 2013-05-26T06:24:23.617 回答
0

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

于 2013-05-26T05:52:22.223 回答
0

创建一个接收Predicate. 就像是:

dynamic MyLinq(Predicate<Object> Check)
{
     return from r in dt.AsEnumerable()
     where Check(r)
     select r;
}
于 2013-05-26T05:53:46.980 回答