Is it possible to set range (or any other model DataAnnotations attributes) in code and keep all the DataAnnotations behaviour?
i.e. I have a decimal model that I want to set its minimum value in code (on page load for example) in a way I could keep using the ValidationMessageFor ?
my current "static" model
[Required]
[DisplayName("Price")]
[RegularExpression(#"[0-9]+(\.[0-9][0-9]?)?$", ErrorMessage = "Invalid price format")]
public decimal MinimumPrice { get; set; }
Thanks!
No, DataAnnotations are compiled into the code at compile time and cannot be changed at runtime.
If you insist on using DataAnnotations, then there really is no other choice.
If you are willing to do something else, then you have a number of other options. You could create your own Attribute that would read values from the variable of your choice (though you can't pass a variable to it).
Or you could use FluentValidation.
You can achieve some of what you want using Fluent API:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<SomeClass>()
.Property(x => x.MinimumPrice )
.IsRequired()
.HasColumnType("decimal")
.HasPrecision(18, 2);
// default vals for decimal type. 18 = precision = how many digits in
// total. 2 = scale = digits after decimal
}
For some things it's better to use fluent configuration and others to use annotations.
Further reading.
Related
I am trying to realize valition on data type. I have used DataAnnotations, but for data type it's not showing customized message
for example when I' am trying enter string data into int typed field. How I can customize messages in this case?
If I had to guess, you sound like you want a custom message to display when validating one or more fields in your model. You can subclass the DataAnnotations.ValidationAttribute class and override the IsValid(object) method and finally setting a custom ErrorMessage value (where ErrorMessage already belongs to the ValidationAttribute class)
public class SuperDuperValidator : ValidationAttribute
{
public override bool IsValid(object value)
{
bool valid = false;
// do your validation logic here
return valid;
}
}
Finally, decorate your model property with the attribute
public class MyClass
{
[SuperDuperValidator(ErrorMessage="Something is wrong with MyInt")]
public int MyInt { get; set; }
}
If you're using out-of-the-box MVC3, this should be all you need to propertly validate a model (though your model will probably differ/have more properties, etc) So, in your [HttpPost] controller action, MVC will automagically bind MyClass and you will be able to use ModelState.IsValid to determine whether or not the posted data is, in fact, valid.
Pavel,
The DataAnnotations DataType attribute does not affect validation. It's used to decide how your input is rendered. In such a case, David's solution above works.
However, if you want to use only the built-in validation attributes, you probably need to use the Range attribute like this:
[Range(0, 10, ErrorMessage="Please enter a number between 0 and 10")]
public int MyInt { get ; set ;}
(Of course, you should really be using the ErrorMessageResourceName/Type parameters and extract out hard-coded error message strings into resx files.)
Make sure to let MVC know where to render your error message:
<%= Html.ValidationMessageFor(m => m.MyInt) %>
Or you can just use EditorForModel and it will set it up correctly.
I don't think this has been answered because I have the same issue.
If you have a Model with a property of type int and the user types in a string of "asd" then the MVC3 framework binding/validation steps in and results in your view displaying "The value 'asd' is not valid for <model property name or DisplayName here>".
To me the poster is asking can this message that the MVC3 framework is outputting be customized?
I'd like to know too. Whilst the message is not too bad if you label your field something that easily indicates an number is expected you might still want to include additional reasons so it says something like:
"The value 'asd' is not valid for <fieldname>; must be a positive whole number."
So that the user is not entering value after value and getting different error messages each time.
my object has field with data type int. when i put in html form in this textbox letter not number the validator say- The field must be a number. how can i change this messages like this
[Required(ErrorMessage = "Введите название")]
[DisplayName("Название")]
public int age { get; set; }
I haven't found a clean way to achieve this using Data Annotations. One way would be to write a custom model binder but this seems like a lot of work to do for such a simple task.
Another way to achieve this is to add an App_GlobalResources folder to your ASP.NET application. Add a resource file called Messages.resx containing a PropertyValueRequired string resource.
PropertyValueRequired = "Some custom error message"
In your Application_Start register the resource class key:
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
DefaultModelBinder.ResourceClassKey = "Messages";
}
Note that ASP.NET MVC 2 uses the PropertyValueInvalid instead of PropertyValueRequired resource key.
IMO using Data Annotations to perform validation logic is limited (maybe in .NET 4 this will change). If you want to have full control over the validation logic I would recommend you using a validation library such as Fluent Validation or xVal.
I ran into the same problem and worked around it by specifying a RegularExpression that only allows positive natural numbers.
[Required(ErrorMessage = "Введите название")]
[DisplayName("Название")]
[RegularExpression(#"^[0-9]+$", ErrorMessage = "Поле возраст не является числом")]
public int age { get; set; }
Not sure if there are any downfalls to this solution. It seems to work fine for me.
PS: If you don't want to allow leading zeroes use "^[1-9]+[0-9]*$".
In retrospect: I have to admit though it's a bit weird to add a regular expression to an integer.
I'm working with the NerdDinner sample application and arrived at the section which deals with the Virtual Earth map. The application stores some values for the longitude and latitude. Unfortunately on my system floating point numbers are stored with a comma as the decimal separator, not a dot like in the US. So if I have a latitude of 47.64 it's retrieved and displayed as 47,64. Because that value is passed in a function call to the Virtual Earth API it fails at that point (e.g. JavaScript API expects 47.64, -122.13, but gets 47,64, -122,13).
I need to make sure that the application always uses dots. In a WebForms app I would have a common class which overrides the System.Web.UI.Page.InitializeCulture() method and I would be inheriting my pages from that class.
I am not sure about how to do the same with MVC. Do I need a customized ViewPage or something? Is there an easy way to solve this? Examples?
Because setting <globalization/> to en-US did not help at all I decided to create a custom class which initializes the proper culture settings and make sure that all views which require this behavior are inherited from my custom class.
NerdDinnerViewPage.cs:
using System.Globalization;
using System.Threading;
using System.Web.Mvc;
namespace NerdDinner.Views
{
public class NerdDinnerViewPage<T> : ViewPage<T> where T : class
{
protected override void InitializeCulture()
{
base.InitializeCulture();
Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentCulture.Clone() as CultureInfo;
if (Thread.CurrentThread.CurrentCulture != null)
{
Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencyDecimalSeparator = ".";
Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator = ".";
}
}
}
}
Edit.aspx:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="NerdDinner.Views.NerdDinnerViewPage<NerdDinner.Models.DinnerFormViewModel>" %>
I'm a Danish developer and was facing exactly the same problem. I found a working solution which has been kindly described by Kristof Neirynck on his dev blog:
Custom Model Binder
Best regards, Finn Vilsbaek
When you say
on my system floating point numbers are stored with a comma as the decimal separator
I assume you mean that they are formatted with a comma, floating point numbers are stored as float.
Whilst you can tackle the formatting issue by setting Cultures the "real" fix is to change the code. OK, it's not your code so maybe you don't want to do that on this occassion, but for general reference you need to ensure that when formatting floats or anything else you use the appropriate culture. In the case of fomatting a number for use by an API you would use the InvariantCulture.
I.e. use foo.ToString(CultureInfo.InvariantCulture) instead of foo.ToString() and likewise when using string.Format(...).
Edit I've just taken a look at the NerdDinner code and have realised that this error is in the Javascript not in C#, so my code above isn't going to help. I don't know if it is possible to format numbers in Javascript, but the real solution I think is to fix the model code to return a correctly formatted string.
Edit 2 I'd suggest you try the following:
In the SearchController.cs change the Latitude and Longitude in JsonDinner to strings. i.e.
public class JsonDinner {
public int DinnerID { get; set; }
public string Title { get; set; }
public string Latitude { get; set; }
public string Longitude { get; set; }
public string Description { get; set; }
public int RSVPCount { get; set; }
}
Then scroll down to the SearchByLocation method and change the Lat/Long lines to format the strings correctly for JavaScript:
Latitude = dinner.Latitude.ToString(CultureInfo.InvariantCulture),
Longitude = dinner.Longitude.ToString(CultureInfo.InvariantCulture),
This should mean that you do not need the fix you put in, and should fix your other question... where I will leave a comment. Hope this helps, I haven't fully tested is as I am not in your locale, but it certainly appears to work.
I'm using a simple quickfix in the TemplateEditor. My application is only using swedish (comma as decimal separator) so it's a single string.Replace but you could of course make it aware of multiple cultures.
In my Views/Shared/EditorTemplates/Decimal.ascx:
I fixed this on the JavaScript-side instead, making sure that what is passed in to the map-library is using points (.), and what is populated back into the text boxes are using commas (,). Obviously, this is not meant for localization, but a quick fix.
Map.js in callbackForLocation:
//If we've found exactly one place, that's our address.
if (points.length === 1) {
$("#Latitude").val(points[0].Latitude.toString().replace('.', ','));
$("#Longitude").val(points[0].Longitude.toString().replace('.', ','));
}
Map.ascx in the jquery-ready():
var latitude = <%=Model.Latitude.ToString().Replace(',', '.')%>;
var longitude = <%=Model.Longitude.ToString().Replace(',', '.')%>;
I have certain panels on my page that are hidden under certain circumstances.
For instance I might have a 'billing address' and 'shipping address' and I dont want to validate 'shipping address' if a 'ShippingSameAsBilling' checkbox is checked.
I am trying to use the new DataAnnotations capabilities of ASP.NET MVC 2 (preview 1) to achieve this.
I need to prevent validation of the 'shipping address' when it is not displayed and need to find the way way to achieve this. I am talking mainly server side as opposed to by using jquery.
How can I achieve this? I have had several ideas, related to custom model binding but my current best solution is below. Any feedback on this method?
For the CheckoutModel I am using this approach (most fields hidden):
[ModelBinder(typeof(CheckoutModelBinder))]
public class CheckoutModel : ShoppingCartModel
{
public Address BillingAddress { get; set; }
public Address ShippingAddress { get; set; }
public bool ShipToBillingAddress { get; set; }
}
public class Address
{
[Required(ErrorMessage = "Email is required")]
public string Email { get; set; }
[Required(ErrorMessage = "First name is required")]
public string FirstName { get; set; }
[Required()]
public string LastName { get; set; }
[Required()]
public string Address1 { get; set; }
}
The custom model binder removes all ModelState errors for fields beginning with 'ShippingAddress' if it finds any. Then 'TryUpdateModel()' will return true.
public class CheckoutModelBinder : DefaultModelBinder
{
protected override void OnModelUpdated(ControllerContext controllerContext,
ModelBindingContext bindingContext) {
base.OnModelUpdated(controllerContext, bindingContext);
var model = (CheckoutModel)bindingContext.Model;
// if user specified Shipping and Billing are the same then
// remove all ModelState errors for ShippingAddress
if (model.ShipToBillingAddress)
{
var keys = bindingContext.ModelState.Where(x => x.Key.StartsWith("ShippingAddress")).Select(x => x.Key).ToList();
foreach (var key in keys)
{
bindingContext.ModelState.Remove(key);
}
}
}
}
Any better solutions?
http://bradwilson.typepad.com/blog/2009/04/dataannotations-and-aspnet-mvc.html
I can see your predicament. I'm looking for other validation solutions also with regard to complex validation rules that might apply to more than one property on a given model object or even many properties from different model objects in a object graph (if your unlucky enough to be validating linked objects like this).
The limitation of the IDataErrorInfo interface is that a model object satisfies the valid state simply when none of the properties have errors. This is to say that a valid object is one where all of it's properties are also valid. However, i may have a situation where if property A, B and C are valid - then the whole object is valid.. but also if property A is not valid but B and C are, then the object satisfies validity. I simply have no way of describing this condition/rule with the IDataErrorInfo interface / DataAnnotations attributes.
So i found this delegate approach. Now many of the helpful advancements in MVC didn't exist at the time of writing this article but the core concept should help you. Rather than using attributes to define the validation conditions of an object we create delegate functions that validate more complex requirements and because they're delegated we can re-use them. Sure it's more work, but the use of delegates means that we should be able to write validation rule code once and store all the validation rules in the one place (maybe service layer) and (the kool bit) even use the MVC 2 DefaultModelBinder to invoke the validation automatically (without heaps of checking in our controller actions - like Scott's blog says we can do with DataAnnotations. Refer to the last paragraph before the 'Strongly Typed UI Helpers' heading)!
I'm sure you can beef the approach suggested in the above article up a little with anonymous delegates like Func<T> or Predicate<T> and writing custom code blocks for the validation rules will enable cross-property conditions (for example the condition you referred to where if your ShippingSameAsBilling property is true then you can ignore more rules for the shipping address, etc).
DataAnnotations serves to make simple validation rules on objects really easy with very little code. But as your requirements develop you will need to validate on more complex rules. The new virtual methods in the MVC2 model binder should continue to provide us with ways of integrating our future validation inventions into the MVC framework.
Make sure the fields you don't want validated are not posted to the action. We only validate the fields that were actually posted.
Edit: (by questioner)
This behavior has changed in MVC2 RC2 :
Default validation system validates
entire model The default validation
system in ASP.NET MVC 1.0 and in
previews of ASP.NET MVC 2 prior to RC
2 validated only model properties that
were posted to the server. In ASP.NET
MVC 2, the new behavior is that all
model properties are validated when
the model is validated, regardless of
whether a new value was posted.
Applications that depend on the
ASP.NET MVC 1.0 behavior may require
changes. For more information about
this change, see the entry Input
Validation vs. Model Validation in
ASP.NET MVC on Brad Wilson’s blog.
For the more complex cases I moved away from simple DataAnnotations to the following: Validation with visitors and extension methods.
If you want to make use of your DataAnnotations you would replace something like the following:
public IEnumerable<ErrorInfo> BrokenRules (Payment payment)
{
// snip...
if (string.IsNullOrEmpty (payment.CCName))
{
yield return new ErrorInfo ("CCName", "Credit card name is required");
}
}
with a method to validate a property by name via DataAnnotations (which I don't have atm).
I created a partial model binder that only validates the keys that were submitted. For security reasons (if I was going to take this a step farther) I'd create a data annotation attribute that marks which fields are allowed to be excluded from a model. Then, OnModelUpdated check field attributes to ensure there is no undesired underposting going on.
public class PartialModelBinder : DefaultModelBinder
{
protected override void OnModelUpdated(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
// default model binding to get errors
base.OnModelUpdated(controllerContext, bindingContext);
// remove errors from filds not posted
// TODO: include request files
var postedKeys = controllerContext.HttpContext.Request.Form.AllKeys;
var unpostedKeysWithErrors = bindingContext.ModelState
.Where(i => !postedKeys.Contains(i.Key))
.Select(i=> i.Key).ToList();
foreach (var key in unpostedKeysWithErrors)
{
bindingContext.ModelState.Remove(key);
}
}
}
This isn't related to DataAnnotations but have you looked at the Fluent Validation project? It gives you fine grain control over your validation and if you have object-to-object validation an aggregate object of the two objects will get you going.
Also it seems to have been build with MVC in mind but it also has its own "runtime" so that you can use it in other .NET applications as well which is another bonus in my book.
I've got a Fitnesse RowFixture that returns a list of business objects. The object has a field which is a float representing a percentage between 0 and 1. The consumer of the business object will be a web page or report that comes from a designer, so the formatting of the percentage will be up to the designer rather than the business object.
It would be nicer if the page could emulate the designer when converting the number to a percentage, i.e. instead of displaying 0.5, it should display 50%. But I'd rather not pollute the business object with the display code. Is there a way to specify a format string in the RowFixture?
You certainly don't want to modify your Business Logic just to make your tests look better. Good news however, there is a way to accomplish this that is not difficult, but not as easy as passing in a format specifier.
Try to think of your Fit Fixture as a service boundary between FitNesse and your application code. You want to define a contract that doesn't necessarily have to change if the implementation details of your SUT (System Under Test) change.
Lets look at a simplified version of your Business Object:
public class BusinessObject
{
public float Percent { get; private set; }
}
Becuase of the way that a RowFixture works we need to define a simple object that will work as the contract. Ordinarily we would use an interface, but that isn't going to serve our purpose here so a simple DTO (Data Transfer Object) will suffice.
Something Like This:
public class ReturnRowDTO
{
public String Percent { get; set; }
}
Now we can define a RowFixture that will return a list of our custom DTO objects. We also need to create a way to convert BusinessObjects to ReturnRowDTOs. We end up with a Fixture that looks something like this.
public class ExampleRowFixture: fit.RowFixture
{
private ISomeService _someService;
public override object[] Query()
{
BusinessObject[] list = _someService.GetBusinessObjects();
return Array.ConvertAll(list, new Converter<BusinessObject, ReturnRowDTO>(ConvertBusinessObjectToDTO));
}
public override Type GetTargetClass()
{
return typeof (ReturnRowDTO);
}
public ReturnRowDTO ConvertBusinessObjectToDTO(BusinessObject businessObject)
{
return new ReturnRowDTO() {Percent = businessObject.Percent.ToString("%")};
}
}
You can now change your underlying BusinessObjects around without breaking your actual Fit Tests. Hope this helps.
I'm not sure what the "polution" is. Either the requirement is that your Business Object returns a value expressed as a percentage, in which case your business object should offer that -OR- you are testing the true value of the response as float, which you have now.
Trying to get fitnesse to massage the value for readability seems a bit odd.