2016-04-28 20 views
12

Sto cercando una soluzione per eseguire alcune convalide di entità personalizzate (che richiederebbero l'accesso al database, la convalida del membro trasversale ...) quando l'utente salva le sue modifiche in una schermata di dati dinamici, con Entity Framework.
La convalida è più complessa di quello che posso fare con gli attributi (richiede un accesso al database, ecc.)Convalida dell'entità avanzata personalizzata con dati dinamici

È possibile intercettare la chiamata di SaveChanges?
Ho tentato di ignorare ValidateEntity nell'oggetto DbContext, ma i dati dinamici non sembrano chiamarlo (probabilmente perché sta utilizzando l'ObjectContext interno, non sono sicuro del perché), e l'annullamento di SaveChanges non aiuta neanche.
Non vedo comunque che potrei sottoscrivere ...

Il documentation dovrebbe aiutare:

convalida Personalizza per un individuo campo di dati sovrascrivendo il metodo OnValidate o la gestione dell'evento Convalida , che vengono invocati quando viene modificato un campo dati. Questo approccio consente di aggiungere la convalida e la logica aziendale per un singolo campo. Questo approccio è più generale di rispetto all'aggiunta della convalida per un singolo campo. È utile quando la stessa logica di convalida può essere applicata a più di un campo di dati. Consente inoltre di eseguire controlli di convalida che coinvolgono più campi .

Ma io sto usando POCO Entity Framework 6 classi in modo non c'è nessun metodo OnValidate per ignorare, e da quello che ho letto questo è per LinqToSql, e non riesco a trovare l'evento Validate fanno menzione.

ho cercato di iscriversi al SavingChanges caso di interno ObjectContext nel costruttore della mia DbContext, chiamare manualmente il ValidateEntity, ma non so cosa fare con il risultato. Se lancio uno DbEntityValidationException (o uno ValidationException come suggerito in this article), ASPNET lo gestisce come qualsiasi eccezione (schermo giallo).

L'implementazione di IValidatableObject non funziona.

Ho anche provato attuazione mia DynamicValidator per vedere cosa succede, ma senza successo, sembra per gestire l'eccezione (se sovrascrivo ValidateException, e mettere un punto di interruzione, lo vedo), ma è ancora gorgogliare fino al default gestore degli errori e visualizza una schermata gialla. Devo mancare qualcosa.

Quindi, come eseguire la convalida complessa (cross-field, con query, ecc.) Sulle entità prima di salvare in Dynamic Data/EF?

risposta

0

ho trovato una soluzione che non mi piace, ma funziona:

mio contesto è ancora eseguendo la convalida e lancia un ValidationException se necessario.

Come il ListView non sembra catturare e gestire l'eccezione, lo faccio io stesso gestendo la OnItemUpdated o OnItemInserted caso di ListView:

protected void ListView1_ItemUpdated(object sender, ListViewUpdatedEventArgs e) 
{ 
    if (e.Exception != null) 
    { 
     ValidationError.DisplayError(e.Exception.Message); 
     e.ExceptionHandled = true; 
     e.KeepInEditMode = true; 
    } 
} 

ValidationError viene utilizzato per aggiungere il messaggio di eccezione per la riassunto di convalida. Aggiunge un "falso", validatore sempre fallito con il messaggio.

public class ValidationError : BaseValidator 
{ 
    private ValidationError(string message) 
     : base() 
    { 
     ErrorMessage = message; 
     IsValid = false; 
    } 

    protected override bool EvaluateIsValid() 
    { 
     return false; 
    } 

    public static void DisplayError(string message, string validationGroup) 
    { 
     var currentPage = HttpContext.Current.Handler as Page; 
     currentPage.Validators.Add(new ValidationError(message) { ValidationGroup = validationGroup }); 
    } 
} 
4

Direi che la logica come si sta tentando di eseguire non appartiene a un tale livello nella propria architettura. Lascia che il database applichi i vincoli che si suppone, come le chiavi esterne, ecc. E fai in modo che la tua logica aziendale sia un livello superiore. Ad esempio, sulla tua entità che desideri convalidare potresti aggiungere un metodo IsValidForAddOrUpdate(), che contiene comunque la logica che inseriresti nei tuoi validatori.Poi basta utilizzare i nuovi metodi:

if (entity.IsValidForAddOrUpdate()) 
{ 
    db.Set<Entity>().Add(entity); 
    db.SaveChanges() 
} 
else throw new DbValidationException("Entity failed validation due to rule xyz."); 
+0

sono d'accordo con questa affermazione, ho anche credere che la logica di business non deve essere accoppiato con Entity Framework – Eldho

+1

E 'discutibile. Forse un oggetto del dominio può invece avere la validazione. Finché non viene usato il terribile pattern "repository layer before EF" ... – James

+0

Sono d'accordo, ma Dynamic Data non offre molte opzioni per la convalida. Ma il problema non c'è, il problema è che DbValidationException non viene catturato da DynamicValidator (anche se i documenti e gli articoli che trovo dicono che dovrebbe) così ottengo una schermata gialla. In questo momento non sto nemmeno cercando di avere una buona architettura, solo per avere qualcosa che funzioni :(. –

3

Un modo come raggiungere questo obiettivo potrebbe essere attuando IDataErrorInfo interfaccia sulle proprie entità come questo:

public partial class MyEntity : IDataErrorInfo 
{ 
    public MyEntity() 
    { 
    } 

    ... 

    #region IDataErrorInfo Members 
    public string Error 
    { 
     get { throw new NotImplementedException(); } 
    } 
    public string this[string propertyName] 
    { 
     get 
     { 
      //Custom Validation logic 
      return MyValidator.ValidateProperty(this, propertyName); 
     } 
    } 
    #endregion 
} 

Per accedere DbContext corrente dai metodi IDataErrorInfo è possibile utilizzare this answer. poi sovrascrivere il metodo SaveChanges del contesto:

public override int SaveChanges() 
    { 
     this.ObjectContext.DetectChanges(); 

     // Get all the new and updated objects 
     var objectsToValidate = 
     ChangeTracker.Entries().Where(x => x.State == EntityState.Modified || x.State == EntityState.Added). 
     Select(e => e.Entity); 

     // Check each object for errors 
     foreach (var obj in objectsToValidate) 
     { 
      if (obj is IDataErrorInfo) 
      { 
       // Check each property 
       foreach (var property in obj.GetType().GetProperties()) 
       { 
        var columnError = (obj as IDataErrorInfo)[property.Name]; 
        if (columnError != null) { 
        //Handle your validation errors 
        throw new DbEntityValidationException(columnError); } 
       } 
      } 
     } 

     return base.SaveChanges(); 
    } 

Vedi anche this answer per farlo funzionare con DataAnnotations.

hai scritto:

Se mi passi un DbEntityValidationException (o un ValidationException come suggerito in questo articolo), ASPNET gestirlo come qualsiasi eccezione (schermo giallo).

Vedere this answer. Quando chiami SaveChanges devi catturare DbEntityValidationException (o ValidationException), se non li prendi per elaborarli all'interno del tuo Controller, vengono elaborati dal gestore di errori predefinito.

Oppure è possibile utilizzare DynamicValidator di controllo per catturare ValidationExceptions:

<!-- Capture validation exceptions --> 
    <asp:DynamicValidator ID="ValidatorID" ControlToValidate="GridView1" 
     runat="server" /> 

Il problema con DynamicValidator è che richiede ControlToValidate proprietà e cattura uniche eccezioni provenienti da tale controllo. Anche le eccezioni racchiuse in altre eccezioni possono creare problemi. Esiste una soluzione alternativa: è possibile ereditare da e sovrascrivere il metodo ValidateExceptionsee this blog.

Vedere this article.

+0

Sì, esattamente quello che ho trovato, ma il DynamicValidator non cattura l'eccezione, non so perché. Ho il debug, vedo che entra nel metodo ValidateException e sembra rilevarlo e fare le cose con esso ([codice sorgente] (http://referencesource.microsoft.com/#System.Web.DynamicData/DynamicData/DynamicValidator. cs, a9f61764351be893, riferimenti)), ma l'Eccezione continua comunque con il gestore di errori predefinito Ho anche provato l'ImprovedDynamicValidator dal pacchetto Dynamic Data Futures, ma non funziona neanche. Non riesco a prenderlo nel Controller, in quanto non ce ne sono con Dynamic Data. –

+0

Vedo, problema interessante, ne darò un'occhiata giovedì se non si ottiene risposta nel frattempo. –

Problemi correlati