2008-11-19 16 views
13

Sono consapevole dell'esistenza di un evento AssociationChanged, tuttavia questo evento si attiva dopo che l'associazione è stata creata. Non c'è un evento AssociationChanging. Quindi, se voglio lanciare un'eccezione per qualche motivo di convalida, come faccio a fare questo e tornare al mio valore originale?Convalida e utilizzo del framework Entity

Inoltre, desidero valori predefiniti per la mia entità in base alle informazioni di altre entità ma farlo solo quando so che l'istanza è stata istanziata per l'inserimento nel database. Come faccio a distinguere tra ciò e l'oggetto che viene istanziato perché sta per essere popolato in base ai dati esistenti? Dovrei saperlo? Questa logica aziendale considerata dovrebbe essere al di fuori della logica aziendale della mia entità?

Se è questo il caso, dovrei progettare le classi controller per includere tutte queste entità? La mia preoccupazione è che se restituisco un'entità, voglio che il client abbia accesso alle proprietà, ma voglio mantenere uno stretto controllo sulle convalide su come sono impostate, come predefinite, ecc. Ogni esempio ho visto il contesto dei riferimenti, che è al di fuori della mia convalida parziale di classe, giusto?

BTW, ho guardato EFPocoAdapter e per la vita di me non posso determinare come compilare elenchi di all'interno della mia classe POCO ... qualcuno sa come ottengo il contesto da una classe EFPoco?

risposta

0

Per quanto riguarda la prima domanda, implementerei semplicemente le modifiche alle associazioni come logica aziendale. Ad esempio, se si aggiunge una classe insegnante con studenti multipla, non aggiungere agli studenti come

aTeacher.Students.Add(new Student) 

invece, creare un metodo AddStudent

public Student AddNewStudent(string name, string studentID) 
{ 

    Student s = new Student(name, studentID); 
    s.Teacher = this; // changes the association 
    return s; 
} 

In questo modo si ha il pieno controllo su quando le associazioni sono cambiati. Ovviamente che cosa impedisce ad un altro programmatore di aggiungere direttamente uno studente? Sul lato Studente, puoi impostare il setter Insegnante su privato (e cambiare il costruttore per accettare un insegnante o simili). Da parte dell'insegnante, come rendere la collezione Studenti non inseribile? Non sono sicuro ... forse trasformandolo in una collezione personalizzata che non accetta inserti.

Per quanto riguarda la seconda parte della tua domanda, potresti probabilmente utilizzare gli eventi OnVarNameChanging. Se EntityState è "Nuovo", puoi applicare la tua logica che recupera i valori reali.

Esiste anche un evento che si attiva quando si salvano le modifiche (OnSavingChanges?) Che è possibile utilizzare per determinare quali oggetti sono nuovi e impostare alcuni valori.

Ma forse la soluzione più semplice è impostare sempre i valori predefiniti nel costruttore e verranno sovrascritti se i dati vengono caricati dal DB.

Buona fortuna

0

Creare una fabbrica che produce le istanze per voi a seconda delle necessità, come:

getStudent(String studentName, long studentId, Teacher teacher) { 
    return new Student(studentName, studentId); 
} 

getStudentForDBInseration(String studentName, long studentId, Teacher teacher) { 
    Student student = getStudent(studentName, studentId); 
    student = teacher; 
    //some entity frameworks need the student to be in the teachers student list 
    //so you might need to add the student to the teachers student list 
    teacher.addStudent(student); 
} 
0

E 'una grave mancanza non avendo un AssociationChanging (che eredita da CancelEventArgs) evento.

Mi da fastidio anche molto, quindi ho segnalato questo a Microsoft Connect Please vote here!

E BTW, penso anche questo è anche stupido che i PropertyChangingEventArgs non eredita CancelEventArgs, dal momento che l'annullamento con un'eccezione è non sempre la soluzione elegante, inoltre, il lancio di eccezioni costa più prestazioni rispetto alla chiamata a OnPropertyChangingEvent, quindi controlla la e.Cancel restituita, quindi costa meno che generare PropertyChangingEvent, che comunque chiamerai entrambi.
Anche un'eccezione può essere lanciata al conduttore in ogni caso invece di contrassegnare e.Cancel come vero, per coloro che insistono per andare in modalità Eccezione. Vote Here.

+1

PropertyChangingEventArgs non ha alcuna relazione con CancelEventArgs. Hanno tempi che devono essere utilizzati esclusivamente per scopi diversi. Costringere l'ereditarietà causerebbe complicazione e frustrazione non necessarie (accoppiandoli strettamente). C'è PropertyChangingEvent e PropertyChangedEvent e ritengo che soddisfino le funzionalità desiderate senza che vengano apportate modifiche (per non parlare del fatto che interromperà la compatibilità da .NET 1.1 a .NET 4.0). – TamusJRoyce

+0

@TamusJRoyce, OK, sono d'accordo, non dovrebbe ereditare da 'CancelEventArgs', ma quello che dovrebbe fare è la mia altra richiesta che fornisce il valore candidato (il modo più semplice per ottenerlo è ottenere gli attributi del metodo corrente con un valore significativo ammontare del costo della prestazione). – Shimmy

0

Per poter rispondere a una parte della domanda o esporre sulla risposta di ADB è possibile utilizzare ObjectStateManager.GetObjectStateEntry per trovare lo stato delle entità e scrivere la logica predefinita personalizzata.

SaveChanges è il metodo sul contesto che è possibile utilizzare oppure SavingChanges è l'evento che si verifica prima che venga richiamato SaveChanges.

È possibile ignorare SaveChanges e base.SaveChanges chiamare solo se non si desidera interrompere il cambiamento

C'è anche un evento ObjectMaterialized per il contesto.

Tra i due si può attaccare tutta la convalida e codice di creazione in una posizione, che può essere appropriato se sono complesse e comprendono i valori di altri oggetti, ecc ..

2

Questo è in risposta a un commento ho lasciato . Spero che questo risponda alla tua domanda, Shimmy. Basta commentare, e lo accorgerò o lo rimuoverò se non risponde alla tua domanda.

Avrete bisogno di entrambe le interfacce INotifyPropertyChanging e INotifyPropertyChanged da implementare sulla vostra classe (a meno che non si tratti di qualcosa come un oggetto framework entità, che credo implementi questi internamente).

E prima di impostare un valore su questa proprietà, è necessario generare l'evento NotifyPropertyChanging.PropertyChanging, utilizzando il nome della proprietà nel costruttore PropertyChangingEventArgs.

E dopo aver impostato questo valore è necessario generare l'evento NofityPropertyChanged.PropertyChanged, utilizzando nuovamente il nome della proprietà che viene generato nel costruttore PropertyChangedEventArgs.

Quindi è necessario gestire gli eventi PropertyChanging e PropertyChanged. Nell'evento PropertyChanging, è necessario memorizzare il valore nella cache. Nell'evento PropertyChanged, puoi confrontare e generare un'eccezione.

Per ottenere la proprietà da Argomenti PropertyChanging/PropertyChanged, è necessario utilizzare la rilettura.

// PropertyName is the key, and the PropertyValue is the value. 
Dictionary <string, object> propertyDict = new Dictionary<object, object>(); 

    // Convert this function prototype to C# from VBNet. I like how Handles is descriptive. 
    Public Sub PropertyChanging(sender As object, e As PropertyChangingEventArgs) Handles Foo.PropertyChanging 
    { 
     if (sender == null || preventRecursion) 
     { 
     return; 
     } // End if 

     Type senderType = sender.GetType(); 
     PropertyInfo info = senderType.GetProperty(e.PropertyName); 
     object propertyValue = info.GetValue(sender, null); 

     // Change this so it checks if e.PropertyName already exists. 
     propertyDict.Add(e.PropertyName, propertyValue); 
    } // End PropertyChanging() Event 

    // Convert this function prototype to C# from VBNet. I like how Handles is descriptive. 
    Public Sub PropertyChanged(sender As object, e As PropertyChangedEventArgs) Handles Foo.PropertyChanged 
    { 
     if (sender == null || preventRecursion) 
     { 
     return; 
     } // End if 

     Type senderType = sender.GetType(); 
     PropertyInfo info = senderType.GetProperty(e.PropertyName); 
     object propertyValue = info.GetValue(sender, null); 

     // Change this so it makes sure e.PropertyName exists. 
     object oldValue = propertyDict(e.PropertyName); 
     object newValue = propertyValue; 

     // No longer needed. 
     propertyDict.Remove(e.PropertyName); 

     if (/* some condition */) 
     { 
     try { 
      preventRecursion = true; 
      info.SetValue(oldValue, null); 
      Throw New Exception(); 
     } finally { 
      preventRecursion = false; 
     } // End try 
     } // End if 
    } // End PropertyChanging() Event 

Notate come sto usando PreventRecursion, che è un valore booleano Ho dimenticato di aggiungere sopra questi metodi? Quando si ripristina la proprietà sul valore precedente, questi eventi verranno richiamati.

tl; dr

Ora si potrebbe ricavare un singolo evento che eredita da INotifyPropertyChanged, ma utilizza un argomento che tiene un oggetto che rappresenta il valore precedente, nonché nome della proprietà.E ciò ridurrebbe il numero di eventi sparati a uno, ha funzionalità simili e ha retrocompatibilità con INotifyPropertyChanged.

Ma se si desidera gestire qualcosa prima che la proprietà venga impostata (diciamo che la proprietà esegue una modifica irreversibile o è necessario impostare altre proprietà prima di impostare quella variabile, altrimenti verrà generata un'eccezione) non sarà possibile Fai quello.

Nel complesso, questo metodo è un modo molto vecchio di fare le cose. Vorrei prendere la risposta di Poker Villian e avere dati non validi che possono essere inseriti. Ma non consentire il salvataggio in un database.

Entity Framework ha un codice eccellente per la convalida. Aggiungi la convalida alle tue proprietà tramite attributi. E poi si occupa del lavoro di elaborazione di quegli attributi. Quindi è possibile creare una proprietà denominata IsValid, che chiama la convalida specifica di Entity Framework. Distingue anche gli errori di campo (come digitare caratteri errati o avere una stringa troppo lunga) e errori di classe (come mancare dati o chiavi in ​​conflitto).

Quindi è possibile associare IsValid alla convalida dei controlli e visualizzeranno una bolla rossa quando vengono immessi dati non validi. Oppure potresti semplicemente implementare la convalida IsValid da solo. Ma se IsValid è falso, l'evento SaveChanges dovrebbe annullare il salvataggio.

btw. Il codice fornito non verrà compilato ed è solo pseudocodice (miscelazione di vb e C#). Ma credo che sia molto più descrittivo di C# da solo - mostrando esattamente ciò che viene gestito.