Question

J'utilise ASP.NET MVC 2.0 et j'essaie de tirer parti de la liaison de modèle dans mon contrôleur et de la validation de modèles de modèle. Cependant, j'ai monté un problème et je voulais le partager avec des gens ici pour voir ce que vous pensez.

OK, j'ai mon utilisateur propre POCO dans ma bibliothèque de classe modèle ...

namespace Model
{    
    public partial class User
    {
        public virtual int Id { get; private set; }
        public virtual string UserName { get; private set; }
        public virtual string DisplayName { get; set; }
        public virtual string Email { get; set; }

        public User(string displayName, string userName)
            : this()
        {
            DisplayName = displayName;
            UserName = userName;
        }
    }
}

La conception que je suis allé uniquement pour permettre la modification de certaines propriétés, une fois que l'objet a été construit. Le nom d'utilisateur par exemple ne peut être défini que lorsque l'objet est construit, cela fait que cela fait de OO Sense, mais est la clé de mon problème, alors je voulais donc la mettre en évidence ici.

J'ai alors une "classe d'amis" qui définit les métadonnées de validation pour ma classe d'utilisateurs ...

namespace Model
{
[MetadataType(typeof(UserMetadata))]
public partial class User
{
    class UserMetadata
    {
        [Required]
        public virtual int Id { get; set; }

        [Required]
        public virtual string UserName { get; set; }

        [Required]
        public virtual string DisplayName { get; set; }

        [RegularExpression(@"^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$", ErrorMessage = "Invalid address")]
        public virtual string Email { get; set; }
    }
}

}

Puis dans ma couche Web, je souhaite permettre à mes utilisateurs de pouvoir modifier cet objet. Donc, j'ai les deux méthodes d'action suivantes dans mon contrôleur de profil.

namespace Web.Controllers
{
    public class ProfileController : Controller
    {
        [Authorize]
        public ActionResult Edit()
        {
            var user = _session.Single<User>(x => x.UserName == HttpContext.User.Identity.Name );
            return View(user);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize]
        [TransactionFilter]
        public ActionResult Edit(User updatedUser)
        {
            // Get the current user to update.
            var user = _session.Single<User>(x => x.UserName == HttpContext.User.Identity.Name);

            if (ModelState.IsValid)
            {
                TryUpdateModel(user);
                // Update store...                
            }
            return View(updatedUser);
        }
    }
}

Ceci a une vue fortement typée pour aller avec elle ...

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Model.User>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Edit
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <%=Html.Script("jquery.validate.js")%>
    <%=Html.Script("MicrosoftMvcJQueryValidation.js")%>
    <%=Html.Script("MvcFoolproofJQueryValidation.js")%>
    <div class="container">
        <div class="column span-14">
        <% using (Html.BeginForm()) {%>
            <%= Html.AntiForgeryToken() %>
            <fieldset>
                <%: Html.DisplayFor(model => model.UserName) %>
                <%= Html.Label("Display Name") %>
                <%= Html.HiddenFor(model => model.DisplayName)%>
                <%= Html.ValidationMessageFor(model => model.DisplayName)%>
                <%= Html.Label("Email address") %>
                <%= Html.EditorFor(model => model.Email)%>
                <%= Html.ValidationMessageFor(model => model.Email)%>
                <%= Html.HiddenFor(model => model.UserName)%>
                <p>
                    <input type="submit" value="Save" />
                </p>
            </fieldset>
        </div>
        <div class="clear"></div>
        <% } %>
    </div>
</asp:Content>

ok donc c'est tout le code sur la voie !!

Voici donc le problème, la vue est rendue bien après la demande initiale d'obtenir. Mais lorsque l'utilisateur publie le formulaire de retour, dites après l'édition de leur nom d'affichage, le modèle de modèle n'est pas valide. En effet, la propriété Nom d'utilisateur dispose d'un setter privé. Cependant, cela est par conception, pour la sécurité et la sémantique, je ne voulais jamais qu'ils changent de nom d'utilisateur, de sorte que le setter est privé. Cependant, comme je l'ai ajouté l'attribut requis à la propriété, il échoue car il n'est pas défini!

La question est de savoir si la modélisation doit signaler cela comme une erreur de validation ou non ?! Comme la propriété est privée, j'ai conçu pour ne pas être défini, par conséquent, par design, je ne m'attends pas à ce que le classeur de modèle soit défini, mais je ne veux pas d'une erreur de validation. Je pense qu'il ne devrait produire que des erreurs de validation pour les propriétés qu'il peut définir.

OK SUBS POSSIBLES SOLUTIONS QUE JE JOUE AVEZ JUSTE ..

rendre la propriété publique.

Si je fais cela, je m'ouvre pour permettre au nom d'utilisateur d'être modifié pour les utilisateurs existants. Je devrais ajouter une logique supplémentaire quelque part pour attraper cela, pas vraiment très gentil. Je devrais également ajouter une obligation d'exclure sur la méthode d'action pour empêcher les méchantes personnes qui essaient de la définir via un post.

Supprimer l'erreur

Je crois que je peux supprimer l'erreur du dictionnaire de modèles de modèle, tout irait à cette occasion, mais je pense que cela introduira une odeur de code, car je devrais ajouter ceci pour tous mes objets comportant des seigtisseurs privés. Je voudrais probablement oublier !!

Tapez fortement ma vue contre une interface

J'ai lu que certaines personnes lient leur vision à une interface de leur modèle, c'est roi d'une interface modèleView sur l'objet Model Business. J'aime cette idée, mais je perds la liaison automatique et je devrais dupliquer mes objets de modèle avec leurs constructeurs de ma couche Web, pas sûr de cela ?! Quelques informations sur cela ici http://www.codethinked.com/post/2010/04/12/easy-and-safe-model-binding-in-aspnet-mvc.aspx .

Utiliser des vues de modèle

Cela ne semble pas sèche pour moi ?! Je suis heureux de les utiliser si je n'ai pas d'objet de modèle existant qui convient (par exemple, j'utilise une vue de modèle d'inscription).

Customotomodalbinder

Mon option préférée, mais je ne suis pas sûr de savoir ce que je fais !! Si je pouvais simplement obtenir le classeur pour ne pas se lier à des propriétés qu'il peut définir, alors je risquerais de rire !!

Qu'est-ce que les gens pensent? Commentaires sur les options ci-dessus, toutes les autres solutions, suis-je juste à côté de la marque avec mon architecture ?!

Merci:)

Était-ce utile?

La solution 3

JFAR a posté un bon lien vers un message de Brad Wilson où Brad Commentaires ...

Vous pouvez toujours faire une édition partielle, mais Vous ne pouvez pas faire de validation partielle Suite.Donc, si vous excluez la liaison quelque chose avec le [Obligatoire] Attribut, alors la validation échouera. Vous avez quelques choix pour travailler autour Ceci:

  • Utilisez un modèle de vue qui reflète exactement les données de formulaire

  • Pré-remplissez [requis] mais des champs non liés avec des données avant d'appeler (Essayer) updateemodel pour que le la validation va réussir (même si Vous n'avez rien l'intention de faire quoi que ce soit avec ces données)

  • Autoriser les erreurs de validation à se produire, puis retirez-les de ModelState après la validation est terminée, Comme ils sont des erreurs inappropriées.

Mon cas semble s'intégrer dans le cas "montage partiel", où je ne veux pas que certains champs soient mis à jour.

Je vais examiner ces solutions.

Autres conseils

" j'ai conçu pour ne pas être défini, par conséquent, par design, je ne m'attends pas à ce que le classeur de modèle soit défini, mais je ne veux pas d'erreur de validation. Je pense que cela ne devrait produire que la validation erreurs pour les propriétés qu'il peut définir. "

En savoir plus sur cette décision de conception ici:

HTTP : //bradwilson.typepad.com/blog/2010/01/input-validation-vs-model-validation-in-aspnet-mvc.html

Fait intéressant, la plupart des gens se sont plaints le contraire complet de ce que vous vous plaignez. ;)

Vous dites essentiellement au système que quelque chose qui ne peut pas être défini devrait toujours être défini. Donc, je ne dirais pas que MVC fonctionne de manière incorrecte ou quelque chose comme ça. Votre juste codage d'un scénario impossible.


Dans l'ensemble, vous arrivez simplement aux points de douleur de la technique de métadatabuddy. Principalement la nécessité d'avoir une validation différente pour les nouveaux scénarios d'édition.

" Si je fais cela, je m'ouvre pour permettre au nom d'utilisateur d'être modifié pour l'utilisateur existant. Je devrais ajouter une logique supplémentaire quelque part pour attraper cela, pas vraiment très gentil. Je devrais aussi ajouter un Bind Exclure sur la méthode d'action pour arrêter les méchantes personnes qui essaient de la définir via un post. "

IMHO Votre trop manger à ces changements de code. Vous ajouteriez une chaîne simple à un seul appel de méthode. Quel est le gros problème? Je prendrais l'approche pragmatique ici.

J'utiliserais un modèle de vue car c'est le meilleur ajustement pour le travail.Ne pense pas au sens sec, vous ne pouvez pas répéter les propriétés sur deux objets, pensez-y comme «ne pas dupliquer la logique ou persistez des données identiques à deux endroits».Dans ce cas, la sémantique du traitement de la liaison de modèle ne correspond pas à votre modèle de domaine, vous devez donc avoir besoin d'un moyen de le traduire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top