I want to validate my View Model in class-Level .
I am using a actionFilter. How do I use a data annotation?
and how to inject the Access database?
A validation that would happen if the customer says it is already our customer or not.
I used action filter but I think it must have a way to use a DataAnnotation
Commented the code follows:
public class DadosAssinaturaFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var model = filterContext.ActionParameters.Values.FirstOrDefault(x => x.GetType() == typeof(DadosAssinatura)) as DadosAssinatura;
var modelState = filterContext.Controller.ViewData.ModelState;
if (model != null)
{
var jaSouCliente = modelState.FirstOrDefault(x => x.Key == "JaSouCliente");
if (jaSouCliente.Key != null) // select "Is Clilent" radiobutton ?
if (jaSouCliente.Value.Errors.Count > 0) // if so remove the errors of the registration data
{
modelState.RemoveKeysStartsWith("DadosCliente.");
modelState.RemoveKeysStartsWith("DadosAcesso.");
}
else if (model.JaSouCliente != null && model.JaSouCliente.Value) // else, click in "Is Client"
{
modelState.RemoveKeysStartsWith("DadosCliente."); //remove
modelState.Remove("DadosAcesso.ConfirmaSenha"); //how injec UnitOfWor/Repository? AutoFac?
if (unitOfWork.Client.GetClientByUser(model.DadosAcesso.Usuario, model.DadosAcesso.Senha) == null)//user and Password
modelState.AddModelError("DadosAcesso.Usuario", "Usuario Nao Encontrado");
}
else if (model.DadosCliente.PessoaFisica) // is a company our people?
{
modelState.Remove("DadosCliente.RazaoSocial"); // remove validate for company name
modelState.Remove("DadosCliente.Cnpj"); //the brazilian document of company
}
else modelState.Remove("DadosCliente.Cpf"); //the brazilian document of people
}
base.OnActionExecuting(filterContext);
}
}
public static class ModelStateErros
{
public static void RemoveKeysStartsWith(this ModelStateDictionary modelStateDictionary, string startsWith)
{
var keys = modelStateDictionary.Keys.Where(key => key.StartsWith(startsWith)).ToList();
foreach (var variable in keys)
{
modelStateDictionary.Remove(variable);
}
}
}
sorry my English
Simply implement IValidateableObject in your ViewModel class (or create another partial class) and avoid the filter completely, and keep your validation logic with your ViewModel.
How do I use IValidatableObject?
Related
I have made a "standard" Web Api 2 OData project with convention model routing. Following OData queries are working:
/odata/Users
/odata/Users(123)
/odata/$metadata
/odata/Users?$select=Username
So everything seemed to be fine until I tried this, which I think is also a legal OData query:
/odata/Users(123)/Username
Slash / in query breaks everything and it does not hit the controller class and OData authentication flow at all. Should this be supported at all in Microsoft ASP.NET OData implementation? Or is this supported only if I define explicit methods with correct routes for every single property like Username? Any suggestions to fix this? I have tried explicit {*rest} routes etc.
AFAIK, the built-in routing conventions don't include one for property access. You'd be required to add many actions for every property access.
However, based on this resource here, it's not all that difficult to add a custom routing convention to handle the property access path template: ~/entityset/key/property
Here's a custom routing convention adapted from the link I shared above
Assembly used: Microsoft.AspNet.OData 7.4.1 - the approach would be the same for any other OData Web API library you might be using
Class used for illustration
public class Product
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
Add routing convention for property access
// Usings
using Microsoft.AspNet.OData.Routing;
using Microsoft.AspNet.OData.Routing.Conventions;
using System;
using System.Linq;
using System.Web.Http.Controllers;
// ...
public class CustomPropertyRoutingConvention : NavigationSourceRoutingConvention
{
private const string ActionName = "GetProperty";
public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap)
{
if (odataPath == null || controllerContext == null || actionMap == null)
{
return null;
}
if (odataPath.PathTemplate == "~/entityset/key/property" ||
odataPath.PathTemplate == "~/entityset/key/cast/property" ||
odataPath.PathTemplate == "~/singleton/property" ||
odataPath.PathTemplate == "~/singleton/cast/property")
{
var segment = odataPath.Segments.OfType<Microsoft.OData.UriParser.PropertySegment>().LastOrDefault();
if (segment != null)
{
string actionName = FindMatchingAction(actionMap, ActionName);
if (actionName != null)
{
if (odataPath.PathTemplate.StartsWith("~/entityset/key", StringComparison.Ordinal))
{
var keySegment = odataPath.Segments.OfType<Microsoft.OData.UriParser.KeySegment>().FirstOrDefault();
if (keySegment == null || !keySegment.Keys.Any())
throw new InvalidOperationException("This link does not contain a key.");
controllerContext.RouteData.Values[ODataRouteConstants.Key] = keySegment.Keys.First().Value;
}
controllerContext.RouteData.Values["propertyName"] = segment.Property.Name;
return actionName;
}
}
}
return null;
}
public static string FindMatchingAction(ILookup<string, HttpActionDescriptor> actionMap, params string[] targetActionNames)
{
foreach (string targetActionName in targetActionNames)
{
if (actionMap.Contains(targetActionName))
{
return targetActionName;
}
}
return null;
}
}
Add single method in your controller to handle request for any property
public class ProductsController : ODataController
{
// ...
[HttpGet]
public IHttpActionResult GetProperty(int key, string propertyName)
{
var product = _db.Products.FirstOrDefault(d => d.Id.Equals(key));
if (product == null)
{
return NotFound();
}
PropertyInfo info = typeof(Product).GetProperty(propertyName);
object value = info.GetValue(product);
return Ok(value, value.GetType());
}
private IHttpActionResult Ok(object content, Type type)
{
var resultType = typeof(OkNegotiatedContentResult<>).MakeGenericType(type);
return Activator.CreateInstance(resultType, content, this) as IHttpActionResult;
}
// ...
}
In your WebApiConfig.cs (or equivalent place where you configure the service)
var modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Product>("Products");
var routingConventions = ODataRoutingConventions.CreateDefaultWithAttributeRouting("odata", configuration);
routingConventions.Insert(0, new CustomPropertyRoutingConvention());
configuration.MapODataServiceRoute("odata", "odata", modelBuilder.GetEdmModel(), new DefaultODataPathHandler(), routingConventions);
configuration.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
configuration.EnsureInitialized();
Request for Name property: /Products(1)/Name
Request for Id property: /Products(1)/Id
I need to perform a series of validations, say ValidationA, ValidationB, ValidationC.
If any validation fails, the chain fails to validate immediately.
Each subsequent validation is performed only after the previous validation condition passes.
If a validation condition fails, I need to show a confirmation dialog to the user, e.g. "Are you sure you want to do this?" — where if the user says "Yes" then we should move on to the next validation in the chain. If the user says "No" in the confirmation dialog, then we can fail immediately.
What is a good way to implement this?
The answer is in C#, but I hope it's clear. The most important thing here is that raw validation is separated from the UI. OverridableValidator is an "UI guy" which gets "logic guy" in constructor, along with the message to be shown to the user.
interface IValidator<T>
{
bool IsValid(T subject);
}
class OverridableValidator<T> : IValidator<T>
{
readonly IValidator<T> _validator;
readonly string _confirmOverrideMessage;
public OverridableValidator(IValidator<T> validator, string confirmOverrideMessage)
{
_validator = validator;
_confirmOverrideMessage = confirmOverrideMessage;
}
public bool IsValid(T subject)
{
if (_validator.IsValid(subject))
{
return true;
}
return MessageBox.Show(_confirmOverrideMessage, "Confirmation required", MessageBoxIcon.Question, MessageBoxButtons.YesNo) == DialogResult.Yes;
}
}
class Person
{
public int Age {get;set;}
public int HeightInCentimeters {get;set;}
}
class MaturityValidator : IValidator<Person>
{
public bool IsValid(Person guy)
{
return guy.Age >= 18;
}
}
class HeightValidator : IValidator<Person>
{
public bool IsValid(Person guy)
{
return guy.HeightInCentimeters < 120;
}
}
void Main()
{
var ageValidator = new OverridableValidator<Person>(new MaturityValidator(), "Do you want to sell him cigarettes?");
var heightValidator = new OverridableValidator<Person>(new HeightValidator(), "Do you want to invite such a short person to your basketball team?");
var validatedPerson = new Person() { Age = 15, HeightInCentimeters = 140 };
var validatorsList = new[] {ageValidator, heightValidator};
foreach (var validator in validatorsList)
{
if (!validator.IsValid(validatedPerson))
{
return; //validaton failed
}
}
Console.WriteLine("This guy can smoke and play in NBA");
}
Implement it like this:
(ValidationA || UserConfirmationA) && (ValidationB || UserConfirmationB) && (ValidationC || UserConfirmationC)
where Validation and UserComfirmation are function which do what their names say and return a boolean.
I assume that && and || are lazy which is the case in most languages.
In addition to what dzendras said, it might help for you to look at Chain of responsibility pattern allowing you to add validators dynamically.
I had two fields some thing like phone number and mobile number. Some thing like..
[Required]
public string Phone { get; set; }
[Required]
public string Mobile{ get; set; }
But user can enter data in either one of it. One is mandatory. How to handle them i.e how to disable the required field validator for one field when user enter data in another field and viceversa. In which event i have to handle it in javascript and what are the scripts i need to add for this. Can anyone please help to find the solution...
One possibility is to write a custom validation attribute:
public class RequiredIfOtherFieldIsNullAttribute : ValidationAttribute, IClientValidatable
{
private readonly string _otherProperty;
public RequiredIfOtherFieldIsNullAttribute(string otherProperty)
{
_otherProperty = otherProperty;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(_otherProperty);
if (property == null)
{
return new ValidationResult(string.Format(
CultureInfo.CurrentCulture,
"Unknown property {0}",
new[] { _otherProperty }
));
}
var otherPropertyValue = property.GetValue(validationContext.ObjectInstance, null);
if (otherPropertyValue == null || otherPropertyValue as string == string.Empty)
{
if (value == null || value as string == string.Empty)
{
return new ValidationResult(string.Format(
CultureInfo.CurrentCulture,
FormatErrorMessage(validationContext.DisplayName),
new[] { _otherProperty }
));
}
}
return null;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "requiredif",
};
rule.ValidationParameters.Add("other", _otherProperty);
yield return rule;
}
}
which you would apply to one of the properties of your view model:
public class MyViewModel
{
[RequiredIfOtherFieldIsNull("Mobile")]
public string Phone { get; set; }
public string Mobile { get; set; }
}
then you could have a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
and finally a view in which you will register an adapter to wire the client side validation for this custom rule:
#model MyViewModel
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script type="text/javascript">
jQuery.validator.unobtrusive.adapters.add(
'requiredif', ['other'], function (options) {
var getModelPrefix = function (fieldName) {
return fieldName.substr(0, fieldName.lastIndexOf('.') + 1);
}
var appendModelPrefix = function (value, prefix) {
if (value.indexOf('*.') === 0) {
value = value.replace('*.', prefix);
}
return value;
}
var prefix = getModelPrefix(options.element.name),
other = options.params.other,
fullOtherName = appendModelPrefix(other, prefix),
element = $(options.form).find(':input[name="' + fullOtherName + '"]')[0];
options.rules['requiredif'] = element;
if (options.message) {
options.messages['requiredif'] = options.message;
}
}
);
jQuery.validator.addMethod('requiredif', function (value, element, params) {
var otherValue = $(params).val();
if (otherValue != null && otherValue != '') {
return true;
}
return value != null && value != '';
}, '');
</script>
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(x => x.Phone)
#Html.EditorFor(x => x.Phone)
#Html.ValidationMessageFor(x => x.Phone)
</div>
<div>
#Html.LabelFor(x => x.Mobile)
#Html.EditorFor(x => x.Mobile)
#Html.ValidationMessageFor(x => x.Mobile)
</div>
<button type="submit">OK</button>
}
Pretty sick stuff for something so extremely easy as validation rule that we encounter in our everyday lives. I don't know what the designers of ASP.NET MVC have been thinking when they decided to pick a declarative approach for validation instead of imperative.
Anyway, that's why I use FluentValidation.NET instead of data annotations to perform validations on my models. Implementing such simple validation scenarios is implemented in a way that it should be - simple.
I know this question is not so hot, because it was asked relatively long time ago, nevertheless I'm going to share with a slightly different idea of solving such an issue. I decided to implement mechanism which provides conditional attributes to calculate validation results based on other properties values and relations between them, which are defined in logical expressions.
Your problem can be defined and automatically solved by the usage of following annotations:
[RequiredIf("Mobile == null",
ErrorMessage = "At least email or phone should be provided.")]
public string Phone{ get; set; }
[RequiredIf("Phone == null",
ErrorMessage = "At least email or phone should be provided.")]
public string Mobile { get; set; }
If you feel it would be useful for your purposes, more information about ExpressiveAnnotations library can be found here. Client side validation is also supported out of the box.
Since nobody else suggested it, I'm going to tell you a different way to do this that we use.
If you create a notmapped field of a custom data type (in my example, a pair of gps points), you can put the validator on that and you don't even need to use reflection to get all the values.
[NotMapped]
[DCGps]
public GPS EntryPoint
{
get
{
return new GPS(EntryPointLat, EntryPointLon);
}
}
and the class, a standard getter/setter
public class GPS
{
public decimal? lat { get; set; }
public decimal? lon { get; set; }
public GPS(decimal? lat, decimal? lon)
{
this.lat = lat;
this.lon = lon;
}
}
and now the validator:
public class DCGps : DCValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (!(value is GPS)) {
return new ValidationResult("DCGps: This annotation only works with fields with the data type GPS.");
}
//value stored in the field.
//these come through as zero or emptry string. Normalize to ""
string lonValue = ((GPS)value).lonstring == "0" ? "" : ((GPS)value).lonstring;
string latValue = ((GPS)value).latstring == "0" ? "" : ((GPS)value).latstring;
//place validation code here. You have access to both values.
//If you have a ton of values to validate, you can do them all at once this way.
}
}
What I have is a model which has one of it's attributes dynamic. This dynamic attribute holds one of about 50 different objects. This model is send to a view that dynamic creates the page based on which object is used. This is working perfectly ... the issue is the postback. When the model posts back the modelbinder is not able to bind the dynamic attribute. I was expecting this and thought I would be able to handle it but nothing that I tried works appart from making an action for EACH different objects.
Model
public class VM_List
{
public Config.CIType CIType { get; set; }
public dynamic SearchData { get; set; }
//Lots of static fields
}
This works
public ActionResult List_Person(VM_List Model, VM_Person_List SearchData)
{
Model.SearchData = SearchData;
//Stuff
}
public ActionResult List_Car(VM_List Model, VM_Car_List SearchData)
{
Model.SearchData = SearchData;
//Stuff
}
But what I want is a single action
public ActionResult List(VM_List Model)
{
//Stuff
}
I have tried things like
public ActionResult List(VM_List Model)
{
switch (Model.CIType)
{
case Config.CIType.Person:
UpdateModel((VM_Person_List)Model.SearchData);
break;
default:
SearchData = null;
break;
}
//Stuff
}
and a Custom modelbinder
CIType CIType = (CIType)bindingContext.ValueProvider.GetValue("CIType").ConvertTo(typeof(CIType));
switch (CIType)
{
case Config.CIType.Person:
SearchData = (VM_Person_List)bindingContext.ValueProvider.GetValue("SearchData").ConvertTo(typeof(VM_Person_List));
break;
default:
SearchData = null;
break;
}
but I can't get either to work. Any ideas?
After trying many different things I finally found a way that works.
Action:
public ActionResult List(VM_List Model)
{
//If the defaultmodelbinder fails SearchData will be an object
if(Model.SearchData.GetType() == typeof(object))
{
//Get SearchData as a Dictionary
Dictionary<string, string> DSearchData = Request.QueryString.AllKeys.Where(k => k.StartsWith("SearchData.")).ToDictionary(k => k.Substring(11), k => Request.QueryString[k]);
switch (Model.CIType)
{
case Config.CIType.Person:
Model.SearchData = new VM_Person_List(DSearchData);
break;
case Config.CIType.Car:
Model.SearchData = new VM_Car_List(DSearchData);
break;
}
//Rest of action
//..
}
and for each object make a constructor that accepts a dictionary
public VM_Car_List(Dictionary<string, string> DSearchData)
{
this.Make = Convert.ToInt32(DSearchData["Make"]);
this.Model = Convert.ToInt32(DSearchData["Model"]);
this.Year = Convert.ToInt32(DSearchData["Year"]);
// ETC
}
I'm currently working on a MVC.NET 3 application; I recently attended a course by "Uncle Bob" Martin which has inspired me (shamed me?) into taking a hard look at my current development practice, particularly my refactoring habits.
So: a number of my routes conform to:
{controller}/{action}/{type}
Where type typically determines the type of ActionResult to be returned, e.g:
public class ExportController
{
public ActionResult Generate(String type, String parameters)
{
switch (type)
{
case "csv":
//do something
case "html":
//do something else
case "json":
//do yet another thing
}
}
}
Has anyone successfully applied the "replace switch with polymorhism" refactoring to code like this? Is this even a good idea? Would be great to hear your experiences with this kind of refactoring.
Thanks in advance!
The way I am looking at it, this controller action is screaming for a custom action result:
public class MyActionResult : ActionResult
{
public object Model { get; private set; }
public MyActionResult(object model)
{
if (model == null)
{
throw new ArgumentNullException("Haven't you heard of view models???");
}
Model = model;
}
public override void ExecuteResult(ControllerContext context)
{
// TODO: You could also use the context.HttpContext.Request.ContentType
// instead of this type route parameter
var typeValue = context.Controller.ValueProvider.GetValue("type");
var type = typeValue != null ? typeValue.AttemptedValue : null;
if (type == null)
{
throw new ArgumentNullException("Please specify a type");
}
var response = context.HttpContext.Response;
if (string.Equals("json", type, StringComparison.OrdinalIgnoreCase))
{
var serializer = new JavaScriptSerializer();
response.ContentType = "text/json";
response.Write(serializer.Serialize(Model));
}
else if (string.Equals("xml", type, StringComparison.OrdinalIgnoreCase))
{
var serializer = new XmlSerializer(Model.GetType());
response.ContentType = "text/xml";
serializer.Serialize(response.Output, Model);
}
else if (string.Equals("csv", type, StringComparison.OrdinalIgnoreCase))
{
// TODO:
}
else
{
throw new NotImplementedException(
string.Format(
"Sorry but \"{0}\" is not a supported. Try again later",
type
)
);
}
}
}
and then:
public ActionResult Generate(string parameters)
{
MyViewModel model = _repository.GetMeTheModel(parameters);
return new MyActionResult(model);
}
A controller should not care about how to serialize the data. That's not his responsibility. A controller shouldn't be doing any plumbing like this. He should focus on fetching domain models, mapping them to view models and passing those view models to view results.
If you wanted to "replace switch with polymorphism" in this case, you could create three overloaded Generate() ActionResult methods. Using custom model binding, make the Type parameter a strongly-typed enum called DataFormat (or whatever.) Then you'd have:
public ActionResult Generate(DataFormat.CSV, String parameters)
{
}
public ActionResult Generate(DataFormat.HTML, String parameters)
{
}
public ActionResult Generate(DataFormat.JSON, String parameters)
{
}
Once you get to this point, you can refactor further to get the repetition out of your Controller.