DataTable C# 中的验证

Validation in DataTable C#

我有一个包含 20 列和 25000 行的数据表。有一个名为 URL 的列和一个名为 Language.

的列

我需要确保所有相同的 URL 具有相同的语言。

目前我已经通过以下步骤实现了

  1. 获取所有不同的(唯一的)URLs

  2. 在 URL 上创建了一个 foreach 循环并创建了一个 DataView(在 URL 上过滤)

  3. 现在在数据视图中我可以检查语言列中的所有值是否相同。

     List<string> all_Distinct_Urls = helperFunction.DataTableToList(master_table, "URL");
    
     foreach (var url in all_Distinct_Urls)
     {
         if (!string.IsNullOrEmpty(url))
         {
             DataView dv = new DataView(master_table);
             dv.RowFilter = "[URL] = '" + url + "'";
             DataTable temp_MasterTable = dv.ToTable();
    
             List<string> all_languages = helperFunction.DataTableToList(temp_MasterTable, "Language");
             if (all_languages.Count > 1)
             {
               Assert.Fail();
             }
      }
    
    
     public List<string> DataTableToList(DataTable masterDataTable, string columnName, bool isDistinct = true) 
     { 
         List<string> list = new List<string>(); 
         foreach (DataRow dataRow in masterDataTable.Rows) 
         { 
             string ID = dataRow[columnName].ToString().Trim(); 
             list.Add(ID); 
         } 
         if (isDistinct) 
         { 
             list = list.Distinct().ToList(); 
         } 
         return list; 
    }
    

但问题是考虑到行数和列数,这会消耗大量时间。有没有更快的方法来实现这个目标?

我会使用 LINQ。我相信这种方法会快很多:

var invalidUrlLanguageGroups = master_table.AsEnumerable()
    .GroupBy(r => r.Field<string>("Url"))
    .Where(g => g.Select(r => r.Field<string>("Language")).Distinct().Skip(1).Any())
    .ToList();

我按 url 分组,然后选择所有不同的语言并检查是否有多种语言。

测试用例:

var master_table = new DataTable();
master_table.Columns.Add("Url");
master_table.Columns.Add("Language");
master_table.Rows.Add("/en-us/sample-page1", "english");
master_table.Rows.Add("/en-us/sample-page1", "german"); // fail
master_table.Rows.Add("/de-de/sample-page2", "german");
master_table.Rows.Add("/en-de/sample-page2", "english");

请注意,查询会收集所有无效的 url 及其数据行。如果您想要一个更高效的查询,只确定是否至少有一个(使测试失败),请使用:

bool anyInvalidUrlLanguageGroups = master_table.AsEnumerable()
    .GroupBy(r => r.Field<string>("Url"))
    .Any(g => g.Select(r => r.Field<string>("Language")).Distinct().Skip(1).Any());

how about if I want to validate that all columns are the same, not just the Language column? So if the URL is the same then all column values should be the same

好吧,那么这个方法将有助于检查所有列(对于每个 url-group)是否相等。您也可以在许多其他情况下使用它,因此很适合扩展:

public static bool AllItemsEqual<T>(IEnumerable<IEnumerable<T>> allSequences, IEqualityComparer<T> comparer = null)
{
    if (comparer == null) comparer = EqualityComparer<T>.Default;
    IEnumerable<T> first = null;
    foreach(IEnumerable<T> items in allSequences)
    {
        if (first == null) 
            first = items;
        else
        {
            if (!items.SequenceEqual(first, comparer))
                return false;
        }
    }

    return true;
}

届时您将以这种方式使用它:

List<string> columnsExceptUrl = master_table.Columns.Cast<DataColumn>()
    .Select(c => c.ColumnName)
    .Where(n => n != "Url")
    .ToList();

var urlRowsWithDifferentColumns = master_table.AsEnumerable()
   .GroupBy(r => r.Field<string>("Url"))
   .Where(g => !AllItemsEqual(g.Select(r => columnsExceptUrl.Select(c => r[c].ToString()))))
   .ToList();

同样,如果你只是想知道它是否失败,你可以提高效率:

bool anyUrlRowsWithDifferentColumns = master_table.AsEnumerable()
   .GroupBy(r => r.Field<string>("Url"))
   .Any(g => !AllItemsEqual(g.Select(r => columnsExceptUrl.Select(c => r[c].ToString()))));