在C#中使用LINQ过滤可变数量的参数

jmo0nnb3  于 12个月前  发布在  C#
关注(0)|答案(2)|浏览(171)

我有一个DataSet和一个DataTable,现在我想要一种通用的方法来检查不同的LINQ查询是否返回任何匹配。
例如,假设我有一个名为MyTable的表,其中包含以下数据。

Col1   Col2   Col3   Col4

A1     B1     C1     D1

A2     B2     C2     D2

A3     B3     C3     D3

字符串
我现在想要一个函数Conflicting,我可以用任意数量的参数来表示列和值。如果所有参数都匹配任何一行,Conflicting应该返回true。

Conflicting(MyTable, (Col1,A1)) //returns True
Conflicting(MyTable, (Col1,A1), (Col2,B1),(Col3,C1), (Col4,D1)) //returns True
Conflicting(MyTable, (Col1,A1), (Col2,D1)) //returns False


这就是我现在的代码,问题是代码只用于检查是否存在一个列设置为特定值的行。

public static bool Conflicting(string table, string colum, string value)
{
    DataTable dt = state.Tables[table];

    var lines =
        from s in dt.AsEnumerable()
        where s.Field<string>(parameter) == value
        select new
        {
            ID = s.Field<Int32>(table + "ID")
        };

    var count = lines.Count();

    return count > 0;
}

0sgqnhkj

0sgqnhkj1#

(You可以使用其他东西来保存参数,而不是Tuple,例如KeyValuePair
列可以是任何类型,而且字段的值可以为null。

public static bool Conflicting(this DataTable dt, params Tuple<string, object>[] columnWithValue)
{
    return dt.AsEnumerable().Any(row =>
    {
          for (int i = 0; i < columnWithValue.Length; i++)
          {
                // check for null values
                if (row.IsNull(columnWithValue[i].Item1))
                {
                    if (columnWithValue[i].Item2 != null) 
                       return false;
                }
                else
                {
                   if (!row[columnWithValue[i].Item1].Equals(columnWithValue[i].Item2)) 
                       return false;
                }
           }
           return true;
      });
}

字符串
使用代码:

DataTable dt = new DataTable();
        dt.Columns.Add("ColA", typeof(string));
        dt.Columns.Add("ColB", typeof(string));
        dt.Columns.Add("ColC", typeof(string));
        dt.Columns.Add("ColD", typeof(string));

        dt.Rows.Add("A", null, "C", "D");

        for (int i = 1; i < 10; i++)
            dt.Rows.Add("A" + i, "B" + i, "C" + i, "D" + i);

        bool res =  dt.Conflicting(
            Tuple.Create<string,object>("ColA", "A1"),
            Tuple.Create<string,object>("ColB", "B1")
            );

        Console.WriteLine(res); //true

        res = dt.Conflicting(
            Tuple.Create<string, object>("ColA", "A1"),
            Tuple.Create<string, object>("ColB", "B11")
            );

        Console.WriteLine(res);//false

        res = dt.Conflicting(
           Tuple.Create<string, object>("ColA", "A2"),
           Tuple.Create<string, object>("ColB", "B2"),
           Tuple.Create<string, object>("ColC", "C2"),
           Tuple.Create<string, object>("ColD", "D2")
           );

        Console.WriteLine(res);//true

        res = dt.Conflicting(
            Tuple.Create<string, object>("ColA", "A"),
            Tuple.Create<string, object>("ColB", null)
            );

        Console.WriteLine(res);//true


同样的结果也可以用一个Linq语句来实现:

public static bool Conflicting(this DataTable dt, params Tuple<string, object>[] columnWithValue)
 {
    return dt.AsEnumerable().Any(row => columnWithValue.All(col => row.IsNull(col.Item1) ? col.Item2 == null : row[col.Item1].Equals(col.Item2)));
 }

4ktjp1zp

4ktjp1zp2#

从你的例子中,我不清楚如果在任何一行中找到了 any 输入,或者如果必须在某一行中找到它们 all,你是否要返回true
前者更简单,所以这里有一个例子:

static bool Conflicting(string table, params Tuple<string, string>[] columnValuePairs)
{
    DataTable dt = state.Tables[table];

    return dt.AsEnumerable().Any(
        row => columnValuePairs.Any(p => row.Field<string>(p.Item1) == p.Item2));
}

字符串
你可以这样调用上面的代码:

Conflicting("MyTable",
    Tuple.Create("Col1", "A1"),
    Tuple.Create("Col2", "B1"),
    Tuple.Create("Col3", "C1"),
    Tuple.Create("Col4", "D1"));

编辑:

根据您更新的问题,您在其中指出您想要后一种行为,即给定列/值对中的 each 必须在 some 行中找到,以下应该起作用:

static bool Conflicting(string table, params Tuple<string, string>[] columnValuePairs)
{
    DataTable dt = state.Tables[table];
    var remainingPairs = new List<Tuple<string, string>>(columnValuePairs);

    foreach (var row in dt.AsEnumerable())
    {
        int i = 0;

        while (i < remainingPairs.Count)
        {
            Tuple<string, string> columnValuePair = remainingPairs[i];

            if (row.Field<string>(columnValuePair.Item1) == columnValuePair.Item2)
            {
                remainingPairs.RemoveAt(i);
                continue;
            }

            i++:
        }

        if (remainingPairs.Count == 0)
        {
            return true;
        }
    }

    return false;
}


上面的方法检查每一行以查找该行的任何匹配列/值对,从列/值对列表中删除该列/值对以查找是否匹配。如果要查找的列/值对列表为空,则该方法返回true,从而找到了所有请求的列/值对。如果枚举了表中的所有行而没有清空列表,则某对不匹配任何行,方法返回false
请注意,上面的方法并不是实现这一点的唯一方法。实际上,如果您希望有大量的列/值对,那么为了提高性能,您可能需要一些不同的方法,例如使用哈希集或字典(两者都可以使用,但它们都有其特定的优点和缺点)。(最多几十个)列/值对,一个列表就足够了,而且它确实保持了实现的简单性。

相关问题