0

J'ai un problème étrange dans ASP.NET MVC avec des objets ne sont pas mis à jour avec UpdateModel lorsqu'il est passé un FormCollection. UpdateModel ne semble pas fonctionner correctement lorsque l'objet en cours de mise à jour est créé par réflexion.ASP.NET MVC Problème avec l'aide des objets créés de réflexion avec le modèle par défaut Binder

Scénario: J'ai une application qui contient environ 50 tables de recherche - chacune d'entre elles contient exactement le même schéma, y ​​compris les champs typiques tels que id, title, description, isactive et createdon. Plutôt que de construire 50 vues, je voulais avoir une seule vue qui pourrait afficher les données de toutes les tables de recherche. J'ai créé une interface appelée IReferenceEntity et l'ai implémentée dans chacun des POCO représentant mes tables de recherche.

En utilisant cette interface, je suis capable de remplir facilement une vue avec un enregistrement de la table de recherche. (Je passe les articles à la vue par ce qui suit.)

System.Web.Mvc.ViewPage<MyNamespece.IReferenceEntity> 

De la base de données à la vue, tout fonctionne parfaitement. Cependant, lorsque j'essaie de mettre à jour le modèle en post, je rencontre des problèmes.

Si je déclare explicitement une référence d'objet comme celle-ci, tout fonctionne parfaitement et les valeurs de mon objet sont mises à jour avec les valeurs de mon formulaire. Par conséquent, je peux ensuite mettre à jour la base de données. Malheureusement, coder en dur le type d'objet détruirait complètement la raison de l'utilisation d'une interface.

(Un objectif principal de l'application est de pouvoir ajouter dynamiquement de nouvelles tables, telles que des tables de recherche, sans avoir à faire quoi que ce soit de spécial, en réfléchissant sur les assemblages chargés et en localisant interface ou classe de base)

Ma stratégie consiste à déterminer le type concret de l'objet lors de la publication, puis à créer une instance du type par réflexion. (Le mécanisme que j'utilise pour déterminer le type est quelque peu primitif.J'inclue un champ caché dans le formulaire.Les meilleures idées sont les bienvenues.)

Lorsque je crée une instance de l'objet en utilisant la réflexion par l'une des méthodes suivantes, aucun des objets n'est mis à jour par UpdateModel.

Type t = {Magically Determined Type} 

object b = Activator.CreatorInstance(t); 

UpdateModel(b, formCollection.ToValueProvider()); 


Type t = {Magically Determined Type} 

var c = Activator.CreatorInstance(t); 

UpdateModel(c, formCollection.ToValueProvider()); 


Type t = {Magically Determined Type} 

IReferenceEntity d = Activator.CreatorInstance(t); 

UpdateModel(d, formCollection.ToValueProvider()); 

Remarque: J'ai vérifié que les objets qui sont créés par relection sont tous du type approprié.

Est-ce que quelqu'un a une idée de ce qui pourrait se passer? Je suis un peu perplexe.

Si j'étais vraiment "dur", je pourrais créer un objet usine qui instancierait beaucoup l'un de ces objets entité/recherche de référence. Cependant, cela pourrait briser la capacité de l'application à permettre l'ajout et la découverte de nouvelles tables de consultation de façon transparente et n'est pas aussi propre.

En outre, je pourrais essayer de dériver d'une classe de base ReferenceEntity réelle par opposition à une interface, mais je doute que cela fasse une différence. Le problème semble être d'utiliser des objets créés par réflexion dans le modelbinder.

Toute aide est appréciée.

Anthony

Répondre

1

Augi ont répondu à cette sur les forums ASP.NET. Cela a fonctionné avec seulement quelques modifications mineures. Merci Augi.


Le problème est que [Essayez] méthodes de updateModel permettent de spécifier le type de modèle en utilisant le paramètre générique uniquement afin qu'ils ne permettent pas la spécification de type de modèle dynamique. J'ai créé un ticket d'émission pour cela.

Vous pouvez voir l'implémentation de la méthode TryModelUpdate ici. Il n'est donc pas difficile d'écrire sa propre surcharge:

public virtual bool TryUpdateModelDynamic<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties, IDictionary<string, ValueProviderResult> valueProvider) where TModel : class 
{ 
    if (model == null) 
    { 
     throw new ArgumentNullException("model"); 
    } 
    if (valueProvider == null) 
    { 
     throw new ArgumentNullException("valueProvider"); 
    } 


    //Predicate<string> propertyFilter = propertyName => BindAttribute.IsPropertyAllowed(propertyName, includeProperties, excludeProperties); 
    IModelBinder binder = Binders.GetBinder(/*typeof(TModel)*/model.GetType()); 

    ModelBindingContext bindingContext = new ModelBindingContext() 
              { 
               Model = model, 
               ModelName = prefix, 
               ModelState = ModelState, 
               //ModelType = typeof(TModel), // old 
               ModelType = model.GetType(), 
               // new 
               //PropertyFilter = propertyFilter, 
               ValueProvider = valueProvider 
              }; 
    binder.BindModel(ControllerContext, bindingContext); 
    return ModelState.IsValid; 
} 
0

Est-ce que votre IReferenceEntity contiennent setters sur les propriétés ainsi que getters? Je penserais que le dernier exemple fonctionnerait si l'interface avait des setters de propriété, bien que vous deviez le lancer pour le compiler.

Type t = {Magically Determined Type} 

IReferenceEntity d = Activator.CreatorInstance(t) as IReferenceEntity; 

UpdateModel(d, formCollection.ToValueProvider()); 

Normalement, la raison pour laquelle il ne sera pas définir une propriété sur une classe est parce qu'il ne peut pas trouver une méthode setter publique disponible à utiliser par réflexion.

+0

Nous vous remercions de votre réponse. Malheureusement, UpdateModel et TryUpdateModel ne prennent pas en charge les paramètres saisis dynamiquement dans la boîte. La solution d'Augi a surmonté la limitation. Merci d'avoir pris le temps de m'aider! –

0

Juste une « autre chose à essayer » rapide:

UpdateModel(d as IReferenceEntity, formCollection.ToValueProvider()); 

Je ne sais pas si cela fonctionnera, et je n'ai pas essayé moi-même, mais c'est la première chose qui vient à l'esprit.

Si je reçois une chance plus tard, je vais coup d'oeil au modèle par défaut Code Binder et voir s'il y a quelque chose là-dedans qui est évident ...

+0

Cela n'a pas vraiment fonctionné comme je l'ai mentionné dans ma réponse. Cependant, j'apprécie vraiment que vous preniez le temps de répondre. Je vous remercie! –