Currently I have a DataModel object which contains my linq to sql classes(a dmbl file). Currently I use a partial class to validate the incoming input. For example
public partial class User : IEntity
{
public NameValueCollection CheckModel()
{
return GetRuleViolations();
}
/// <summary>
/// Method validates incoming data, by given rules in the if statement.
/// </summary>
/// <returns>NameValueCollection</returns>
private NameValueCollection GetRuleViolations()
{
NameValueCollection errors = new NameValueCollection();
if (string.IsNullOrEmpty(Username))
errors.Add("Username", "A username is required");
// and so on
return errors;
}
}
Now what I want to try to do is add validation attributes to the fields. For example I want to try to add the required attribute to the field Username instead/in addtion of using the validation I currently have. My question is how can I achieve this because the dmbl file is auto generated. Or maybe it is not possible and should I use a different approach?
You should read about Metadata classes. This is example blog entry about it.
Adding Required atrribute to User class will be something like:
[MetadataType(typeof(UserMetadata))]
public partial class User
{
}
public class UserMetadata
{
[Required]
public string Username { get; set; }
}
Related
I am creating an ASP.NET MVC application.
I have a model with data annotations like this:
public class SearchModel
{
[MaxLength(11)]
public string? SSN { get; set; } = string.Empty;
}
And I have a controller method that receives an object of this type as parameter:
public async Task<IActionResult> Search([Bind(include: "SSN")] SearchModel searchModel)
{
// do something
}
I get a Veracode error
ASP.NET misconfiguration : improper model validation (CWE ID 1174)
on the definition of the method...
Testing.. If I replace SearchModel with String, it works. So the problem is the model definition, but I added the data annotations to the property.
What else can I check?
Thanks
As I found here the only way to avoid this flaw is to remove signature Async Task<> from the Method..
I am in the process of migrating PHP code to ASP.NET MVC and previously for the register page I would store if the new user had accepted the rules and also was COPPA verified by redirecting from /register to /register&readrules=1&coppa=1. I would then just parse the #readrules and #coppa in the code.
What is the best way to do this in ASP.NET? Thanks
Use query string parameters instead:
/register?readrules=1&coppa=1
This is more standard and you do not need any parsing. Just define a view model to accomodate those values:
public class MyViewModel
{
public int Readrules { get; set; }
public int Coppa { get; set; }
}
and ten have your Register controller action take this view model as parameter:
public ActionResult Register(MyViewModel model)
{
... at this stage model.Readrules and model.Coppa will contain the values passed
as query string parameters tat you could use here
}
The default model binder will automatically bind the values of the readrules and coppa query string parameters to the corresponding properties of the view model that your controller action takes.
I am using a viewmodel with required field validation specified for some properties. I was able to create readonly version of same model using "displayfor". In a page, along with this readonly view there are other controls too along with submit. Now, when I click on "submit", it is getting validated and ModelState is invalid. How to remove validation, if we use model only for display.
ViewModel
public class CustomerVM
{
[Required]
public string Name {get;set;}
}
View
#using (Html.BeginForm("CreateCustomer, "Customer", FormMethod.Post))
{
#Html.DisplayFor(o => o.Name)
#..other input controls.#
<input id="btnSave" type="submit" value="Save" />
}
Model.State is invalid since name is rendered as label and httppost doesn't have that value.
This is where MetadataTypeAttribute comes in handy:
public class MyModel
{
public string Name { get; set; }
}
public interface IMyModelValidation
{
[Required]
public string Name { get; set; }
}
[MetadataType(typeof(IMyModelValiation))]
public class MyModelValidation : MyModel { }
Now MyModel has no validation and MyModelValidation does have validation, and they can be used almost interchangeably.
MetadataType
The MetadataTypeAttribute attribute enables you to associate a class with a data-model partial class. In this associated class you provide additional metadata information that is not in the data model.
For example, in the associated class you can apply the RequiredAttribute attribute to a data field. This enforces that a value is provided for the field even if this constraint is not required by the database schema.
You could use a different view model with different validation requirements for this 'read only' view. Or you could use the ModelState.Remove() method in your controller to get rid of errors against properties that you don't want validated. IMO the separate view model approach is better.
edit after seeing your code
Add a hiddenfor
#Html.DisplayFor(o => o.Name)
#Html.HiddenFor(o => o.Name)
That will pass the data back to the controller on the post and result in ModelState.IsValid == true
I'm not saying this is the best approach but I had to do something similar so I setup validation groups. I created an attribute that I placed on each model property that defined its validation group. Then on postback I called an extension method on the ViewDataDictionary and passed in the validation group that I wanted to run validation on. This would remove any validation messages for all other groups. Here is some example code:
The attribute:
/// <summary>
/// Attribute that assigns the property on a model to a given
/// validation group. By using the ValidationGroupExtension
/// and calling ValidateGroup on the ViewData validation errors
/// for properties that are not included in the validation group
/// will be removed.
/// </summary>
public class ValidationGroup : Attribute
{
/// <summary>
/// The unique name of the group.
/// </summary>
public String Group { get; set; }
public ValidationGroup(String group)
{
this.Group = group;
}
}
The extension:
/// <summary>
/// Used in conjunction with the ValidationGroup attribute to
/// specify which fields in a model should be validated. The
/// ValidateGroup extension should be called on ViewData before
/// checking whether the model is valid or not.
/// </summary>
public static class ValidationGroupExtension
{
/// <summary>
/// Remove all validation errors that are assocaited with
/// properties that do not have the ValidationGroup attribute
/// set with the specified group name.
///
/// This only handles flat models.
/// </summary>
/// <param name="viewData">View Data</param>
/// <param name="model">Data model returned</param>
/// <param name="group">Name of the validation group</param>
/// <returns></returns>
public static ViewDataDictionary ValidateGroup(this ViewDataDictionary viewData, Object model, String group)
{
//get all properties that have the validation group attribut set for the given group
var properties = model.GetType().GetProperties()
.Where(x => x.GetCustomAttributes(typeof(ValidationGroup), false)
.Where(a => ((ValidationGroup)a).Group == group).Count() > 0)
.Select(x => x.Name);
//find all properties that don't match these properties
var matchingProperties = viewData.ModelState.Where(x => !properties.Contains(x.Key)).ToList();
//remove any property that isn't in the gorup
foreach (var matchingProperty in matchingProperties)
{
viewData.ModelState.Remove(matchingProperty.Key);
}
return viewData;
}
}
On PostBack:
ViewData.ValidateGroup(model, "my validation group name");
if (ModelState.IsValid)
{
...
}
On the ViewModel:
[Required]
[DisplayName("Name")]
[ValidationGroup("my validation group name")]
public string Name { get; set; }
I want to allow multiple input-formats for a input field. In my case it is a Longitude field. In the DB I set it to decimal(18,10). It expects to recieve a comma-separated-value e.g. 16,2345678 but Google Maps or some Users are using a dot e.g. 16.2345678
I dont want to return an error but simply be glad and transform it to the expected db-format.
I tried to do it in my MetaData Validation Class (using Entity Framework)
public partial class Job
/*[Bind(Exclude = "ID")]*/
public class JobMetaData
{
public object Longitude
{
get { return this.Longitude; }
set { this.Longitude = value; /* Seems that this point isnt reached*/ }
}
but unfortunatly the setter is not called and the ViewState.isValid returns simply false.
[HttpPost]
public ActionResult Edit(Job model)
{
// parse the long-lat if needed here??
try
{
if (!ModelState.IsValid)
{
Where should I try to parse the value to allow both values (comma-separated and dot-separated) transform them and safe it.
I have the same issue for another field: I would like the user to enter 4 or 4€, in case just delete the €-sign and save it as a number to db.
Thanks for your help.
If you have same issue for another field.
as a best practise -
1] use ViewModel "JobViewModel"
2] so that If required you can create Custom validation attributes for your other properties
3] In Edit Post accept ViewModel.
[HttpPost]
public ActionResult Edit(Job model)
{
// parse the long-lat if needed here??
try
{
if (!ModelState.IsValid)
{
4] and if Modellstate isValid perform required parsing and then save it to database.
EDIT
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(JobViewModel jobviewModel)
{
try
{
if (!ModelState.IsValid)
{
jobviewModel.Longitude.replace(".",",")
-- save DB logic here
}
}
}
public class JobViewModel
{
public string Longitude{ get; set; }
}
For GET methods need to use use Automapper. or
jobviewModel.Longitude = model.Longitude
Use the string type and do whatever you want with it.
public partial class Job
{
public class JobMetaData
{
public string Longitude { get; set; }
}
}
How do you validate a class using Validation attributes when validating strongly typed view models.
Suppose you have a view model like so:
[PropertiesMustMatch("Admin.Password", "Admin.ConfirmPassword")]
public class AdminsEditViewModel
{
public AdminsEditViewModel()
{
this.Admin = new Admin(); // this is an Admin class
}
public IEnumerable<SelectListItem> SelectAdminsInGroup { get; set; }
public IEnumerable<SelectListItem> SelectAdminsNotInGroup { get; set; }
public Admin Admin { get; set; }
}
I get null exception when on this line of PropertiesMustMatchAttribute
object originalValue = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value);
since Password field is a property of Admin class and NOT AdminsEditViewModel. How do I make it so that it will go so many levels deep until it does find property of Admin in the ViewModel AdminsEditViewModel?
thanks
You need to modify the PropertiesMustMatchAttribute class to parse the property name and search deeply.
This attribute is not part of the framework; it's included in the default MVC template (in AccountModels.cs)
You can therefore modify it to suit your needs.
Specifically, you would call name.Split('.'), then loop through splitted names and get the property values.
It would look something like
object GetValue(object obj, string properties) {
foreach(strong prop in properties)
obj = TypeDescriptor.GetProperties(obj)
.Find(prop, ignoreCase: true)
.GetValue(obj);
}
return obj;
}