动态构建' where' Linq中的条款

时间:2017-02-14 23:13:30

标签: c# linq

我有一个代码,用于在数据表中搜索特定字符串,并根据特定条件(唯一id字段)返回整个数据表的子集。下面的示例代码搜索" First"和"员工"在数据表中,只返回那些具有相同l_id值的条目。

//Get all Id's that satisfy all conditions:            
List<int> results = dtResult.AsEnumerable()
    // Get all Id's:
    .Select(dataRow => dataRow.Field<int>("l_id"))
    // Filter the Id's : 
    .Where(id =>
        // the Id should be greater than one.
            id > 1
                // and the datatable rows with this Id should have a record with W_Text = First
            && dtResult.AsEnumerable().Any(dataRow => dataRow.Field<string>("W_Text") == "First" && dataRow.Field<int>("l_id") == id)
                // and the datatable rows with this Id should have a record with W_Text = Employee
            && dtResult.AsEnumerable().Any(dataRow => dataRow.Field<string>("W_Text") == "Employee" && dataRow.Field<int>("l_id") == id))
            .Distinct().ToList();

// Get all datatable rows filtered by the list of Id's.
dtCopy = dtResult.AsEnumerable().Where(dataRow => results.Contains((dataRow.Field<int>("l_id")))).CopyToDataTable();

现在,如果我动态获取搜索文本,那么我该如何处理呢? 对于。例如,在上面的代码中,如果我必须通过&#34; First&#34;以及&#34;员工&#34;以及&#34;薪水&#34;那我该怎么办?

如何制作此通用名称?

预先感谢您的协助!

编辑 - 看起来我的帖子里还没有明确,所以让我再说一遍

我有一个看起来像这样的数据表

l    |  t    |  r    |  b   |   x   |   y   |   w_text | l_id
-------------------------------------------------------------
70   |  314  |  141  |  328 |   1   |   5   |   First  | 4
149  |  318  |  194  |  328 |   2   |   5   |   Employe| 4
204  |  311  |  254  |  326 |   3   |   5   |   John   | 4
264  |  311  |  325  |  326 |   4   |   5   |   Smith  | 4
1924 |  310  |  2000 |  329 |   5   |   5   |   First  | 5
70   |  341  |  109  |  355 |   1   |   6   |   step   | 5
115  |  340  |  130  |  355 |   2   |   6   |   of     | 5
136  |  340  |  175  |  355 |   3   |   6   |   Linq   | 5
185  |  339  |  320  |  356 |   4   |   6   |   Last   | 6
70   |  394  |  101  |  411 |   1   |   8   |   Employe| 6
114  |  390  |  199  |  405 |   2   |   8   |   John   | 6
210  |  390  |  269  |  405 |   3   |   8   |   Doe    | 6

我手上唯一的搜索条件是&#39; W_Text&#39;。所以我想搜索一下唯一的短语&#34; First Employee&#34;。只有一个l_id(在这种情况下) l_id = 4)会有两个单词&#34; First&#34;以及&#34;员工&#34;。如果我搜索&#34;第一&#34;单独和&#34;员工&#34;单独,然后我会得到一个更大的数据集,这不解决我的目的。我的目标是在搜索&#34; First Employee&#34;

时获取以下唯一数据集
l    |  t    |  r    |  b   |   x   |   y   |   w_text | l_id
-------------------------------------------------------------
70   |  314  |  141  |  328 |   1   |   5   |   First  | 4
149  |  318  |  194  |  328 |   2   |   5   |   Employe| 4
204  |  311  |  254  |  326 |   3   |   5   |   John   | 4
264  |  311  |  325  |  326 |   4   |   5   |   Smith  | 4

在SQL术语中,这类似于

Select * From Table where l_id in (Select l_id from Table where W_Text in ('First','Employee') group by l_id having count(l_id) > 1)

上面提到的代码(由一位优秀的撒玛利亚人帮助过)完全正常,并返回上面的数据集。问题是它只适用于&#34; First Employee&#34;。我在查找说&#34; Linq&#34;的第一步时遇到了麻烦。搜索短语在运行时传递给程序,可以是多少个单词。我试过分离Wheres但是有'&#39;条件未命中,也是返回整个数据集的位置。

因此,我请求大家帮助我解决这个问题。我对Linq很新,我正努力工作。与此同时,我能得到的任何帮助都将受到高度赞赏。感谢。

---编辑使用此代码(有人的帮助)使其工作

 List<string> wTextFilter = new List<string>();

        foreach (string sf in strInputString.Split(' ')) //array by splitting on white space
        {
            wTextFilter.Add(sf);
        }

        // Get all Id's that satisfy all conditions:            
        List<int> results = dtResult.AsEnumerable()
            // Get all Id's:
            .Select(dataRow => dataRow.Field<int>("l_id"))
            // Filter the Id's : 
            .Where(id =>
                // the Id should be greater than one.
                    id > 1 &&
                        // check if all W_Text entries has a record in the datatable with the same Id.
                    wTextFilter.All(W_Text => dtResult.AsEnumerable().Any(dataRow => dataRow.Field<string>("W_Text") == W_Text && dataRow.Field<int>("l_id") == id)))
                    .Distinct().ToList();

        // Get all datatable rows filtered by the list of Id's.
        dtCopy = dtResult.AsEnumerable().Where(dataRow => results.Contains((dataRow.Field<int>("l_id")))).CopyToDataTable();      

3 个答案:

答案 0 :(得分:2)

您的代码几乎没有问题:

  1. 您在过滤之前选择了ID 。这意味着您最终得到int s的集合,这意味着您无法再按另一列过滤。您应该过滤然后选择所需的列

  2. 您无需进行最终检查以查看列l_id。我们已经检查了l_id == id,显然列存在

  3. 您当前的查询不正确。如果数据集中的任何行匹配,则返回该行:

  4. dtResult.AsEnumerable().Any(dataRow => dataRow.Field<string>("W_Text") == "First" && dataRow.Field<int>("l_id") == id)
    

    这表示,对于每一行,检查是否有匹配的任何行。如果是这样,请返回该行。您的查询将返回整个数据集,或者不返回任何内容。

    您可以链接.Where()子句。例如:

    public List<int> DoIt(int id, params string[] searchFor)
    {
        var results = dtResult.AsEnumerable()       
            // Filter the Id's : 
            .Where(dr => dr.id > 1)
            .Where(dr => dr.Field<int>("l_id") == id);
    
        foreach (var sf in searchFor)
            results = results.Where(dr => dr.Field<string>("W_Text") == sf);
    
        results = results.Select(dataRow => dataRow.Field<int>("l_id"))
    
        return results.Distinct().CopyToDataTable();
    }
    

答案 1 :(得分:1)

从您的基本条款开始:

results = results.Where(id => id > 1);

然后根据需要动态添加子句:

if (/**some condition**/)
    results = results.Where(id => dtResult.AsEnumerable().Any(dataRow => dataRow.Field<string>("W_Text") == "First" && dataRow.Field<int>("l_id") == id));
if (/**another condition**/)
    results = results.Where(id => dtResult.AsEnumerable().Any(dataRow => dataRow.Field<string>("W_Text") == "Employee" && dataRow.Field<int>("l_id") == id))

等等。对于要动态添加的每个条件,请添加新的.Where()子句。您可以根据需要链接尽可能多的这些内容,从逻辑上讲,它可以与单个&&中的一系列.Where()子句相同。

答案 2 :(得分:0)

这是更接近您的SQL子查询的内容:

var q = dtResult.AsEnumerable()                                    // from Table 
  .Where(r => new[] { "First", "Employee" }.Contains(r["W_Text"])) // where W_Text in ('First','Employee')
  .GroupBy(r => (int)r["l_id"])                                    // group by l_id
  .Where(g => g.Count() > 1)                                       // having count(l_id) > 1
  .Select(g => g.Key);                                             // Select l_id

这是一个更高效的版本:

var words = new HashSet<string> { "First", "Employee" };  // optional HashSet instead of List for a bit faster .Contains

int iId = dtResult.Columns.IndexOf("l_id");
int iText = dtResult.Columns.IndexOf("W_Text");

var iRows = dtResult.Rows.Cast<DataRow>(); // a bit faster than dtResult.AsEnumerable()

var results = new HashSet<int>(  // optional HashSet instead of List for faster .Contains
        iRows
        .Where(r => words.Contains(r[iText]))    // filter the rows that contain the words
        .ToLookup(r => (int)r[iId])              // group by l_id
        .Where(g => g.Count() >= words.Count)    // filter the groups that contain all words
        .Select(g => g.Key)                      // select l_id
    );

var dtCopy = iRows.Where(r => results.Contains((int)r[iId])).CopyToDataTable(); // InvalidOperationException if no DataRows

但是如果所有数据都已存在于DataTable中,那么您只需按l_id分组并获取包含所有字词的组:

string[] words = { "First", "Employee" }; 

int iId = dtResult.Columns.IndexOf("l_id");
int iText = dtResult.Columns.IndexOf("W_Text");

var iRows = dtResult.Rows.Cast<DataRow>(); 

var idGroups = iRows.ToLookup(r => (int)r[iId]);    // group by id

var result = idGroups.Where(g => !words.Except(g.Select(r => r[iText] as string)).Any());

var dtCopy = result.SelectMany(g => g).CopyToDataTable();