MVC 2 Model Validation messages - asp.net

i have a view model with a property like this one :
[RegularExpression(#"^d\+$", ErrorMessageResourceType = typeof(Resources.Validation), ErrorMessageResourceName = "NumberValidationMsg" )]
public int? Number {get; set;}
NumberValidationMsg resource is set to "Only numbers allowed !".
but when I try to enter something like 'test' into Number field on form, ModelState displays the ErrorMessage with content similar to : "The value 'test' is not valid for Number."
can this message be turned off, customized ?
(or maybe the best solution would be just to replace int? with string )
Thank You !

If you want to accept text in the field, you need to change it to a string, and make your conversions to int according to your rules.
If your model is an int, then the only valid input will be int (or empty, if it is "int?"), and you should not try to prevent this unless there are good reasons... Moreover, I believe that you could leave the whole regular expression out, because the MVC already does that check for you implicitly (because it is an int).

Related

Logical Thinking: Using Dynamic vs Static Values to Represent Data

I don't think I am asking the question correctly, but hopefully you know what I am asking.
What are pros and cons of using a string value to represent a database field (or any variable) vs using an enumeration or constant? I am not asking about the datatype, but hows its handled on the back-end. I'll use LINQ to SQL for an example.
My thinking is that by using an enumerable or constant it's: easier to read, ensures compatibly should the values ever need to be changed, and the value is hard coded -so to speak- so there are less chances of an error caused by a typo. On the flip side, do I really need a class/structure with member enumerations that essentially act as a look up for the value I want?
Using an Constant
Module Trip
Public Const OPEN As String = "Open"
Public Const PENDING_PAYMENT As String = "Pending Payment"
Public Const CANCELLED As String = "Cancelled"
Public Const CLOSED As String = "Closed"
End Module
Dim product = From p In db.Payments
Where p.PaymentId = PaymentId
For Each item In product
item.Status = PayStatus.PENDING_PAYMENT
Next
Using a string
Dim product = From p In db.Payments
Where p.PaymentId = PaymentId
For Each item In product
item.Status = "Pending Payment"
Next
As one of the comments says, the common way to deal with this is using a lookup table in the database. In its most simple form, you would have a class, let's say PaymentStatus:
Class PaymentStatus
Public Property Id As Integer
Public Property Name As String
End Class
and Payment would have reference property like
Public Property PaymentStatus As PaymentStatus
This way, you can always get the options from the database and you will never make a typo in code. It's also much easier to add options or to change descriptions.
For instance, think of what you need to do if you'd decide that "Cancelled" needs to be differentiated into "Cancelled by user" (the old status) and "Cancelled by system" (a new status introduced by new business logic). You'd need a script to update all records in the database to the new string (and change the code, but you'd be changing code anyway). A lookup table allows you to update only one record (and add a new one in this example).

Validation in Custom web config section class

I have a custom web config class. I want to add RegexStringValidator as an attribute to a web config property like:
[ConfigurationProperty("siteDomainName", DefaultValue = "")]
[RegexStringValidator(#"^([a-zA-Z0-9_-]*(?:\.[a-zA-Z0-9_-]*)+):?([0-9]+)?/?")]
public string SiteDomainName
{
get
{
return (string) this["siteDomainName"];
}
set
{
this["siteDomainName"] = value;
}
}
The error i am getting is :
The value does not conform to the validation regex string
'^([a-zA-Z0-9_-]*(?:.[a-zA-Z0-9_-]*)+):?([0-9]+)?/?'.
Even if the value supplied is correct and matches the Regex.
What is the problem with this??
Like ronen said in his comment, your default value should also match the regular expression. See this answer for example: https://stackoverflow.com/a/5313223/4830. The reason is that the default value is also evaluated and validated, even when you set a value in your web.config file.
Something like this should work (default value validates, and property is required so it should never actually use the default value in practice):
[ConfigurationProperty("siteDomainName", DefaultValue="www.example.com", IsRequired=True)]
[RegexStringValidator(#"^([a-zA-Z0-9_-]*(?:\.[a-zA-Z0-9_-]*)+):?([0-9]+)?/?")]
public string SiteDomainName
...
In case you don't want a default value, you could change the regular expression to accept the empty string, by making the whole value basically optional:
[ConfigurationProperty("siteDomainName", IsRequired=False)]
[RegexStringValidator(#"^(([a-zA-Z0-9_-]*(?:\.[a-zA-Z0-9_-]*)+):?([0-9]+)?/?)?$")]
public string SiteDomainName
...
Notice that the use of IsRequired in both code examples, use the one that best fits your needs. Be aware that de default value is always going to be validated.

ModelState.IsValid is true, but a [Required] property is missing from POST data

My view model has a [Required] non-nullable int property, selected by a DropDownListFor. If the list to choose from is empty, ModelState.IsValid is true.
My model has a Required int property:
public class MyModel
{
[Required]
public int PickedValue { get; set;}
IEnumerable<SelectListItem> Items { get; set; }
}
In my Create view, I render a <select> element:
#Html.DropDownListFor(model => model.PickedValue, model.Items)
And in my controller, if the model.Items list is empty (no elements to choose from), ModelState.IsValid is true:
[HttpPost]
public ActionResult Create( MyModel model )
{
if( ModelState.IsValid )
{
// true, and ModelState.Keys doesn't contain PickedValue because it was never POSTed.
}
//...
}
Now, the problem goes away if:
PickedValue is Nullable (int?), or
if I have an empty item in my <select> - #Html.DropDownListFor(model => model.PickedValue, model.Items, ""), or
if client-side validation is enabled (since the action is never fired).
Is there a way to force ModelState.IsValid to be false if some of MyModel's properties are [Required] but are missing from the POST data? Shouldn't this be the default behavior?
In your case, I think it would probably be better to make PickedValue a nullable int. If there is a chance that your DropDownList has no items, then null should be a possible value for your PickedValue.
Reply to comments
That makes sense but doesn't answer my original question... Is there a
way to force ModelState.IsValid to be false if some of MyModel's
properties are [Required] but are missing from the POST data?
Shouldn't this be the default behavior?
When it comes to ints, doubles, bools, and other primitives that cannot have a null value, when the DefaultModelBinder receives no data for them in the HTTP request, it must still construct a model instance. Therefore, the model gets constructed and the properties are initialized to their default values. In the case if int, this means it will be initialized to zero. Since nothing is received in the HTTP request, this means the int never gets set, hence it will always be zero. Have you tried giving your int a [Range] validator instead of a [Required] validator? This should validate that it is not zero:
[Range(1, int.MaxValue)]
public int PickedValue { get; set; }
I'd like to avoid changing the model because it's already a common
pattern in my code... Plus making a property in the model nullable to
force it not to be null seems counter-intuitive.
Just because it's a common pattern in your code doesn't mean it's correct. I have to reiterate my original answer: If it is possible for your dropdown list to not have any items, then your model should allow null for its selected value.
This is one reason why it's good practice to have separate entity & viewmodel layers. In your domain entities, a relationship might be required. However when a user is first presented with a form to select that relationship, you either have to give them a default or empty selected dropdown item. In this case, the foreign key in the entity might be an int, but the representation in the viewmodel should be a Nullable<int>. For this reason alone I don't think it's counter-intuitive to make something nullable just to make sure it is not null. You make it nullable in the viewmodel so you can give the user a form with a blank value, and require them to fill it in.

DataMember Emit Default Value

I have a .Net Web Service function that can accept one string.
That function will then serialize that string to JSON, but I only want to serialize it if it's value is not "".
I found these instructions:
http://msdn.microsoft.com/en-us/library/aa347792.aspx
[DataContract]
public class MyClass
{
[DataMember (EmitDefaultValue=false)]
public string myValue = ""
}
Unfortunatelly I can not hide the myValue from the serialization because "" is not the .Net default value for a string (how dumb is that!)
One of two option ocurred
On the web service have some kind of attribute that sets the "" to null
Have some condition on the class
I would prefer the 1st because it makes the code cleaner but an opinion would be great.
Thanks
You can explicitly set what the default value is (for the purposes of serialization) using the DefaultValueAttribute class:
[DataContract]
public class MyClass
{
[DataMember (EmitDefaultValue=false)]
[DefaultValue("")]
public string myValue = ""
}
I think you have at least a couple of options here. It's extra work but worth it.
You can encapsulate the string in a reference type. Since reference types are null if not present, that lets you know right away if a string was present or not (because the encapsulating reference type would be either non-null or null, if the string is non-empty or not.)
A final option you have is to add an extra complementary variable (perhaps a boolean) that is set on OnDeserializing/OnDeserialized/OnSerializing/OnSerialized and use this to track whether or not something was actually present on the wire. You might, for example, set this complementary variable to true only when you're actually serializing out a non-empty string and similarly

AutoCompleteExtender in ASP.NET additional info

how do I pass additional information to the service method returning the collection of items? I'll attempt to explain what I mean, I have 2 text boxes on a form, I need to fill out names, based of a specific account id in a database. so, I need to pass an integer to the getNamesForDropDown method. I couldn't figure out what to do, so I did the wrong thing, and used the CompletionSetCount to actually pass the information I needed:
[System.Web.Services.WebMethod]
[System.Web.Script.Services.ScriptMethod]
public string[] getNamesForDropDown(string prefixText, int count)
{
String sql = "Select fldName From idAccountReps Where idAccount = " + count.ToString();
//... rest of the method removed, this should be enough code to understand
//... the evil wrongness I did.
}
in my front side aspx file, i set the CompletionSetCount based off the Account id the user is currently viewing on that page.
<ajaxtk:AutoCompleteExtender
runat="server"
ID="AC1"
TargetControlID="txtAccName"
ServiceMethod="getNamesForDropDown"
ServicePath="AccountInfo.asmx"
MinimumPrefixLength="1"
EnableCaching="true"
CompletionSetCount='<%# Eval("idAccount") %>'
/>
So, that's definitely a wrong way... what would be the right way?
azam has the right idea- but the signature of the autocomplete method can also have a third parameter:
public string[] yourmethod(string prefixText, int count, string contextKey)
you can Split up the results of the contextKey string using Azam's method- but this way you do not have to worry about sanatizing the user's input of the .Split()'s delimiter (:)
Holy smoke, I think this is what I need, I swear I never saw this option before I started programming this. Is this a new property to the autocompleteextender?
Excerpt from Documentation:
ContextKey - User/page specific context provided to an optional overload of the web method described by ServiceMethod/ServicePath. If the context key is used, it should have the same signature with an additional parameter named contextKey of type string:
[System.Web.Services.WebMethod]
[System.Web.Script.Services.ScriptMethod]
public string[] GetCompletionList(
string prefixText, int count, string contextKey) { ... }
Note that you can replace "GetCompletionList" with a name of your choice, but the return type and parameter name and type must exactly match, including case.
Edit: It doesn't matter if it is new or not, or whether i just completely overlooked it. It works, and I'm happy. It took me about 10 minutes from being confused to figuring out my own answer.
If you like you can use a separator with the prefixText. So, you can pass "1:bcd" and on the service end you can split the two items:
string[] arguments = prefixText.Split(':');
int id = Int32.Parse(arguments[0]);
string text = arguments[1];
Refer here
http://www.aspsnippets.com/Articles/ASPNet-AJAX-AutoCompleteExtender-Pass-Additional-Parameter-to-WebMethod-using-ContextKey.aspx

Resources