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.
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:
- 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?
- 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.
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
Vous devez vous connecter pour publier un commentaire.
Espère que Cela Aidera.
Vous pouvez également essayer @Ryan solution.
Ce pourrait être votre Personnalisé ModelBinder. ( Dans ce cas, vous n'avez pas besoin de mettre à jour votre Action Edit Suite Comme je l'ai suggéré ci-dessus)
Que vous avez mis à jour votre portée pour la liaison. J'ai fourni ma suggestion dans les commentaires. Aussi, si vous utilisez ModelBinder de la Propriété et de l'Agent que vous pouvez faire comme ceci.
Aussi, je tiens à dire que vous êtes en mesure de trouver de nombreuses informations liées à cela et aussi vous pouvez faire la même chose de plusieurs façons. Comme si vous présentez un nouvel attribut qui s'appliquent à la classe de la propriété pour la liaison même manière que vous appliquez ModelBinder au niveau de la classe.
Je me rends compte que bindingContext.ValueProvider.GetValue(clé) ne peut récupérer la valeur d'une propriété d'un type simple. Si je définis l'agent immobilier sa propre classe comme Agent public { public string Prenom {get; set; } public string Nom{get; set; } public string PhoneNumber {get; set; } } et mon PropertyModel aurait cette propriété: publique Agent{get; set; }. Supposons que je souhaite également modifier agent premières infos dans le formulaire de collecte dans GetPropertyValue, comment puis-je reconstruire l'objet d'Agent à l'intérieur de GetPropertyValue? Merci.
Je voudrais suggérer que vous devriez avoir à utiliser modelbinder de la Propriété et de l'Agent de la classe plutôt que CompositeViewModel.
Vous n'avez pas à le faire. Même dans la mise à jour de votre échantillon que vous avez configurés dans le Mondial.asax donc il n'est pas nécessaire de préciser dans la méthode de l'Indice de nouveau. En dehors de cela, il ya une autre façon d'appliquer le liant et c'est grâce à l'attribut de la classe.
Même que j'ai mis à jour ma réponse si vous voulez chercher.
OriginalL'auteur dotnetstep
J'ai reçu votre validation fonctionne très bien par changer lorsque
BindModel
incendies. Dans votre code, vous avez ces lignes dansPropertyModelBinder
:J'ai déménagé
base.BindModel
à feu immédiatement avant de retourner l'objet (après la reconstruction de contexte) et maintenant la validation fonctionne comme prévu. Voici le nouveau code:OriginalL'auteur RyanCJI
Ce n'est pas exactement la réponse à votre question, mais je vais le mettre comme une réponse de toute façon parce que je pense qu'il traite de ce que les gens ont de vous demander de vous au sujet de votre précédente question.
Dans ce cas particulier, il sonne vraiment comme si vous voulez une chaîne de caractères. Oui, je sais, les valeurs sont, en fin de compte, numérique (entier, semble-t-il des valeurs, mais quand vous travaillez avec quelque chose comme MVC, vous devez vous rappeler que les modèles qui sont utilisés par votre point de vue ne doivent pas correspondre à la modèles qui font partie de votre logique métier. Je pense que vous êtes en cours d'exécution dans cette question parce que vous tentez de mélanger les deux.
Au lieu de cela, je recommande la création d'un modèle (un ViewModel) qui est spécifiquement pour la Vue que vous êtes l'affichage à l'utilisateur final. Ajouter les diverses annotations de données qui permettra de valider la CHAÎNE qui constitue le Prix. (Vous pouvez facilement le faire tout de validation que vous souhaitez par un simple expression régulière annotation de données. Ensuite, une fois que le modèle est en fait soumis à votre contrôleur (ou ce que d'autres données est soumis à votre contrôleur) et vous savez qu'il est valide (via l'annotation de données), vous pouvez ensuite convertir en un nombre entier que vous souhaitez pour le modèle que vous utilisez avec votre logique métier, et à l'utiliser comme vous le feriez (un nombre entier).
De cette façon, vous éviter d'avoir toute cette complexité inutile que vous avez demandé (qui n'ont de solution, mais il ne rentre pas vraiment l'état d'esprit derrière MVC) et vous permet d'atteindre la flexibilité que vous recherchez dans votre point de vue avec l'exigence stricte dans une logique d'entreprise.
J'espère que ça a du sens. Vous pouvez rechercher sur le web Viewmodel dans MVC. Un bon endroit pour commencer est le ASP.NET MVC tutoriels.
On dirait que vous avez déjà trouvé une réponse à ce que vous recherchez, donc c'est bien. Toutefois, je ne recommande vraiment comprendre le problème ci-dessus (suivre le lien que j'ai fourni pour le tutoriel, car il a un bon exemple de ce que vous aurez besoin de faire ce que je suis suggestion).
Merci pour le suivi. Mais je ne suis pas clair comment cela magasin de musique tutoriel est liée à la question que j'ai voulu résoudre, et la façon dont je veux qu'il soit résolu.
C'est le problème, ma solution ne résoudrait pas ce que tu voulais résoudre le problème (c'est pourquoi ma mise en garde a été le début de ma réponse). Au lieu de cela, j'ai été suggestion relooker à votre conception en tant que ce que vous êtes en train de faire, alors que vous avez à travailler, ne peuvent pas être la meilleure approche. Le tutoriel vous montre comment construire que par la conception et préoccupations distinctes mieux. (Plus précisément, cette section traite de la façon d'aborder le problème que vous essayez de résoudre en premier lieu.)
J'ai fait l'essai de la stratégie, et il semble être une solution sympa, sans trop de tracas qui vient avec le modèle de classeur. Il semble que si nous utilisons notre modèle de liaison personnalisé, nos annotations de nos propriétés de l'objet sera ignorée, et nous allons aussi manuellement valider notre modèle dans notre modèle de liaison personnalisé. Corrigez-moi si je me trompe. J'espère vraiment que je me trompe.
OriginalL'auteur JasCav