2012-11-13 19 views
6

ho un primo elenco di entità simile a questo:Unire due elenchi di diversi tipi e ordinarli per data

public partial class Networking :EntityBase 
{ 

    public virtual int NetWorkingId 
    { 
     get; 
     set; 
    } 

    public virtual string NetWorkingParam 
    { 
     get; 
     set; 
    } 

    public virtual System.DateTime NetWorkingDate 
    { 
     get; 
     set; 
    } 
} 

E ho un secondo elenco di entità simile a questo:

public partial class PrivateNetwork :EntityBase 
{ 
    public virtual int PrivateNetworkId 
    { 
     get; 
     set; 
    } 

    public virtual int ContaId 
    { 
     get { return _contaId; } 
     set 
     { 
      if (_contaId != value) 
      { 
       if (Contact != null && Contact.ContaId != value) 
       { 
        Contact = null; 
       } 
       _contaId = value; 
      } 
     } 
    } 

    public virtual Nullable<System.DateTime> DateCreation 
    { 
     get; 
     set; 
    } 
} 

I vuoi raccogliere questi due elenchi in uno e ordinare tutti gli elementi per data.

È possibile?

+0

Hai provato qualcosa? Unione di Linq Join OrderBy può aiutare ... –

+0

Sì, ho provato Union, ma il problema è come ordinarli perché non hanno lo stesso campo – kbaccouche

+0

Puoi 'Selezionare' entrambi su un oggetto anonimo comune –

risposta

5

Questo problema potrebbe essere facilmente risolto utilizzando il polimorfismo; utilizzare una classe base comune o un'interfaccia per entrambe le classi, che ha la proprietà DateTime su cui si desidera ordinare.

Esempio:

public abstract class NetworkingBase : EntityBase 
{ 
    public DateTime DateToSortOn { get; set; } 
} 

o

public interface INetworking 
{ 
    public DateTime DateToSortOn { get; set; } 
} 

e poi fare le vostre classi derivano da NetworkingBase o implementare INetworking:

public partial class Networking : NetworkingBase 
{ 
    ... 
} 

public partial class PrivateNetwork : NetworkingBase 
{ 
    ... 
} 

o

public partial class Networking : EntityBase, INetworking 
{ 
    ... 
} 

public partial class PrivateNetwork : EntityBase, INetworking 
{ 
    ... 
} 

Fare un LINQ Union o Concat e quindi un OrderBy sulla raccolta risultante.

+5

Questo non è vero (che una base comune è * richiesta *); Gli oggetti "union" possono essere creati - anonimi o nominati - che possono quindi essere ordinati/memorizzati. Non c'è bisogno di una base comune. –

+0

@ pst Quindi si finirebbe con un elenco di oggetti anonimi? – khellang

+0

Suppongo che abbia più entità di base su EntityBase. Forse questa data non è rilevante per quelle classi. –

5

Si può fare questo, anche se non è molto carina, e si finisce con un IEnumerable<object> modo da avere per verificare il tipo di ogni oggetto prima è possibile utilizzarlo:

IEnumerable<object> sorted = myNetworkingList 
    .Concat<object>(myPrivateNetworkList) 
    .OrderBy(n => n is Networking 
       ? (DateTime?)((Networking)n).NetWorkingDate 
       : ((PrivateNetwork)n).DateCreation); 

foreach (object either in sorted) 
{ 
    if (either is Networking) 
     // Networking; do something 
    else 
     // PrivateNetwork; do something else 
} 
+0

-1: Questa è una soluzione brutta e complessa a un problema risolto con il polimorfismo.Quando viene aggiunto un terzo tipo 'CafeWifiNetwork', non c'è niente, tranne la memoria originale devs (_famously difettosa_), per dire che questo ciclo linq e foreach deve cambiare. Con uno qualsiasi degli approcci suggeriti da @khellang, il compilatore ti costringerà a scrivere il codice correttamente. Funzionerà ma non passerà alcuna recensione del codice di cui farò parte. –

+0

@Binary Non c'è nulla in comune tra le due classi - le proprietà della data non sono nemmeno dello stesso tipo, per non parlare dello stesso nome - quindi creare un'interfaccia comune non è un polimorfismo, è solo nascondere una brutta operazione (ordinare una lista di oggetti non correlati) via nelle classi stesse piuttosto che inserirle nel codice in cui viene consumato il risultato. In particolare, non chiamerei l'interfaccia "Inetworking" quando non contiene nulla a che fare con il networking; è più simile a 'IArbitraryObejctThatHasANullableDateTimeProperty'. – Rawling

+0

Non sono d'accordo. C'è qualche ragione per cui il richiedente vuole unire le liste e ordinarle, quindi concettualmente a un certo livello ** sono la stessa cosa **. L'argomento secondo cui le date sono "diversi tipi" è spurio, "DateTime?" Copre entrambe. Infine, non c'è assolutamente nulla di sbagliato nell'avere un'interfaccia 'INetworkingCreatedDate' con un singolo membro. Anche se "stai solo nascondendo una brutta operazione nelle classi", è meglio _hidden_ ​​there ** dove la relazione verrà applicata dal compilatore ** piuttosto che sparsi in vari _hard per trovare_ posti attraverso la base del codice. –

1

creare un'interfaccia che ha la data e implementare in entrambe le classi. Dopo che lo smistamento è facile.

public interface INetwork 
{ 
    DateTime? Date { get; } 
} 

public partial class Networking :EntityBase, INetwork 
{ 
    public DateTime? Date 
    { 
     get { return NetWorkingDate; } 
    } 
} 

public partial class PrivateNetwork :EntityBase, INetwork 
{ 
    public DateTime? Date 
    { 
     get { return DateCreation; } 
    } 
} 

var commonList = new List<INetwork>(); 
// Add instances of PrivateNetwork and Networking to the list 

var orderedByDate = commonList.OrderBy(n => n.Date); 
2

Quello che avrei dovuto chiedere prima è. . .

Cosa vuoi fare dopo averli ordinati?

La risposta a questo potrebbe avere un grande impatto sulla potenziale soluzione.

Se la risposta è qualcosa come ho bisogno di visualizzare un elenco delle date, dove è necessario solo le date in ordine. In tal caso, non è necessario unire i due elenchi, è possibile ottenere una sequenza delle sole date ordinate e utilizzarle ad es.

var orderedDates = networks.Select(n => n.NetworkingDate) 
        .Union(privateNetworks.Select(n => n.DateCreation)) 
        .OrderBy(date => date); 

Se la risposta è ho bisogno di visualizzare un elenco di link che mostra la data che si collega alla Id dell'oggetto, e qualcosa per identificare il tipo di oggetto, allora si potrebbe ottenere via con qualcosa di molto come sopra, con un oggetto Anonimo.

var orderedDates = networks.Select(n => new {Date = n.NetworkingDate, Id = n.NetWorkingId, NetworkType = n.GetType().Name}) 
        .Union(privateNetworks.Select(n => new {Date = n.DateCreation, Id = n.PrivateNetWorkingId, NetworkType = n.GetType().Name})) 
        .OrderBy(n => n.Date); 

Tuttavia, se la risposta è ho bisogno di inviare un comando di spegnimento() per le 10 reti più antiche allora si ha realmente bisogno di una soluzione polimorfico, dove si ha un unico tipo che è possibile chiamare un Metodo Shutdown(), che verrà risolto con il metodo specifico Shutdown() sui tipi che stai utilizzando.

Una soluzione polimorfico di utilizzare solo se l'utente khellang's answer non funziona per voi

Da un commento su un'altra risposta

@BinaryWorrier Ho scelto questa risposta perché ho già record in il database, quindi se scelgo di aggiungere una nuova interfaccia, come faccio a trattare con i record già archiviati prima di aggiungere l'interfaccia?

Trovo difficile credere che il vostro ORM non permetterà di aggiungere un'interfaccia per una classe entità e non - in qualche modo - segnano l'interfaccia e/o è membro quindi sono ignorati dal ORM.

Tuttavia, supponendo che non sia possibile aggiungere una nuova interfaccia o classe base, è ancora possibile farlo in modo polimorfico.

aggiungere l'interfaccia, aggiungere una classe che implementa l'interfaccia che per ciascuna delle vostre classi di rete (i Abstractor classi), poi trasformare le classi di rete in Abstractor classi, aggiungendoli a un List<INetwork> e smistamento quella lista.

public interface INetwork 
{ 
    DateTime? Date { get; } 
} 

public class PrivateNetworkAbstractor 
    :INetwork 
{ 
    private PrivateNetwork network; 
    public PrivateNetworkAbstractor(PrivateNetwork network) 
    { 
     this.network = network; 
    } 
    public DateTime? Date 
    { 
     get { return network.DateCreation; } 
    } 
} 

public class NetworkingAbstractor 
    : INetwork 
{ 
    private Networking networking; 
    public NetworkingAbstractor(Networking networking) 
    { 
     this.networking = networking; 
    } 
    public DateTime? Date 
    { 
     get { return networking.NetWorkingDate; } 
    } 
} 
... 

public IEnumerable<INetwork> MergenSort(IEnumerable<Networking> generalNetWorks, IEnumerable<PrivateNetwork> privateNetWorks) 
{ 
    return generalNetWorks.Select(n => new NetworkingAbstractor(n)).Cast<INetwork>() 
    .Union(privateNetWorks.Select(n => new PrivateNetworkAbstractor(n)).Cast<INetwork>()) 
    .OrderBy(n=> n.Date); 
} 
+0

+1 per il modello di quasi-decoratore :) – khellang

+0

@KristianHellang: C'è stato un tempo in cui conoscevo i pattern back all'indietro, ma non l'ho aperto da anni e ora ricordo le tecniche ma dimentico il nome del pattern, o dimentica che ho imparato la tecnica come un modello. Ragazzo, vorrei che il mio cervello funzionasse come 20 anni fa. . . –

0

Prima soluzione sta usando tipo anonimo

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace Example1 
{ 
    class Program 
    { 
     class Human 
     { 
      public string Name { get; set; } 
      public string Hobby { get; set; } 
      public DateTime DateOfBirth { get; set; } 
     } 
     class Animal 
     { 
      public string Name { get; set; } 
      public string FavouriteFood { get; set; } 
      public DateTime DateOfBirth { get; set; } 
     } 

     static void Main(string[] args) 
     { 
      var humans = new List<Human> 
      { 
       new Human 
       { 
        Name = "Kate", 
        Hobby = "Fitness", 
        DateOfBirth = DateTime.Now.AddYears(-27), 
       }, 
       new Human 
       { 
        Name = "John", 
        Hobby = "Cars", 
        DateOfBirth = DateTime.Now.AddYears(-32), 
       }, 
      }; 

      var animals = new List<Animal> 
      { 
       new Animal 
       { 
        Name = "Fluffy", 
        FavouriteFood = "Grain", 
        DateOfBirth = DateTime.Now.AddYears(-2), 
       }, 
       new Animal 
       { 
        Name = "Bongo", 
        FavouriteFood = "Beef", 
        DateOfBirth = DateTime.Now.AddYears(-6), 
       }, 
      }; 

      var customCollection = (from human in humans 
            select new 
             { 
              Name = human.Name, 
              Date = human.DateOfBirth, 
             } 
             ).Union(from animal in animals 
               select new 
                { 
                 Name = animal.Name, 
                 Date = animal.DateOfBirth, 
                }).OrderBy(x => x.Date); 


      foreach (dynamic customItem in customCollection) 
       Console.WriteLine(String.Format("Date: {0}, Name: {1}",  customItem.Date, customItem.Name)); 

      Console.Read(); 
     } 
    } 
} 

o senza tipo anonimo (creata CustomClass):

... 
class CustomClass 
     { 
      public string Name { get; set; } 
      public DateTime Date { get; set; } 
     } 
... 
var customCollection = (from human in humans 
            select new CustomClass 
             { 
              Name = human.Name, 
              Date = human.DateOfBirth, 
             } 
             ).Union(from animal in animals 
               select new CustomClass 
                { 
                 Name = animal.Name, 
                 Date = animal.DateOfBirth, 
                }).OrderBy(x => x.Date); 


      foreach (CustomClass customItem in customCollection) 
       Console.WriteLine(String.Format("Date: {0}, Name: {1}", customItem.Date, customItem.Name)); 

... 
0

ho semplicemente aggiunto una classe base e assegnato come il genitore di entrambi classi della lista. e poi semplice ha fatto il sindacato. ha fatto il trucco

Problemi correlati