2012-03-09 14 views
6

Potrebbe per favore controllare quanto segue cosa c'è di sbagliato in questo.DataTable Union

Ho bisogno unione di questo, ma restituisce 6 registrare invece di 5 (perché "Amir" si verifica due volte)

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

risposta

9

Il problema qui è che Linq non sa che si desidera confrontare il Name. Invece fa quello che fa per tutti i tipi di oggetti confronta l'hash che è diverso per due diverse istanze.

Quello che ti serve è dire al metodo dell'Unione come confrontare due elementi. È possibile farlo creando un numero personalizzato IEqualityComparer che confronta due righe di dati nel modo desiderato.

Ecco un esempio di implementazione:

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 
} 

Quando si chiama Union è quindi necessario passare in un'istanza di questo operatore di confronto:

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

Vedi qui per maggiori info:
http://msdn.microsoft.com/en-us/library/bb358407.aspx

Parola di consulenza:
Linq è il migliore con le classi di dati personalizzate, che non lo è con lo standard DataRow. È meglio avere una proprietà Name effettiva sulla classe, solo allora Linq può davvero brillare.
Se non si ha bisogno della flessibilità dello schema dinamico, si dovrebbe stare lontani da DataTable e implementare classi personalizzate che assomigliano esattamente a ciò di cui si ha bisogno, dal momento che DataTable è estremamente gonfio e lento.

3

Se schemi tuoi DataTable sono gli stessi, si potrebbe utilizzare la DataRowComparer.Default esistente, in questo modo:

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

E la funzione di aggregazione è molto utile quando è necessario l'unione più di 2 tavoli, es .:

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