Required attribute not displaying - asp.net-mvc-3

Public Class Duration
{
[Required]
Public DurationUnit Unit
[Required]
Public int Length
}
Public Class Employee
{
[RequiredAttribute]
public virtual Duration NotificationLeadTime { get; set; }
}
The fields Unit and Length, when not suplied are getting highlighted in Red but the error message is not getting displayed.
I tries also giving [Required(ErrorMessage="sadfdsf")],but this is also not working.
I also tried inheriting the class with IValidatableObject but that also didn't work.
How to display the error message ?

You should use properties, not fields:
public class Duration
{
[Required]
public DurationUnit Unit { get; set; }
[Required]
public int Length { get; set; }
}
In order to display the corresponding error message use the Html.ValidationMessageFor helper.
For example:
#Html.EditorFor(x => x.NotificationLeadTime.Unit)
#Html.ValidationMessageFor(x => x.NotificationLeadTime.Unit)
By the way it doesn't really make sense to decorate a non-nullable type such as int with the [Required] attribute because those types always have a default value. You should make it a nullable integer instead. Same remark stands for the DurationUnit property if DurationUnit is an enum.

Related

Delta<T> in PATCH actions not tracking primitive types

I'm using Delta for patching an entity as outlined in "Partial Updates(PATCH request)" section outlined here
I have the following ProductDTO:
public class ProductDTO
{
public int ID { get; set; }
[Required]
public string Name { get; set; }
[UIHint("Date")]
[DataType(DataType.Date)]
public DateTime? ModifiedOn { get; set; }
public int Price { get; set; }
}
And the following action method defined:
public HttpResponseMessage Patch(int id, Delta<ProductDTO> delta)
{
return Request.CreateResponse(HttpStatusCode.NoContent);
}
If I pass in the following JSON (via Fiddler using the PATCH verb)
{"ID":1,"Name":"test","Price":"1000"}
The "delta" in the action method contains only the "Name" property and not the ID and Price properties. It appears that the delta is not containing values of types "int","decimal" and primitive types in general.
What am I missing here?
TIA
EDIT: Here is a link to a modded version of Delta that includes support for primitive JSON data types. Comments appreciated

Problems with Model binding and validation attributes with asp.net Web API

I am writing a Web API with ASP.NET Web API, and make use of the following View Model.
I seem to be having a problem with the data binding when there are two validation attributes on a particular property (i.e. [Required] and [StringLength(10)]).
When posting a JSON value from a client to a controller action of the form:
// POST api/list
public void Post([FromBody] TaskViewModel taskVM)
I observe the following:
If I remove one of the multiple attributes everything is bound OK;
If I leave in the multiple attributes, the client recieves a 500 internal server error and the body of the Post method is never reached.
Any ideas why this happens?
Cheers
public class TaskViewModel
{
//Default Constructor
public TaskViewModel() { }
public static TaskViewModel MakeTaskViewModel(Task task)
{
return new TaskViewModel(task);
}
//Constructor
private TaskViewModel(Task task)
{
this.TaskId = task.TaskID;
this.Description = task.Description;
this.StartDate = task.StartDate;
this.Status = task.Status;
this.ListID = task.ListID;
}
public Guid TaskId { get; set; }
[Required]
[StringLength(10)]
public string Description { get; set; }
[Required]
[DataType(DataType.DateTime)]
public System.DateTime StartDate { get; set; }
[Required]
public string Status { get; set; }
public System.Guid ListID { get; set; }
}
You need to inspect what is inside in the 500 internal server
make sure that you turn customerror off in your web.config
If you selfhost web.API you need to set GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
use your browser development console's network tab (in IE, Chrome you can get the console with F12) or if you are using FireFox then use FireBug or a thrid party tool like Fiddler.
Then you can see what went wrong on the server and go further to solve your problem.
In your case this is in the response:
"Message":"An error has occurred.","ExceptionMessage":"Property
'StartDate' on type 'MvcApplication3.Controllers.TaskViewModel' is
invalid. Value-typed properties marked as [Required] must also be
marked with [DataMember(IsRequired=true)] to be recognized as
required. Consider attributing the declaring type with [DataContract]
and the property with
[DataMember(IsRequired=true)].","ExceptionType":"System.InvalidOperationException"
So your problem is not that you have two attributes but that you've marked your properties with [Required] to solve this the exception tells you what to do.
You need to add [DataMember(IsRequired=true)] to your required properties where the property type is a value type (e.g int, datatime, etc.):
So change your TaskViewModel to:
[DataContract]
public class TaskViewModel
{
//Default Constructor
public TaskViewModel() { }
[DataMember]
public Guid TaskId { get; set; }
[Required]
[DataMember]
[StringLength(10)]
public string Description { get; set; }
[Required]
[DataMember(IsRequired = true)]
[DataType(DataType.DateTime)]
public System.DateTime StartDate { get; set; }
[Required]
[DataMember]
public string Status { get; set; }
[DataMember]
public System.Guid ListID { get; set; }
}
Some side notes:
You need to reference the System.Runtime.Serialization dll in order to use the DataMemberAttribute
You need to mark your class with [DataContract] and you need to mark all of its properties with [DataMember] not just the required ones.

Problems converting an entity property to HiddenField in MVC

I have a Supplier Entitiy that contains
ID - int
Status - string
Name - string
CreateDate- datetime
I am using the partial class method to create Data Annotations for the above Entity.as described here
[MetadataType(typeof(SupplierMetadata))]
public partial class Supplier
{
// Note this class has nothing in it. It's just here to add the class-level attribute.
}
public class SupplierMetadata
{
// Name the field the same as EF named the property - "FirstName" for example.
// Also, the type needs to match. Basically just redeclare it.
// Note that this is a field. I think it can be a property too, but fields definitely should work.
[HiddenInput]
public Int32 ID;
[Required]
[UIHint("StatusList")]
[Display(Name = "Status")]
public string Status;
[Required]
[Display(Name = "Supplier Name")]
public string Name;
}
the HiddenInput annotation throws an error saying "Attribute 'HiddenInput' is not valid on this declaration type. It is only valid on 'class, property, indexer' declarations."
Please help
The error states that that attribute can only be added to 'class, property, or indexer declarations'.
public Int32 ID; is none of these - it is a field.
If you change it to a property
public Int32 ID { get; set; } you will be able to decorate it with that attribute.
All of your properties are defined incorrectly as they are missing the get/set accessors. All properties in .NET require getter and/or setter accessors. Change your code to this:
public class SupplierMetadata
{
[HiddenInput]
public Int32 ID { get; set; }
[Required]
[UIHint("StatusList")]
[Display(Name = "Status")]
public string Status { get; set; }
[Required]
[Display(Name = "Supplier Name")]
public string Name { get; set; }
}

ASP.NET MVC3 Conditional Validation of nested model for EditorTemplate

Suppose you have a viewModel:
public class CreatePersonViewModel
{
[Required]
public bool HasDeliveryAddress {get;set;}
// Should only be validated when HasDeliveryAddress is true
[RequiredIf("HasDeliveryAddress", true)]
public Address Address { get; set; }
}
And the model Address will look like this:
public class Address : IValidatableObject
{
[Required]
public string City { get; set; }
[Required]
public string HouseNr { get; set; }
[Required]
public string CountryCode { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string ZipCode { get; set; }
[Required]
public string Street { get; set; }
#region IValidatableObject Members
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
string[] requiredFields;
var results = new List<ValidationResult>();
// some custom validations here (I removed them to keep it simple)
return results;
}
#endregion
}
Some would suggest to create a viewmodel for Address and add some custom logic there but I need an instance of Address to pass to my EditorTemplate for Address.
The main problem here is that the validation of Address is done before the validation of my PersonViewModel so I can't prevent it.
Note: the RequiredIfAttribute is a custom attribute which does just what I want for simple types.
Would have been a piece of cake if you had used FluentValidation.NET instead of DataAnnotations or IValidatableObject which limit the validation power quite in complex scenarios:
public class CreatePersonViewModelValidator : AbstractValidator<CreatePersonViewModel>
{
public CreatePersonViewModelValidator()
{
RuleFor(x => x.Address)
.SetValidator(new AddressValidator())
.When(x => x.HasDeliveryAddress);
}
}
Simon Ince has an alpha release of Mvc.ValidationToolkit which seems to be able to do what you want.
Update
As I understand it, the 'problem' lies in the DefaultModelBinder class, which validates your model on the basis that if it finds a validation attribute it asks it if the value is valid (quite reasonable really!), it has no notion of hierarchy. In order to support your required functionality you'll have to write a custom model binder that binds and then validates, if required, as determined by your declarative markup.
If you do write such a class it may be a good candidate for MVC futures.

Using Multiple Interfaces with MVC DataAnnotations and MetaDataType

I am applying validation using DataAnnotations to an MVC ViewModel which is a composite of several entity framework objects and some custom logic. The validation is already defined for the entity objects in interfaces, but how can I apply this validation to the ViewModel?
My initial idea was to combine the interfaces into one and apply the combined interface to the ViewModel, but this didn't work. Here's some sample code demonstrating what I mean:
// interfaces containing DataAnnotations implemented by entity framework classes
public interface IPerson
{
[Required]
[Display(Name = "First Name")]
string FirstName { get; set; }
[Required]
[Display(Name = "Last Name")]
string LastName { get; set; }
[Required]
int Age { get; set; }
}
public interface IAddress
{
[Required]
[Display(Name = "Street")]
string Street1 { get; set; }
[Display(Name = "")]
string Street2 { get; set; }
[Required]
string City { get; set; }
[Required]
string State { get; set; }
[Required]
string Country { get; set; }
}
// partial entity framework classes to specify interfaces
public partial class Person : IPerson {}
public partial class Address : IAddress {}
// combined interface
public interface IPersonViewModel : IPerson, IAddress {}
// ViewModel flattening a Person with Address for use in View
[MetadataType(typeof(IPersonViewModel))] // <--- This does not work.
public class PersonViewModel : IPersonViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public string Street1 { get; set; }
public string Street2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
}
My real-world problem involves about 150 properties on the ViewModel, so it's not as trivial as the sample and retyping all the properties seems like a horrible violation of DRY.
Any ideas on how to accomplish this?
In order for this to work you will need to manually associate the interfaces as metadata for your concrete classes.
I expected to be able to add multiple MetadataType attributes but that is not permitted.
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] // Notice AllowMultiple
public sealed class MetadataTypeAttribute : Attribute
Therefore, this gives a compilation error:
[MetadataType(typeof(IPerson))]
[MetadataType(typeof(IAddress))] // <--- Duplicate 'MetadataType' attribute
public class PersonViewModel : IPersonViewModel
However, it works if you only have one interface. So my solution to this was to simply associate the interfaces using a AssociatedMetadataTypeTypeDescriptionProvider and wrap that in another attribute.
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class MetadataTypeBuddyAttribute : Attribute
{
public MetadataTypeBuddyAttribute(Type modelType, Type buddyType)
{
TypeDescriptor.AddProviderTransparent(
new AssociatedMetadataTypeTypeDescriptionProvider(
modelType,
buddyType
),
modelType);
}
}
In my situation (MVC4) the data annotation attributes on my interfaces already worked. This is because my models directly implement the interfaces instead of having multi-level inheritance. However custom validation attributes implemented at the interface level do not work.
Only when manually associating the interfaces all the custom validations work accordingly. If I understand your case correctly this is also a solution for your problem.
[MetadataTypeBuddy(typeof(PersonViewModel), typeof(IPerson))]
[MetadataTypeBuddy(typeof(PersonViewModel), typeof(IAddress))]
public class PersonViewModel : IPersonViewModel
based on answer here, I couldn't somehow make that MetadataTypeBuddy attribute works. I'm sure that we must set somewhere that MVC should be calling that attribute. I managed to get it work when I run that attribute manually in Application_Start() like this
new MetadataTypeBuddyAttribute(typeof(PersonViewModel), typeof(IPerson));
new MetadataTypeBuddyAttribute(typeof(PersonViewModel), typeof(IAddress));
The MetadataTypeBuddy attribute did not work for me.
BUT adding "new" MetadataTypeBuddyAttribute in the "Startup" did work BUT it can lead to complex code where the developer is not aware to add this in the "Startup" for any new classes.
NOTE: You only need to call the AddProviderTransparent once at the startup of the app per class.
Here is a thread safe way of adding multiple Metadata types for a class.
[AttributeUsage(AttributeTargets.Class)]
public class MetadataTypeMultiAttribute : Attribute
{
private static bool _added = false;
private static readonly object padlock = new object();
public MetadataTypeMultiAttribute(Type modelType, params Type[] metaDataTypes)
{
lock (padlock)
{
if (_added == false)
{
foreach (Type metaDataType in metaDataTypes)
{
System.ComponentModel.TypeDescriptor.AddProviderTransparent(
new AssociatedMetadataTypeTypeDescriptionProvider(
modelType,
metaDataType
),
modelType);
}
_added = true;
}
}
}
}

Resources