Comment valider mon modèle personnalisé dans un modèle de classeur?

J'ai demandé au sujet d'un problème que j'ai avec délimité par des virgules, des valeurs numériques ici.

Donné certaines réponses, j'ai tenté d'essayer de mettre en œuvre mon propre modèle de classeur comme suit:

namespace MvcApplication1.Core
{
    public class PropertyModelBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            object objectModel = new object();

            if (bindingContext.ModelType == typeof(PropertyModel))
            {
                HttpRequestBase request = controllerContext.HttpContext.Request;
                string price = request.Form.Get("Price").Replace(",", string.Empty);

                ModelBindingContext newBindingContext = new ModelBindingContext()
                {
                    ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(
                        () => new PropertyModel() 
                        {
                            Price = Convert.ToInt32(price)
                        },
                        typeof(PropertyModel)       
                    ),
                    ModelState = bindingContext.ModelState,
                    ValueProvider = bindingContext.ValueProvider
                };

                //call the default model binder this new binding context
                return base.BindModel(controllerContext, newBindingContext);
            }
            else
            {
                return base.BindModel(controllerContext, bindingContext);
            }
        }

        //protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
        //{
        //   //return base.CreateModel(controllerContext, bindingContext, modelType);
        //   PropertyModel model = new PropertyModel();

        //   if (modelType == typeof(PropertyModel))
        //   {
        //       model = (PropertyModel)base.CreateModel(controllerContext, bindingContext, modelType);
        //       HttpRequestBase request = controllerContext.HttpContext.Request;
        //       string price = request.Form.Get("Price").Replace(",", string.Empty);
        //       model.Price = Convert.ToInt32(price);
        //   }

        //   return model;
        //}
    }
}

Et mise à jour de mon contrôleur de classe comme ceci:

namespace MvcApplication1.Controllers
{
    public class PropertyController : Controller
    {
        public ActionResult Edit()
        {
            PropertyModel model = new PropertyModel
            {
                AgentName = "John Doe",
                BuildingStyle = "Colonial",
                BuiltYear = 1978,
                Price = 650000,
                Id = 1
            };

            return View(model);
        }

        [HttpPost]
        public ActionResult Edit([ModelBinder(typeof(PropertyModelBinder))] PropertyModel model)
        {
            if (ModelState.IsValid)
            {
                //Save property info.              
            }

            return View(model);
        }

        public ActionResult About()
        {
            ViewBag.Message = "Your app description page.";

            return View();
        }

        public ActionResult Contact()
        {
            ViewBag.Message = "Your contact page.";

            return View();
        }
    }
}

Maintenant, si je m'indiquer le prix avec des virgules, mon modèle de classeur de supprimer les virgules, c'est ce que je veux, mais la validation ne se fait pas. Donc, la question est: Comment faire une validation personnalisée dans mon classeur de modèles personnalisées telles que la capture de la valeur des prix avec des virgules peuvent être évités? En d'autres termes, je pense que j'ai besoin de faire plus dans mon classeur de modèles personnalisées, mais ne savent pas comment et quoi. Merci.Comment valider mon modèle personnalisé dans un modèle de classeur?

Mise à jour:

Donc, j'ai essayé @mare solution à https://stackoverflow.com/a/2592430/97109 et mis à jour mon classeur de modèles comme suit:

namespace MvcApplication1.Core
{
    public class PropertyModelBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            object objectModel = new object();

            if (bindingContext.ModelType == typeof(PropertyModel))
            {
                HttpRequestBase request = controllerContext.HttpContext.Request;
                string price = request.Form.Get("Price").Replace(",", string.Empty);

                ModelBindingContext newBindingContext = new ModelBindingContext()
                {
                    ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(
                        () => new PropertyModel() 
                        {
                            Price = Convert.ToInt32(price)
                        },
                        typeof(PropertyModel)    
                    ),
                    ModelState = bindingContext.ModelState,
                    ValueProvider = bindingContext.ValueProvider
                };

                //call the default model binder this new binding context
                object o = base.BindModel(controllerContext, newBindingContext);
                newBindingContext.ModelState.Remove("Price");
                newBindingContext.ModelState.Add("Price", new ModelState());
                newBindingContext.ModelState.SetModelValue("Price", new ValueProviderResult(price, price, null));
                return o;
            }
            else
            {
                return base.BindModel(controllerContext, bindingContext);
            }
        }

        //protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
        //{
        //   //return base.CreateModel(controllerContext, bindingContext, modelType);
        //   PropertyModel model = new PropertyModel();

        //   if (modelType == typeof(PropertyModel))
        //   {
        //       model = (PropertyModel)base.CreateModel(controllerContext, bindingContext, modelType);
        //       HttpRequestBase request = controllerContext.HttpContext.Request;
        //       string price = request.Form.Get("Price").Replace(",", string.Empty);
        //       model.Price = Convert.ToInt32(price);
        //   }

        //   return model;
        //}
    }
}

Il sorta fonctionne, mais si j'ai entrez 0 pour le prix, le modèle est de retour en tant que valide, ce qui est faux parce que j'ai une Gamme d'annotation qui dit que le prix minimal est de 1. À mon wit's end.

Mise à jour:

Afin de tester un modèle de classeur avec des types de composé. J'ai créé le point de vue suivant des classes de modèle:

using System.ComponentModel.DataAnnotations;

namespace MvcApplication1.Models
{
    public class PropertyRegistrationViewModel
    {
        public PropertyRegistrationViewModel()
        {

        }

        public Property Property { get; set; }
        public Agent Agent { get; set; }
    }

    public class Property
    {
        public int HouseNumber { get; set; }
        public string Street { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string Zip { get; set; }

        [Required(ErrorMessage="You must enter the price.")]
        [Range(1000, 10000000, ErrorMessage="Bad price.")]
        public int Price { get; set; }
    }

    public class Agent
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        [Required(ErrorMessage="You must enter your annual sales.")]
        [Range(10000, 5000000, ErrorMessage="Bad range.")]
        public int AnnualSales { get; set; }

        public Address Address { get; set; }
    }

    public class Address
    {
        public string Line1 { get; set; }
        public string Line2 { get; set; }
    }
}

Et voici le contrôleur:

using MvcApplication1.Core;
using MvcApplication1.Models;
using System.Web.Mvc;

namespace MvcApplication1.Controllers {
    public class RegistrationController : Controller
    {
        public ActionResult Index() {
            PropertyRegistrationViewModel viewModel = new PropertyRegistrationViewModel();
            return View(viewModel);
        }

        [HttpPost]
        public ActionResult Index([ModelBinder(typeof(PropertyRegistrationModelBinder))]PropertyRegistrationViewModel viewModel)
        {
            if (ModelState.IsValid)
            {
                //save registration.
            }

            return View(viewModel);
        }
    }
}

Ici est le modèle de classeur de mise en œuvre:

using MvcApplication1.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcApplication1.Core
{
public class PropertyRegistrationModelBinder : DefaultModelBinder
{
protected override object GetPropertyValue(
ControllerContext controllerContext,
ModelBindingContext bindingContext,
System.ComponentModel.PropertyDescriptor propertyDescriptor,
IModelBinder propertyBinder)
{
if (propertyDescriptor.ComponentType == typeof(PropertyRegistrationViewModel))
{
if (propertyDescriptor.Name == "Property")
{  
var price = bindingContext.ValueProvider.GetValue("Property.Price").AttemptedValue.Replace(",", string.Empty);
var property = new Property();
//Question 1: Price is the only property I want to modify. Is there any way 
//such that I don't have to manually populate the rest of the properties like so?
property.Price = string.IsNullOrWhiteSpace(price)? 0: Convert.ToInt32(price);
property.HouseNumber = Convert.ToInt32(bindingContext.ValueProvider.GetValue("Property.HouseNumber").AttemptedValue);
property.Street = bindingContext.ValueProvider.GetValue("Property.Street").AttemptedValue;
property.City = bindingContext.ValueProvider.GetValue("Property.City").AttemptedValue;
property.State = bindingContext.ValueProvider.GetValue("Property.State").AttemptedValue;
property.Zip = bindingContext.ValueProvider.GetValue("Property.Zip").AttemptedValue;
//I had thought that when this property object returns, our annotation of the Price property
//will be honored by the model binder, but it doesn't validate it accordingly.
return property;
}
if (propertyDescriptor.Name == "Agent")
{
var sales = bindingContext.ValueProvider.GetValue("Agent.AnnualSales").AttemptedValue.Replace(",", string.Empty);
var agent = new Agent();
//Question 2: AnnualSales is the only property I need to process before validation,
//Is there any way I can avoid tediously populating the rest of the properties?
agent.AnnualSales = string.IsNullOrWhiteSpace(sales)? 0:  Convert.ToInt32(sales);
agent.FirstName = bindingContext.ValueProvider.GetValue("Agent.FirstName").AttemptedValue;
agent.LastName = bindingContext.ValueProvider.GetValue("Agent.LastName").AttemptedValue;
var address = new Address();
address.Line1 = bindingContext.ValueProvider.GetValue("Agent.Address.Line1").AttemptedValue + " ROC";
address.Line2 = bindingContext.ValueProvider.GetValue("Agent.Address.Line2").AttemptedValue + " MD";
agent.Address = address;
//I had thought that when this agent object returns, our annotation of the AnnualSales property
//will be honored by the model binder, but it doesn't validate it accordingly.
return agent;
}
}
return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
}
protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var model = bindingContext.Model as PropertyRegistrationViewModel;
//In order to validate our model, it seems that we will have to manually validate it here. 
base.OnModelUpdated(controllerContext, bindingContext);
}
}
}

Et voici le Rasoir vue:

@model MvcApplication1.Models.PropertyRegistrationViewModel
@{
ViewBag.Title = "Property Registration";
}
<h2>Property Registration</h2>
<p>Enter your property and agent information below.</p>
@using (Html.BeginForm("Index", "Registration"))
{
@Html.ValidationSummary();    
<h4>Property Info</h4>
<text>House Number</text> @Html.TextBoxFor(m => m.Property.HouseNumber)<br />
<text>Street</text> @Html.TextBoxFor(m => m.Property.Street)<br />
<text>City</text> @Html.TextBoxFor(m => m.Property.City)<br />
<text>State</text> @Html.TextBoxFor(m => m.Property.State)<br />
<text>Zip</text> @Html.TextBoxFor(m => m.Property.Zip)<br />
<text>Price</text> @Html.TextBoxFor(m => m.Property.Price)<br /> 
<h4>Agent Info</h4>
<text>First Name</text> @Html.TextBoxFor(m => m.Agent.FirstName)<br />
<text>Last Name</text> @Html.TextBoxFor(m => m.Agent.LastName)<br />
<text>Annual Sales</text> @Html.TextBoxFor(m => m.Agent.AnnualSales)<br />
<text>Agent Address L1</text>@Html.TextBoxFor(m => m.Agent.Address.Line1)<br />
<text>Agent Address L2</text>@Html.TextBoxFor(m => m.Agent.Address.Line2)<br />
<input type="submit" value="Submit" name="submit" />
}

Et c'est ici le mondial.asax fichier où je fil le modèle de liaison personnalisé. BTW, il semble que cette étape n'est pas nécessaire, coz je remarque qu'il fonctionne toujours sans passer par cette étape.

using MvcApplication1.Core;
using MvcApplication1.Models;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace MvcApplication1 {
//Note: For instructions on enabling IIS6 or IIS7 classic mode, 
//visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication {
protected void Application_Start() {
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
ModelBinders.Binders.Add(typeof(PropertyRegistrationViewModel), new PropertyRegistrationModelBinder());
}
}
}

Peut-être que je fais quelque chose de mal ou pas assez. J'ai remarqué que les problèmes suivants:

  1. Bien que je n'ai besoin de modifier le Prix de la valeur de la propriété de l'objet, il semble que j'ai à faire remplir toutes les autres propriétés dans le modèle de classeur. Je dois faire de même pour le AnnualSales propriété de l'agent de la propriété. Est-il de toute façon cela peut être évité dans le modèle de classeur?
  2. Je pensais que par défaut, la méthode BindModel sera l'honneur de notre annotation de nos propriétés d'objets et de les valider en conséquence, après il appelle GetPropertyValue, mais il ne le fait pas. Si je entrer une valeur moyen de sortir de gamme pour le Prix de la Propriété de l'objet ou de la AnnualSales de l'Agent de l'objet, le modèle est de retour comme valide. En d'autres termes, la Gamme des annotations sont ignorés. Je sais que je peux les valider par des raisons impérieuses OnModelUpdated dans le modèle de classeur, mais c'est trop de travail, et en plus, j'ai les annotations en place, pourquoi ne pas le défaut de mise en œuvre du modèle de classeur honneur juste parce que je suis primordial?

@dotnetstep: Pourriez-vous jeter un certain éclairage sur cette? Merci.

Pourriez-vous montrer les annotations de données utilisées sur le Prix de la propriété?
stackoverflow.com/questions/23098059/...
avez-vous essayé ceci: stackoverflow.com/questions/2588588/...
Oui, j'ai juste essayé la solution de @mare. Il sorta travaillé. Mais alors, si j'ai entrez 0 pour le prix, le modèle est de retour en tant que valide, ce qui est faux parce que j'ai une Gamme d'annotation qui dit le prix minimum est de 1. Hmm, frustrant.

OriginalL'auteur Stack0verflow | 2014-04-18