linq 有没有办法把这些重复的代码变成一个单一的、干净的方法

ctehm74n  于 12个月前  发布在  其他
关注(0)|答案(2)|浏览(101)

我在我的搜索逻辑中有以下代码,它引用了一个遗留数据库,其中我可以匹配搜索的唯一方法是表行名称,Level-1,Level-2等。

if (criteria.Levels.Contains("Level-1", StringComparer.CurrentCultureIgnoreCase))
{
    var filteredResult = allCachedItems
        .Where(x => !string.IsNullOrEmpty(x.Level1));

    resultsSet.AddRange(filteredResult.Except(resultsSet));
}

if (criteria.Levels.Contains("Level-2", StringComparer.CurrentCultureIgnoreCase))
{
    var filteredResult = allCachedItems
        .Where(x => !string.IsNullOrEmpty(x.Level2));

    resultsSet.AddRange(filteredResult.Except(resultsSet));
}

if (criteria.Levels.Contains("Level-3", StringComparer.CurrentCultureIgnoreCase))
{
    var filteredResult = allCachedItems
        .Where(x => !string.IsNullOrEmpty(x.Level3));

    resultsSet.AddRange(filteredResult.Except(resultsSet));
}

字符串
显然,这看起来很可怕,而且是重复的代码,所以我想创建一个可以接受字符串的方法,例如“Level-1”和某种委托来确定模型上的哪些属性要测试。
或者用一种更干净的方式来过滤我的“allCachedItems”集合。
该表具有多个名为级别{X}的列,每个列在适合记录的地方包含一些文本,在不适合的地方为空。
搜索表单以“Level-{x}"格式传递纯文本。

xpcnnkqh

xpcnnkqh1#

给予这个一个裂缝:

void AddCachedLevel(string level, Func<Item, string> property)
{
    if (criteria.Levels.Contains(level, StringComparer.CurrentCultureIgnoreCase))
    {
        var filteredResult = allCachedItems.Where(x => !string.IsNullOrEmpty(property(x)));
        resultsSet.AddRange(filteredResult.Except(resultsSet));
    }
}

AddCachedLevel("Level-1", x => x.Level1);
AddCachedLevel("Level-2", x => x.Level2);
AddCachedLevel("Level-3", x => x.Level3);

字符串
或者,如果resultsSet最初为空:

List<Item> resultsSet =
(
    from x in allCachedItems
    where
        criteria.Levels.Contains("Level-1", StringComparer.CurrentCultureIgnoreCase) && !string.IsNullOrEmpty(x.Level1)
        || criteria.Levels.Contains("Level-2", StringComparer.CurrentCultureIgnoreCase) && !string.IsNullOrEmpty(x.Level2)
        || criteria.Levels.Contains("Level-3", StringComparer.CurrentCultureIgnoreCase) && !string.IsNullOrEmpty(x.Level3)
    select x
).ToList();


或者,如果你想要两者的结合:

bool Check(Item item, string level, Func<Item, string> property) =>
    criteria.Levels.Contains(level, StringComparer.CurrentCultureIgnoreCase)
    && !string.IsNullOrEmpty(property(item));
    
List<Item> resultsSet =
(
    from item in allCachedItems
    where
        Check(item, "Level-1", x => x.Level1)
        || Check(item, "Level-2", x => x.Level2)
        || Check(item, "Level-3", x => x.Level3)
    select item
).ToList();


或者这是对重复的终极打击。

bool Check(Item item, string level, Func<Item, string> property) =>
    criteria.Levels.Contains(level, StringComparer.CurrentCultureIgnoreCase)
    && !string.IsNullOrEmpty(property(item));

Func<Item, bool>[] checks = new Func<Item, bool>[]
{
    i => Check(i, "Level-1", x => x.Level1),
    i => Check(i, "Level-2", x => x.Level2),
    i => Check(i, "Level-3", x => x.Level3),
};

List<Item> resultsSet =
(
    from item in allCachedItems
    where checks.Any(check => check(item))
    select item
).ToList();

mefy6pfw

mefy6pfw2#

所以我想创建一个方法,它可以接受一个字符串,例如“Level-1”和某种委托来确定模型上的哪些属性要测试。
你可以像这样创建一个本地函数:

void AddLevelToResultSet(int level, Func<X, string?> levelProperty) {
    // kind of weird that you are using a culture-specific comparison here...
    if (criteria.Levels.Contains($"Level-{level}", StringComparer.CurrentCultureIgnoreCase))
    {
        var filteredResult = allCachedItems
            .Where(x => !string.IsNullOrEmpty(levelProperty(x)));
    
        resultsSet.AddRange(filteredResult.Except(resultsSet));
    }
}

// ...

AddLevelToResultSet(1, x => x.Level1);
AddLevelToResultSet(2, x => x.Level2);
AddLevelToResultSet(3, x => x.Level3);

字符串
其中XallCachedItems中的项目类型。
还可以考虑使用Dictionary<int, Func<X, string?>>来记录级别和属性之间的Map:

Dictionary<int, Func<X, string?>> mapping = new() {
    { 1, x => x.Level1 },
    { 2, x => x.Level2 },
    { 3, x => x.Level3 },
}


这样,AddLevelToResultSet只需要level参数。levelProperty可以用mapping[level]计算。
或者用一种更干净的方式来过滤我的allCachedItems集合。
如果allCachedItems是一个普通的集合(与IQueryable相反),并且所有的级别都是不相交的,那么你可以在一个Where中做到这一点,其中所有的级别都与||相连。

// local helper function
bool CheckForLevel(int level, X item) =>
    criteria.Levels.Contains(
        $"Level-{level}", 
        StringComparer.CurrentCultureIgnoreCase
    ) && !string.IsNullOrEmpty(mapping[level](item));

var filteredResult = allCachedItems
    .Where(x => 
        CheckForLevel(1, x) || 
        CheckForLevel(2, x) || 
        CheckForLevel(3, x)
    );
resultsSet.AddRange(filteredResult.Except(resultsSet));

相关问题