2012-03-09 11 views
6

¿Podría verificar lo siguiente?DataTable Union

necesito unión de esto, pero devuelve 6 registro en lugar de 5 (porque "Amir" aparece dos veces)

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

Respuesta

9

El problema aquí es que LINQ no sabe que usted quiere comparar el Name. En cambio, hace lo que hace para todos los tipos de objetos, compara el hash, que es diferente para dos instancias diferentes.

Lo que necesita todo lo que necesita es decirle al método de la Unión cómo comparar dos elementos. Puede hacerlo creando un IEqualityComparer personalizado que compare dos filas de datos de la forma que desee.

Aquí está un ejemplo de implementación:

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 
} 

Al llamar Union este caso es necesario pasar en una instancia de este comparador:

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

Ver aquí para más información:
http://msdn.microsoft.com/en-us/library/bb358407.aspx

Un consejo:
Linq es mejor con clases de datos personalizadas, que no es DataRow. Lo mejor es tener una propiedad de Nombre real en la clase, solo entonces Linq realmente puede brillar.
Si no necesita la flexibilidad del esquema dinámico, debe mantenerse alejado de DataTable e implementar clases personalizadas que se asemejen exactamente a lo que necesita, ya que DataTable es extremadamente inflado y lento.

3

Si esquemas sus DataTables' son los mismos, sólo podría utilizar el DataRowComparer.Default existentes, así:

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

Y la función agregada es muy útil cuando se necesita unión más que 2 mesas, por ejemplo:

// 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>();