Ordinare lista di oggetti sulla base di una proprietà mutevole

rated by 0 users
This post has 8 Replies | 2 Followers

Top 10 Partecipanti
Maschio
Post 268
Punteggio 4.907
petrux Posted: 06-17-2010 15.49
Ciao a tutti,

la mia situazione è questa:

class Person
{
DateTime BirthDay { get; set; }
}

class Group
{
string Name { get; set; }

private IList people;
public IEnumerable People
{
get { return people; }
}
public void AddPerson(Person p)
{
//qui dentro viene aggiunta la persona
//a 'people' in modo che la lista sia ordinata
//sulla base della property 'BirthDay'.
}
}

Supponiamo quindi di avere;

Group g = ...; //già pieno di Person
Person p = new Person() { BirthDay = new DateTime(1978, 1, 1) };
g.AddPerson(p);

A questo punto la collection "people" dentro a "Group" è ordinata. Ma se
poi faccio:

p.BirthDate = new DateTime(1981, 1, 1);

l'ordinamento, di fatto, è sbagliato. Voi come gestireste una situazione
del genere?

Grazie,
petrux
--
  • | Punteggio Post: 20
Top 10 Partecipanti
Maschio
Post 243
Punteggio 3.383

Una soluzione semplice è quella di usare LINQ, ovvero esponi una proprietà IEnumerable<People> e dentro fail'ordinamento secondo la proprietà BirthDate, essendo la orderby un deferred operator, ogni volta che il chiamante scorre la lista viene riordinata.

Altra opssibilità, le entità implementano la INotifyPropertyChanged (e qui si può aprire un mondo di discussione), quindi quando aggiungi una person al gruppo, lui si registra per la PropertyChanged e monitora quando la proprietà BirthDay Cambia.

Alternativamente, se la relazione è bidirezionale, ovvero Person ha un link ai gruppi a cui appartiene, potrebbe esser nel setter di BirthDate che Person comunica a tutti i gruppi che la proprietà è cambiata.

Alk.

 

  • | Punteggio Post: 20
Top 10 Partecipanti
Maschio
Post 268
Punteggio 4.907
Ciao alk,

alkampfer wrote:
> Una soluzione semplice è quella di usare LINQ, ovvero esponi una
> proprietà IEnumerable e dentro fail'ordinamento secondo la
> proprietà BirthDate, essendo la orderby un deferred operator, ogni volta
> che il chiamante scorre la lista viene riordinata.

cosa intendi per "dentro"?

> Altra opssibilità, le entità implementano la INotifyPropertyChanged (e
> qui si può aprire un mondo di discussione), quindi quando aggiungi una
> person al gruppo, lui si registra per la PropertyChanged e monitora
> quando la proprietà BirthDay Cambia.

Oppure la classe Person espone un evento BirthDayChanged. Ci avevo
pensato ma l'idea che un componente del modello consumi l'interfaccia
INotifyPropertyChanged di un altro componente - non so perché - mi suona
un po' strana...

> Alternativamente, se la relazione è bidirezionale, ovvero Person ha un
> link ai gruppi a cui appartiene, potrebbe esser nel setter di BirthDate
> che Person comunica a tutti i gruppi che la proprietà è cambiata.

Potrebbe esserlo ma in teoria potrebbe anche non esserlo (diciamo che
nel caso reale sarei di fronte a uno di quei casi di mapping polimorico,
nel senso che oltre a Group, Person può essere "contenuta" anche in
altre classi).

In ultima analisi, forse la prima soluzione - ammesso che abbia capito
ciò di cui si sta parlando - è quella che mi piace di più.

Ciao,
petrux
--
  • | Punteggio Post: 20
Top 10 Partecipanti
Maschio
Post 243
Punteggio 3.383

petrux:
> Una soluzione semplice è quella di usare LINQ, ovvero esponi una
> proprietà IEnumerable e dentro fail'ordinamento secondo la
> proprietà BirthDate, essendo la orderby un deferred operator, ogni volta
> che il chiamante scorre la lista viene riordinata.

cosa intendi per "dentro"?

Internamente alla prooprietà, se tu fai una propreità di tipo IEnumerable<Oggetto> nella get restituisce un _innerlist.Orderby(o => o.proprietà)

Per l'inotifypropertychanged alla fine è un observer pattern :)

Alk.

  • | Punteggio Post: 20
Top 10 Partecipanti
Maschio
Post 268
Punteggio 4.907

Ciao alk,

Gian Maria Ricci:

Internamente alla prooprietà, se tu fai una propreità di tipo IEnumerable<Oggetto> nella get restituisce un _innerlist.Orderby(o => o.proprietà)

...mumble mumble... Ok, facciamo il merge con l'altro thread (quello del mapping di una lista). Il mio scenario è questo (codice non testato, lo sto scrivendo al volo :-p):

class Person { DateTime BirthDay { get; set; } }

class Group {

    private bool wrapList = true;

    private IList<Person> people; //per il mapping

    public IEnumerable<Person>  

    {

        get {

              if (wrapList) people = new PersonContainer(people);

              return people;

         {

     }

}

 

internal class PersonContainer : IList<Person>

{

     internal PersonContainer(IEnumerable<Person> range) { ... }

//blablabla

}

 

Dentro la classe PersonContainer poi eseguo l'ordinamento che viene ri-eseguito ad ogni GetEnumerable() se nella lista è occorso un cambiamento. La domanda che mi preme è: ma poi NHibernate tiene traccia dell'*istanza* che lui ha mappato sulla propety "people", oppure all'update prende quello che trova e lo riscrive sul db?

 

Gian Maria Ricci:

Per l'inotifypropertychanged alla fine è un observer pattern :)

 

Uhm... come pure l'evento che notifica il cambiamento di una data property. Quello che mi chiedo: è "ortodosso" che a consumare l'evento sia un altra entità del modello?

 

Ciao,

petrux

  • | Punteggio Post: 20
Top 10 Partecipanti
Maschio
Post 243
Punteggio 3.383

Io farei una cosa più semplice tipo questa

  public IEnumerable<Person>  PErsons

    {

        get {

 

              return people.OrderBy(p => p.BirthDay);

         {

     }

Ovvero usi link per tornare un ienumeable ordinato della lista originale. Dato che linq ti restituisce un deferred operator, praticamente ogni volta che il chiamante enumera la lista essa viene riordinata, per cui non hai problemi. In questo caso nhibernate salva nel DB la lista interna people in maniera non ordinata, perchè è il domain model che la ordina per il chiamante senza problemi.

Il fatto che sia Ortodosso o meno che un'entità consumi un evento di change se ne potrebbe discutere molto, ma grazie a linq, se qualcuno cambia un proprietà birthday di un oggetto nella lista, quando rilegge la proprietà gli oggetti li trova sempre ordinati :) è per questo che probabilmente è la soluzione più semplice.

Alk.

Top 10 Partecipanti
Maschio
Post 268
Punteggio 4.907

Ciao alk,

 

Gian Maria Ricci:

Io farei una cosa più semplice tipo questa

  public IEnumerable<Person>  PErsons

    {

        get {

 

              return people.OrderBy(p => p.BirthDay);

         {

     }

Ovvero usi link per tornare un ienumeable ordinato della lista originale. Dato che linq ti restituisce un deferred operator, praticamente ogni volta che il chiamante enumera la lista essa viene riordinata, per cui non hai problemi. In questo caso nhibernate salva nel DB la lista interna people in maniera non ordinata, perchè è il domain model che la ordina per il chiamante senza problemi.

Il fatto che sia Ortodosso o meno che un'entità consumi un evento di change se ne potrebbe discutere molto, ma grazie a linq, se qualcuno cambia un proprietà birthday di un oggetto nella lista, quando rilegge la proprietà gli oggetti li trova sempre ordinati :) è per questo che probabilmente è la soluzione più semplice.

 

Ok, tutto molto più chiaro. L'unica cosa è che la persistenza vedrà un ordine diverso da quello "reale".  Ora rimane solo da verificare se l'istanza della IList che NHibernate inietta nel mio oggetto di dominio viene tracciata o ri-letta in fase di update.

 

Ciao,

petrux

  • | Punteggio Post: 5
Top 10 Partecipanti
Maschio
Post 268
Punteggio 4.907

Ciao alk,

 

Gian Maria Ricci:

Io farei una cosa più semplice tipo questa

 

  public IEnumerable<Person>  PErsons

 

    {

        get {

              return people.OrderBy(p => p.BirthDay);

         {

 

     }

 

ho notato questo: 

 

public class Foo

{

public int Seed { get; set; }

public string Label { get; set; }

}

 

public class FooContainer

{

protected IList<Foo> fooList = new List<Foo>();

public IEnumerable<Foo> FooList

{

get { return fooList.OrderBy((f) => f.Seed); }

}

 

public void Add(Foo foo)

{

fooList.Add(foo);

}

public bool Remove(Foo foo)

{

return fooList.Remove(foo);

}

}

 

class Program

{

static void Main(string[] args)

    {

try

{

FooContainer c = new FooContainer();

c.Add(new Foo() { Label = "1", Seed = 1 });

c.Add(new Foo() { Label = "2", Seed = 2 });

c.Add(new Foo() { Label = "3", Seed = 3 });

c.Add(new Foo() { Label = "4", Seed = 4 });

c.Add(new Foo() { Label = "5", Seed = 5 });

 

foreach (Foo f in c.FooList)

if (f.Seed == 3)

c.Add(new Foo() { Label = "IMPOSSIBILE", Seed = -1 });

}

catch (Exception e)

{

Console.WriteLine(e.Message);

}

    }

}

 

se qualcuno sta enumerando una collection e questa nel frattempo viene modificata, in teoria continuando l'iterazione si dovrebbe avere una eccezione che in questo caso *non si ha* visto che l'IEnumerable<Foo> su sui sto enumerando non è la collection che vado a visitare. Non è un po' misleading?

 

Ciao,

petrux

--

  • | Punteggio Post: 20
Top 10 Partecipanti
Maschio
Post 243
Punteggio 3.383

Chiaramente la semantica dell'ienumerable in questo caso è cambiata. Direi che non è un problema insormontabile, raramente capita di dovere enumerare e contemporaneamente aver una buona ragione per cambiare la lista.

Chiaramente se questa difformità dal funzionamento base non è accettabile allora ti puoi fare una tua implementazione di una lista che internamente si ordina e lancia eccezione se qualcuno la modifica durante l'enumerazione. ;)

alk.

  • | Punteggio Post: 5
Pagina 1 di 1 (9 elementi) | RSS
Powered by Community Server (Commercial Edition), by Telligent Systems