【问题标题】:Using LINQ to filter on a variable number of parameters in c#使用 LINQ 在 c# 中过滤可变数量的参数
【发布时间】:2015-03-04 11:53:24
【问题描述】:

我有一个 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

这是我现在拥有的代码,问题是该代码仅用于检查是否存在将 ONE 列设置为特定值的行。

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;
}

【问题讨论】:

    标签: c# linq linq-to-dataset


    【解决方案1】:

    从您的示例中我不清楚您是否要返回 true 如果在任何行中找到 any 输入,或者如果它们 all 必须返回可以在某行找到。

    前者更简单,所以这里有一个例子:

    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"));
    


    如果以上不是您实际寻找的标准,请编辑您的问题,以便清楚您想要什么标准。要非常具体,并记住一组示例不是唯一地定义了某些标准。如果您不确定我的意思,请阅读https://stackoverflow.com/help/how-to-ask 以获得更具体的提示。


    编辑:

    根据您更新的问题,您在其中表明您想要后一种行为,即给定列/值对的 每个 必须在 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

    请注意,上述方法并不是实现这一点的唯一方法。实际上,如果您希望有大量的列/值对,为了提高性能,您可能需要一些不同的东西,例如使用哈希集或字典(可以使用其中任何一种,但它们各有其特定的优点和缺点)。但是对于数量相对较少(最多几十个)的列/值对,一个列表就足够了,而且它确实使实现简单。

    【讨论】:

    • 我正在寻找后面的“它们都必须在某一行中找到”。我已经更新了我原来的问题。希望现在更清楚了。
    【解决方案2】:

    (您可以使用其他东西来保存参数而不是Tuple,例如KeyValuePair

    列可以是任何类型,而且字段的值可以为空。

    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)));
     }
    

    【讨论】:

      猜你喜欢
      • 2012-10-06
      • 2010-12-12
      • 1970-01-01
      • 1970-01-01
      • 2017-08-04
      • 2018-09-30
      • 1970-01-01
      • 2014-07-08
      相关资源
      最近更新 更多