2016-04-28 2 views
12

Je cherche une solution pour effectuer une validation d'entité personnalisée (ce qui nécessiterait un accès à la base de données, une validation croisée ...) lorsque l'utilisateur enregistre ses modifications dans un écran de données dynamiques, avec Entity Framework.
La validation est plus complexe que ce que je peux faire avec les attributs (il faut un accès à la base de données, etc.)Validation d'entité avancée personnalisée avec Dynamic Data

Pouvez-vous intercepter l'appel SaveChanges?
J'ai essayé de surcharger ValidateEntity dans l'objet DbContext, mais Dynamic Data ne semble pas l'appeler (probablement parce qu'il utilise l'ObjectContext interne, je ne sais pas pourquoi), et l'annulation de SaveChanges n'aide pas non plus.
Je ne vois pas un événement que je pourrais souscrire à ...

Le documentation devrait aider:

validation Personnaliser pour un champ de données individuelles en remplaçant la méthode OnValidate ou traiter l'événement Valider , qui sont appelées lorsqu'un champ de données est modifié. Cette approche vous permet d'ajouter la validation et la logique métier pour un champ individuel. Cette approche est plus générale que l'ajout de validation pour un champ individuel. Il est utile lorsque la même logique de validation peut être appliquée à plusieurs champs de données . Il vous permet également d'effectuer des vérifications de validation qui impliquent plusieurs champs.

Mais j'utilise POCO Entity Framework 6 classes donc il n'y a pas de méthode OnValidate pour passer outre, et de ce que je lis ceci est pour LinqToSql, et je ne peux pas trouver l'événement Validate qu'ils mentionnent.

J'ai essayé de souscrire à l'événement SavingChanges du ObjectContext intérieur dans le constructeur de mon DbContext, appeler manuellement le ValidateEntity, mais je ne sais pas quoi faire avec le résultat. Si je lance un DbEntityValidationException (ou un ValidationException comme suggéré dans this article), ASPNET le gère comme n'importe quelle exception (écran jaune). La mise en œuvre de IValidatableObject ne fonctionne pas non plus. J'ai également essayé d'implémenter mon propre DynamicValidator pour voir ce qui se passe, mais sans succès, il semble gérer l'exception (si je remplace ValidateException, et mettre un point d'arrêt, je le vois) mais il est toujours passé au niveau par défaut gestionnaire d'erreurs et affiche un écran jaune. J'ai dû louper quelque chose. Alors, comment effectuer une validation complexe (cross-field, avec requêtes, etc.) sur des entités avant d'enregistrer dans Dynamic Data/EF?

Répondre

0

Je trouve une solution de contournement Je n'aime pas, mais il fonctionne:

Mon contexte exécute encore la validation et jette un ValidationException si nécessaire.

Comme le ListView ne semble pas attraper et gérer l'exception, je le fais moi-même en manipulant l'événement OnItemUpdated ou OnItemInserted du 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 est utilisé pour ajouter le message d'exception à la résumé de validation. Il ajoute un "faux", toujours un validateur défaillant avec le message.

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

Je dirais que la logique comme celle que vous essayez de réaliser n'appartient pas à un tel niveau dans votre architecture. Laissez la base de données appliquer les contraintes auxquelles elle est supposée, comme les clés étrangères, etc., et ayez une couche supérieure dans votre logique métier. Par exemple, sur votre entité que vous souhaitez valider, vous pouvez ajouter une méthode IsValidForAddOrUpdate(), qui contient la logique que vous avez mise dans vos validateurs de toute façon.Ensuite, il suffit d'utiliser les nouvelles méthodes:

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

Je suis d'accord avec cette affirmation, je Beleive aussi que la logique métier ne doit pas être couplé avec le cadre de l'entité – Eldho

+1

Il peut soutenir. Peut-être qu'un objet de couche de domaine pourrait avoir la validation à la place. Tant que l'horrible 'couche de dépôt devant le modèle EF' n'est pas utilisée ... – James

+0

Je suis d'accord, mais Dynamic Data ne donne pas beaucoup d'options pour la validation. Mais le problème n'est pas là, le problème est que DbValidationException n'est pas attrapé par DynamicValidator (bien que les documents et les articles que je trouve disent qu'il devrait) donc j'obtiens un écran jaune. En ce moment je n'essaie même pas d'avoir une bonne architecture, juste pour avoir quelque chose qui fonctionne :(. –

3

Une façon comment ACHIVE cela pourrait être implémentant l'interface IDataErrorInfo sur vos entités comme ceci:

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 
} 

Pour accéder DBContext courant des méthodes IDataErrorInfo vous pouvez utiliser this answer. outrepasser ensuite SaveChanges de votre contexte de la méthode:

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

Voir aussi this answer pour le faire fonctionner avec DataAnnotations.

Vous avez écrit:

Si je jette un DbEntityValidationException (ou un ValidationException comme suggéré dans cet article), le réSEAU gérer comme une exception (écran jaune).

Voir this answer. Lorsque vous appelez SaveChanges vous devez attraper DbEntityValidationException (ou ValidationException), si vous ne les attrapez pas pour les traiter dans votre contrôleur, ils sont traités par le gestionnaire d'erreurs par défaut.

Ou vous pouvez utiliser DynamicValidator contrôle pour attraper ValidationExceptions:

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

Le problème avec DynamicValidator est qu'il exige ControlToValidate la propriété et il ne saisit que des exceptions à venir de ce contrôle. De plus, les exceptions encapsulées dans d'autres exceptions peuvent créer des problèmes. Il existe une solution de contournement - vous pouvez hériter de DynamicValidator et remplacer sa méthode ValidateExceptionsee this blog.

Voir this article.

+0

Oui, exactement ce que j'ai trouvé, mais le DynamicValidator ne saisit pas l'exception, je ne sais pas pourquoi. Je débogue, je vois que cela marche dans la méthode ValidateException et il semble la détecter et faire des choses avec (code source) (http://referencesource.microsoft.com/#System.Web.DynamicData/DynamicData/DynamicValidator. cs, a9f61764351be893, references)), mais l'exception va toujours au gestionnaire d'erreur par défaut J'ai même essayé l'ImprovedDynamicValidator à partir du package Dynamic Data Futures, mais cela ne fonctionne pas non plus. Contrôleur, comme il n'y en a pas avec Dynamic Data –

+0

Je vois, problème intéressant, je vais y jeter un coup d'oeil jeudi si on ne répond pas entre temps. –