Domanda

Sto usando ASP.NET MVC 2.0 e sto cercando di sfruttare il binding del modello nel mio controller e anche la convalida del modello. Comunque sono venuto in mente un problema e volevo condividerlo con le persone qui per vedere cosa ne pensi.

OK Ho il mio utente pulito Poco nella mia biblioteca di classe modello ...

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;
        }
    }
}
.

Il design che sono andato per consentire solo alcune proprietà da modificare, dopo che l'oggetto è stato costruito. Il nome utente per esempio può essere impostato solo quando l'oggetto è costruito, a me questo rende oo senso, ma è la chiave del mio problema, quindi volevo evidenziarlo qui.

Ho quindi una "classe di amici" che definisce i metadati di convalida per la mia classe utente ...

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; }
    }
}
.

}

Allora nel mio strato web voglio consentire ai miei utenti di poter modificare questo oggetto. Quindi ho i seguenti due metodi di azione nel mio controllore del profilo.

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

Questo ha una vista fortemente digitata per andare con esso ...

<%@ 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 Quindi questo è tutto il codice fuori il modo !!

Quindi ecco il problema, la vista è resa fine dopo la richiesta iniziale di ottima. Ma quando l'utente pubblica il modulo, ad esempio dopo aver modificato il nome del display, il modello non è valido. Questo perché la proprietà del nome utente ha un setter privato su di esso. Tuttavia questo è per progettazione, per la sicurezza e la semantica non li voglio mai che cambino il loro nome utente, quindi il setter è privato. Tuttavia, poiché ho aggiunto l'attributo richiesto alla proprietà, è fallito in quanto non è impostato!

La domanda è che il modellismo sia segnalato questo come errore di convalida o no?! Poiché la proprietà è privata, ho progettato per non essere impostato, quindi dal design non mi aspetto che il raccoglitore del modello sia di definirlo, ma non voglio un errore di convalida. Penso che dovrebbe solo produrre errori di convalida per le proprietà che può impostare.

OK così possibili soluzioni che ho trovato così lontano ..

Fai la proprietà pubblica.

Se lo faccio mi apro fino a consentire al nome utente di essere modificato per gli utenti esistenti. Dovrei aggiungere logica extra da qualche parte per catturare questo, non proprio molto bello. Dovrei anche aggiungere un legame escluso sul metodo di azione per fermare le persone cattive che cercano di impostarlo tramite un post.

Rimuovi l'errore

Credo di poter rimuovere l'errore dal dizionario ModelState, questo andrebbe bene in questa occasione, ma penso che questo introdurrà qualche odore di codice, come dovrei aggiungere questo per tutti i miei oggetti che hanno setter privato. Probabilmente vorrei dimenticare !!

Digita fortemente la mia vista contro un'interfaccia

Ho letto che alcune persone legano la loro vista a un'interfaccia del loro modello, questo è il re di un'interfaccia ModelView sull'oggetto del modello di business. Mi piace questa idea, ma perdo la rilegatura automatica e avrei dovuto duplicare i miei oggetti modello con i loro costruttori nel mio strato web, non sono sicuro di questo?! Alcune informazioni su questo qui http://www.codethethedinked.com/post/2010/04/12/easy-and-safe-model-binding-in-aspnet-mvc.aspx .

Usa visualizzazioni del modello

Questo semplicemente non sembra asciutto per me?! Sono felice di usarli se non ho un oggetto modello esistente che si adatta (ad esempio uso una vista modello di iscrizione).

CustomModelbinder

La mia opzione preferita, ma non sono sicuro di sapere cosa sto facendo !! Se potessi solo prendere il legante solo per legare le proprietà, può impostare, allora ridendo !!

Cosa ne pensano le persone? Commenti sulle opzioni di cui sopra, qualsiasi altra soluzione, sono appena fuori dal marchio con la mia architettura?!

Grazie :)

È stato utile?

Soluzione 3

JFAR ha pubblicato un buon collegamento a un post da Brad Wilson dove Brad Comments ...

.

puoi ancora fare modifica parziale, ma Non è possibile effettuare una convalida parziale Di Più.Quindi se escludi il legame qualcosa con il [richiesto] Attributo, quindi la convalida fallirà. Hai alcune scelte per aggirare Questo:

    .
  • Utilizzare un modello di vista che rispecchia esattamente i dati del modulo

  • pre-compilato [richiesto] ma i campi non associati con i dati prima della chiamata (Prova) updatemodel in modo che il La convalida avrà successo (anche se Non intendi fare nulla con Quel dato)

  • Consentire gli errori di convalida si verificano e quindi rimuoverli da ModelState dopo la convalida è fatto, Dal momento che sono errori inappropriati.

Il mio caso sembra adattarsi al caso "Modifica parziale", dove non voglio che alcuni campi siano aggiornati.

Guarderò in questi come soluzioni.

Altri suggerimenti

" ho progettato per non essere impostato per non essere impostato, quindi dal design non mi aspetto che il Binder Model lo possa impostare, ma non voglio un errore di convalida. Penso che dovrebbe produrre validazione solo Errori per le proprietà che possono impostare. "

Leggi di più su questa decisione di progettazione qui:

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

Interessantemente la maggior parte delle persone si è lamentata il completo opposto di ciò che si lamentava. ;)

Il tuo fondamentalmente dice al sistema che qualcosa che non può essere impostato deve essere sempre impostato. Quindi non direi MVC funziona in modo errato o niente del genere. Stai solo codificando uno scenario impossibile.


.

Nel complesso il tuo solo raggiungendo i punti del dolore della tecnica metadatabocco. Principalmente la necessità di avere una convalida diversa per i nuovi e modifica scenari.

" Se lo faccio apro per consentire al nome utente di essere modificato per gli utenti esistenti. Dovrei aggiungere logica extra da qualche parte per catturare questo, non proprio molto bello. Dovrei anche aggiungere un Legare escludere il metodo di azione per fermare eventuali persone cattive che cercano di impostarlo tramite un post. "

IMHO il tuo eccezzorimento a questi cambiamenti di codice. Ti aggiungeresti una semplice stringa a una singola chiamata del metodo. Qual è il grosso problema? Prenderò l'approccio pragmatico qui.

Utilizzerei un modello di vista perché è la migliore vestibilità per il lavoro.Non pensare a secco che significa che non puoi ripetere le proprietà su due oggetti, pensarlo come "Non duplicare la logica o persistono dati identici in due luoghi".In questo caso, la semantica di affrontare il binding del modello non è abbinata al tuo modello di dominio in modo da avere un modo per tradurlo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top