DataTable Union - PullRequest
       2

DataTable Union

8 голосов
/ 09 марта 2012

Не могли бы вы проверить следующее, что не так в этом.

Мне нужно объединение этого, но он возвращает 6 записей вместо 5 (потому что "Амир" встречается два раза)

DataTable dt1 = new DataTable();
dt1.Columns.Add(new DataColumn("Name"));
dt1.Rows.Add(dt1.NewRow()["Name"] = "Imran");
dt1.Rows.Add(dt1.NewRow()["Name"] = "Amir");
dt1.Rows.Add(dt1.NewRow()["Name"] = "Asif");

DataTable dt2 = new DataTable();
dt2.Columns.Add(new DataColumn("Name"));
dt2.Rows.Add(dt2.NewRow()["Name"] = "Tandulkar");
dt2.Rows.Add(dt2.NewRow()["Name"] = "Amir");
dt2.Rows.Add(dt2.NewRow()["Name"] = "Sheqwag");

 DataTable dtUnion = dt1.AsEnumerable()
  .Union(dt2.AsEnumerable()).CopyToDataTable<DataRow>();

Ответы [ 2 ]

10 голосов
/ 09 марта 2012

Проблема здесь в том, что Linq не знает, что вы хотите сравнить Name. Вместо этого он делает то же, что и для всех типов объектов, сравнивает хеш, который отличается для двух разных экземпляров.

Что вам нужно сделать, так это указать методу Union, как сравнивать два элемента. Вы можете сделать это, создав пользовательский IEqualityComparer, который сравнивает две строки данных так, как вы этого хотите.

Вот пример реализации:

class CustomComparer : IEqualityComparer<DataRow>
{
    #region IEqualityComparer<DataRow> Members

    public bool Equals(DataRow x, DataRow y)
    {
        return ((string)x["Name"]).Equals((string)y["Name"]);
    }

    public int GetHashCode(DataRow obj)
    {
        return ((string)obj["Name"]).GetHashCode();
    }

    #endregion
}

При вызове Union вам нужно передать экземпляр этого компаратора:

var comparer = new CustomComparer();
DataTable dtUnion = dt1.AsEnumerable()
      .Union(dt2.AsEnumerable(), comparer).CopyToDataTable<DataRow>();

Смотрите здесь для получения дополнительной информации:
http://msdn.microsoft.com/en-us/library/bb358407.aspx

Слово совета:
Linq лучше всего подходит для пользовательских классов данных, а DataRow нет. Лучше иметь фактическое свойство Name в классе, только тогда Linq может действительно сиять.
Если вам не нужна гибкость динамической схемы, вам следует держаться подальше от DataTable и реализовывать пользовательские классы, которые точно соответствуют вашим потребностям, поскольку DataTable чрезвычайно раздутый и медленный.

4 голосов
/ 29 ноября 2012

Если схемы ваших DataTables совпадают, вы можете просто использовать существующий DataRowComparer.Default, например так:

DataTable dtUnion = dt1.AsEnumerable().Union(dt2.AsEnumerable()).Distinct(DataRowComparer.Default).CopyToDataTable<DataRow>();

И функция Aggregate очень удобна, когда вам нужно объединить более двух таблицНапример:

// Create a table "template"
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("Name"));

// Create a List of DataTables and add 3 identical tables
List<DataTable> dtList = new List<DataTable>();
dtList.AddRange(new List<DataTable>() { dt.Clone(), dt.Clone(), dt.Clone()});

// Populate the 3 clones with some data
dtList[0].Rows.Add("Imran");
dtList[0].Rows.Add("Amir"); 
dtList[0].Rows.Add("Asif");

dtList[1].Rows.Add("Tandulkar");
dtList[1].Rows.Add("Amir");  
dtList[1].Rows.Add("Sheqwag");

dtList[2].Rows.Add("John");
dtList[2].Rows.Add("Sheqwag");
dtList[2].Rows.Add("Mike");

// Union the 3 clones into a single DataTable containing only distinct rows
DataTable dtUnion = dtList
                    .Select(d => d.Select().AsEnumerable())
                    .Aggregate((current, next) => current.Union(next))
                    .Distinct(DataRowComparer.Default)
                    .CopyToDataTable<DataRow>();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...