Is there a way to make a data annotation conditional? I have a table Party where I store both organisations and persons. If I'm adding an organisation I don't want the field surname to be required, but only if I'm adding a person.
public class Party
{
[Required(ErrorMessage = "{0} is missing")]
[DisplayName("Your surname")]
public object surname { get; set; }
[DisplayName("Type")]
public object party_type { get; set; }
...
}
I'd like a condition for the required data annotation of surname, something like:
if (party_type=='P') then surname is required, else the surname can be empty.
EDIT
If I have to move this validation to the controller, how would I do it there? How can I trigger the same error message from there?
You can make your model inherit from IValidatableObject and then put your custom logic into the Validate method. You'll have to remove the RequredAttribute from the property as well. You will have to write some custom javascript to validate this rule on the client as the Validate method doesn't translate into the unobtrusive validation framework. Note I changed your properties to strings to avoid casting.
Also, if you have other validation errors from attributes, those will fire first and prevent the Validate method from being run so you only detect these errors if the attribute-based validation is ok.
public class Party : IValidatableObject
{
[DisplayName("Your surname")]
public string surname { get; set; }
[DisplayName("Type")]
public string party_type { get; set; }
...
public IEnumerable<ValidationResult> Validate( ValidationContext context )
{
if (party_type == "P" && string.IsNullOrWhitespace(surname))
{
yield return new ValidationResult("Surname is required unless the party is for an organization" );
}
}
}
On the client you can do something like:
<script type="text/javascript">
$(function() {
var validator = $('form').validate();
validator.rules('add', {
'surname': {
required: {
depends: function(element) {
return $('[name=party_type]').val() == 'P';
}
},
messages: {
required: 'Surname is required unless the party is for an organization.'
}
}
});
});
</script>
I know this topic has some time, but if you'd like to use only declarative validation for that, you could just use such a simple construction (see this reference for further possibilities):
[RequiredIf(DependentProperty = "party_type", TargetValue = "P")]
public string surname { get; set; }
public string party_type { get; set; }
Update:
Since the ExpressiveAnnotations 2.0, there is a breaking change. Now the same thing can be done in a simpler manner:
[RequiredIf("party_type == 'P'")]
public string surname { get; set; }
In Controller you can check like this:
Before if (ModelState.IsValid)
if (model.party_type == 'p')
{
this.ModelState.Remove("surname");
}
Related
I have this viewModel and I use Asp.net MVC and EF code First .
public class AddNewsVM
{
public string Title { get; set; }
public string TitleEn { get; set; }
public string Body { get; set; }
public string BodyEn { get; set; }
public string Author { get; set; }
public bool IsActive { get; set; }
public Guid ImageId { get; set; }
}
But user can Add EnNews(englisg) Or FaNews(persian) or both of them . I don't Add Required to all them . how can I validatie it . for example I want if use enter En News Title user should enter All En Field . do I use If statement in Action ?
It depends on your needs. If you need both (client / server) validations, I recommend to use:
https://github.com/JeremySkinner/FluentValidation
If you want to just validate your ViewModel on server side (class level validation), you can write your own custom validation rules using the IValidatable.
http://weblogs.asp.net/scottgu/class-level-model-validation-with-ef-code-first-and-asp-net-mvc-3
You can do client side validation like this
create one javascript validation metod.
perform all your custom validation, if validation fails return false at the end return true.
call the validation method on submit button "onclick" event
eg:- onclick="return myValidationFunction()"
I have made a remote validation in my project, to avoid duplicate entries in DB. My model class is like this
public class Supplier
{
public int SupplierId { get; set; }
public string SupplierName { get; set; }
[Required, DisplayName("Supplier Code")]
[Remote("ViCodeExists", "Supplier", "Vi Code is already exists.", AdditionalFields = "SupplierId")]
public string SupplierCode { get; set; }
}
And inside my SupplierController I have the function like this
public JsonResult ViCodeExists(string SupplierCode, int SupplierId = 0)
{
var user = _db.Suppliers.Where(x => x.SupplierCode == SupplierCode.Trim() && x.SupplierId != SupplierId);
return !user.Any() ?
Json(true, JsonRequestBehavior.AllowGet) :
Json(string.Format("{0} is already exists.", SupplierCode),
JsonRequestBehavior.AllowGet);
}
In my create View
#Html.TextBoxFor(model => model.SupplierCode)
#Html.ValidationMessageFor(model => model.SupplierCode)
Everything looks okay to me, but this validation does not works. I have tried adding breakpoint inside controller, But it never get hit. Can any one point out What I am doing wrong here?
Note: I have same type of validation in some other controllers in the
same project and they all work well. Issue is with this one only.
You using the overload of RemoteAttribute that accepts 3 string parameters where the 3rd parameter is the area name (not an error message).
Change the attribute to
[Remote("ViCodeExists", "Supplier", ErrorMessage = "Vi Code is already exists.", AdditionalFields = "SupplierId")]
public string SupplierCode { get; set; }
Note your overriding the error message in the methods return statement anyway, so you can probably omit it and just use
[Remote("ViCodeExists", "Supplier", AdditionalFields = "SupplierId")]
public string SupplierCode { get; set; }
Perhaps I'm missing something here, but it seems that anything in the object model tree 3 or more levels down, is ignored when using TryUpdateModel.
For example (simplified):
public virtual ActionResult SomeAction(int id, FormCollection form)
{
IValueProvider vpFrom = form.ToValueProvider();
/*
At this stage, vpForm contains:
1)PropertyA
2) PropertyB.SubPropertyA
3) PropertyB.SubPropertyB.SubSubPropertyA
*/
TryUpdateModel(someObjectModel, null, null, null, vpFrom);
//The first two properties are applied, number (3) seems to be ignored
Am I missing something here? If this is just the way it is, has anyone come up with a workaround?
A quick project created with the following model.
public class TestModel {
public TestModelA A { get; set; }
public string Name { get; set; }
}
public class TestModelA {
public TestModelB B { get; set; }
public string Name { get; set; }
}
public class TestModelB {
public TestModelC C { get; set; }
public string Name { get; set; }
}
public class TestModelC {
public TestModelD D { get; set; }
public string Name { get; set; }
}
public class TestModelD {
public TestModelE E { get; set; }
public string Name { get; set; }
}
public class TestModelE {
public string Name { get; set; }
}
Here's my edit - which is essentially the same as yours
[HttpPost]
public ActionResult Edit(FormCollection form) {
IValueProvider vpFrom = form.ToValueProvider();
Models.TestModel t = new Models.TestModel();
TryUpdateModel(t, null, null, null, vpFrom);
return View(t);
}
This all works exactly as expected with all the models created properly. The only problem that I can see happening is that you possibly aren't passing the same property names back from the form. (by not using <%: Html.TextBoxFor(model => model.A.B.C.CName)%> for example)
The models require parameterless constructors. But I'm sure you would have gotten an error about that - unless you're consuming the error.
So without more information about your project it will be hard to help as a basic setup produces expected results.
I believe the problem is in one of your model classes. Check, please, if PropertyB.SubPropertyB.SubSubPropertyA is really a property but not a field. A property should have get and set accessors.
Here's my checklist:
Make sure you're getting the value back in the form request. Request["A.B.C.Name"] and etc.
All the required fields are on the form.
I had deleteOnNull issue with Linq to SQL: How to set DeleteOnNull from designer for future ref if you're using L2SQL.
Let's say you have an object called Person that looks like this:
class Person
{
public int ID { get; set; }
public string Name { get; set; }
public int NumberOfCatsNamedEnder { get; set; }
}
I have a simple HTML form that exposes the properties that gets posted to an ASP.NET MVC action inside of my PersonController class. The issue I have is that if someone puts in the letter 'A' for NumberOfCatsNamedEnder, I get a The model of type 'Person' was not successfully updated. error. Since this happens while trying to update the Model, I can't find any way to check to see if someone passed in a non-integer value without resorting to
if(!IsInteger(formCollection["NumberOfCatsNamedEnder"]))
{
ModelState.AddModelError(
"NumberOfCatsNamedEnder",
"Ender count should be a number");
}
Is there a better way to do this? I was able to find some information on custom ModelBinders; is that what is needed?
I really like the approach of using a presentation model. I'd create a class like this:
class PersonPresentation
{
public int ID { get; set; }
public string Name { get; set; }
public string NumberOfCatsNamedEnder { get; set; }
public void FromPerson(Person person){ /*Load data from person*/ }
}
Then your controller action can bind the view to a PersonPresentation:
public ActionResult Index()
{
Person person = GetPerson();
PersonPresentation presentation = new PersonPresentation();
ViewData.Model = presentation.FromPerson(person);
return View();
}
...and then accept one in your Update method and perform validation:
public ActionResult Update(PersonPresentation presentation)
{
if(!IsInteger(presentation.NumberOfCatsNamedEnder))
{
ModelState.AddModelError(
"NumberOfCatsNamedEnder",
"Ender count should be a number");
}
...
}
i going to create some validation for custom object in my app. But i have some trouble when try to create CustomValidation rule. My object has field - BirthDay - which not required but if user enter it i need to validate it on simple validation, for example user DataType validation - DataType.DateTime. When i am try to do it i have validation error - BirthDay is required. When i create custom validation and always return true i have same error. Below some lines of code:
[MetadataType(typeof(User.Metadata))]
public class User
{
#region Metadata
private class Metadata
{
[Required(ErrorMessage="Name is required")]
[StringLength(5, ErrorMessage="Max Length is 5")]
public string Name { get; set; }
[CustomValidation(typeof(User), "ValidateBirthDay", ErrorMessage="We have trouble.")]
public DateTime BirthDay { get; set; }
}
#endregion
public static bool ValidateBirthDay(object value)
{
return true;
}
public int? ID { get; set; }
public string Name { get; set; }
public DateTime BirthDay { get; set; }
}
p.s. sorry for my English =)
You need to make your propery nullable, ie
public DateTime? BirthDay { get; set; }
so it can have a null value and not required to be set.
Also the way you use the CustomValidation attribute doesn't seem right. I believe you need to create a class that derives from ValidationAttribute base class and pass its type in CustomValidation attribute's first param.