减少这个LINQ查询的代码行数

本文关键字:代码 查询 LINQ | 更新日期: 2023-09-27 18:15:37

我在我的代码中使用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;

这里我需要改变条件,只有每次,其余部分保持不变,即选择新的部分。

减少这个LINQ查询的代码行数

我可以在代码中编写此查询一次,并在何处条件更改时对其进行调用吗?

您可以将谓词拆分为一个单独的方法;

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

这将被赋值给一个可以在表达式中使用的函数;

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

像这样新建一个函数:

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更改为您的实际类型,以便您可以访问。field值

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

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

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