MVC 3: Use a VERY custom format for a Date with automapping on server-side - asp.net-mvc-3

I have a very confusing problem, and I am able to solve it, but there has to be a Out-Of-The-Box-Solution.
The situation is as following:
An user inputs on the client-side a date (i.e. with a date-picker) which outputs the following value to the textbox:
Mi 22.02.2012 (KW 8)
I have a Button which posts my form with this textbox to an controller-action.
Let's say you have the following controller action which takes a MyType as Parameter
[HttpPost]
public ActionResult Create(MyType model)
{
//model.Date is always null
//(or in case the DateTime is not Nullable<T>, the value is DateTime.Min
}
public class MyType
{
public DateTime? Date { get; set; }
}
The question is simple:
How do I map the input to the DateTime?
I do not want to add properties to my class like in the following example:
public class IDontWantThis
{
public DateTime? Date { get { /* Some custom string parsing with DateText */ } }
public string DateText { get; set; } //this would be the auto-mapped property
}

How a date is displayed in the view is of no concern to the controller.
I would add a hidden field with the name of the property you want the datetime to map on the viewmodel (Date). On the client, using javascript, you can format this date any way you want, as long as the hidden field contains the datetime you need to work with.
So let the datepicker store the selected date twice, once with your weird date formatting for display and another time in a hidden field in a common format the modelbinder will understand.

This is what ViewModels are for. I understand that you don't want your domain Model to include a DateText member, but you really should consider something like this:
public class MyType
{
public DateTime? Date { get; set; }
}
public class MyTypeViewModel
{
public MyType MyType { get; set; }
public string DateText { get; set; }
}
What your requirement is one of the main points that makes ViewModels so powerful. Using an intermediate class between your View and your Model to link them together. This way, you won't have any trace of "formatting" data in your Model (MyType).

Related

Ignore DateTime prop from being serialized (not working)

i have tried the solution proposed in this solution
i have a class
public class MyClass
{
[IgnoreDataMember]
public DateTime? Date { get; set; }
}
when the JavaScriptSerializer serializes the result it changes the date time for example if i have 2012-07-20 in my database after the serialization it is returned as 2012-07-21 because the application is hosted at a location that is outside my time zone i am facing a lot of problems because of it
You could use the [ScriptIgnore] attribute to exclude properties from being serialized with the JavaScriptSerializer:
public class MyClass
{
[ScriptIgnore]
public DateTime? Date { get; set; }
... some other properties
}
This being said, the correct way to do this is to use a view model that simply won't contain the property that you don't want to be contained in the response and then return this view model instead of torturing your domain models with plumbing code about serialization which they should be absolutely agnostic of.

Why my view is not rendering DateTime

I have following Model Class
public class CandidateViewModel
{
public string cName { get; set; }
public DateTime dob { get; set; }
public DateTime dod { get; set; }
}
my view which is based on this model class does not render TextBoxes for DateTime type. It does render Labels bot not input types. My view model code seems perfectly ok
When I change DateTime Type to String or int etc. then the input boxes are rendered.
Please note that this Model class is not part of the EDM.
I found the answer
I found the answer to my own question. I had a DateTime Template under shared folder. I deleted (not excluded) this folder from the project, and no where datetime was rendering HTML. I went to the actual folder on drive C, and physically deleted the Template folder and every thing started working fine.

Can AutoMapper map object to model property of the same type

I am attempting to improve my data flow between my MVC 3 Model and Views (mainly CRUD). I have taken the approach of using ViewModels and FormModels. My ViewModel contains everything it need to represent the view FormData, DropDownLists etc. The FormModel simply contains the FormData fields that are submitted by the form and are needed to update a record.
My question is can I use AutoMapper to map UserDto information onto my FormData field in my ViewModel?
Obviously my mapping below is only mapping between the two object and not an object to property but I have tried using the ‘.ForMember’ mapping options but they are again for object members not an object to an object member. I have also looked at Custom Type Convertors but not sure if this is the right way to go.
Mapper.CreateMap<UserDto, UserViewModel>();
Mapper.CreateMap<UserViewModel, UserDto>();
public class UserViewModel
{
public User FormData { get; set; }
// DropDownLists
// Other view specific data
}
public class UserFormModel
{
public int UserId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Age { get; set; }
[Required]
public string Email { get; set; }
}
Any help would be much appreciated.
You need to create the map to the FormData property type and then tell AutoMapper to use this map.
(The following will likely not compile; I'm in the process of recreating my work machine and am working from memory).
Mapper.CreateMap<UserDto, User>(); // set up property mapping
Mapper.CreateMap<UserDto, UserViewModel>()
.ForMember(vm => vm.FormData, map => map.MapFrom(dto => Mapper.Map<UserDto, User>(dto)));

MVC3 / EF CustomValidator two fields in model

Using MVC3 and EF4.1 how do I validate on client and server more than one field in my view model?
I have a start date text box (that can be modified) and I have the original start date in a hidden field. When the user submits the form I want to check that the modied start date is no more than one month either side of the original start date.
I can't figure out how this can be done with DataAnnotation and CustomValidation (or maybe I'm going down the wrong road)? This is an example of whay I've been working with:
[MetadataType(typeof(Metadata.MyUserMetaData))]
public partial class MyUser
{
public System.DateTime DateOfBirth { get; set; }
}
Partial Class
public class MyUserMetaData
{
[CustomValidation(typeof(AmendedStartDate), "amendedstartdate", ErrorMessage = "Invalid date."]
public DateTime StartDate { get; set; };
public DateTime OriginalStartDate { get; set; };
}
Custom Validator
public class AmendedStartDate : ValidationAttribute, IClientValidatable
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
// How do I get multiple field values from object value?
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(Modelmetadata metadate, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "amendedstartdate"
};
yield return rule;
}
}
I know I've still to add jQuery to the view for this validator.
Instead of using data annotations implement IValidatableObject on your model class - it is simpler and much more clear in scenarios with cross validation.
If you still want to use ValidationAttribute you have two parameters in the IsValid method:
value represents validated value of the property where the attribute is assigned
context is context in which the property is validated. It also contains ObjectInstance and ObjectType properties to access the whole model and its type so you can cast the instance and access other properties.
The question asked in MVC custom validation: compare two dates has an example of a validator which compares to a second value in the model. That should get you started.

Custom model validation of dependent properties using Data Annotations

Since now I've used the excellent FluentValidation
library to validate my model classes. In web applications I use it in conjunction with the jquery.validate plugin to perform client side validation as well.
One drawback is that much of the validation logic is repeated on the client side and is no longer centralized at a single place.
For this reason I'm looking for an alternative. There are many examples out there showing the usage of data annotations to perform model validation. It looks very promising.
One thing I couldn't find out is how to validate a property that depends on another property value.
Let's take for example the following model:
public class Event
{
[Required]
public DateTime? StartDate { get; set; }
[Required]
public DateTime? EndDate { get; set; }
}
I would like to ensure that EndDate is greater than StartDate. I could write a custom
validation attribute extending ValidationAttribute in order to perform custom validation logic. Unfortunately I couldn't find a way to obtain the
model instance:
public class CustomValidationAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
// value represents the property value on which this attribute is applied
// but how to obtain the object instance to which this property belongs?
return true;
}
}
I found that the CustomValidationAttribute seems to do the job because it has this ValidationContext property that contains the object instance being validated. Unfortunately this attribute has been added only in .NET 4.0. So my question is: can I achieve the same functionality in .NET 3.5 SP1?
UPDATE:
It seems that FluentValidation already supports clientside validation and metadata in ASP.NET MVC 2.
Still it would be good to know though if data annotations could be used to validate dependent properties.
MVC2 comes with a sample "PropertiesMustMatchAttribute" that shows how to get DataAnnotations to work for you and it should work in both .NET 3.5 and .NET 4.0. That sample code looks like this:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public sealed class PropertiesMustMatchAttribute : ValidationAttribute
{
private const string _defaultErrorMessage = "'{0}' and '{1}' do not match.";
private readonly object _typeId = new object();
public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty)
: base(_defaultErrorMessage)
{
OriginalProperty = originalProperty;
ConfirmProperty = confirmProperty;
}
public string ConfirmProperty
{
get;
private set;
}
public string OriginalProperty
{
get;
private set;
}
public override object TypeId
{
get
{
return _typeId;
}
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
OriginalProperty, ConfirmProperty);
}
public override bool IsValid(object value)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
object originalValue = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value);
object confirmValue = properties.Find(ConfirmProperty, true /* ignoreCase */).GetValue(value);
return Object.Equals(originalValue, confirmValue);
}
}
When you use that attribute, rather than put it on a property of your model class, you put it on the class itself:
[PropertiesMustMatch("NewPassword", "ConfirmPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public class ChangePasswordModel
{
public string NewPassword { get; set; }
public string ConfirmPassword { get; set; }
}
When "IsValid" gets called on your custom attribute, the whole model instance is passed to it so you can get the dependent property values that way. You could easily follow this pattern to create a date comparison attribute, or even a more general comparison attribute.
Brad Wilson has a good example on his blog showing how to add the client-side portion of the validation as well, though I'm not sure if that example will work in both .NET 3.5 and .NET 4.0.
I had this very problem and recently open sourced my solution:
http://foolproof.codeplex.com/
Foolproof's solution to the example above would be:
public class Event
{
[Required]
public DateTime? StartDate { get; set; }
[Required]
[GreaterThan("StartDate")]
public DateTime? EndDate { get; set; }
}
Instead of the PropertiesMustMatch the CompareAttribute that can be used in MVC3. According to this link http://devtrends.co.uk/blog/the-complete-guide-to-validation-in-asp.net-mvc-3-part-1:
public class RegisterModel
{
// skipped
[Required]
[ValidatePasswordLength]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation do not match.")]
public string ConfirmPassword { get; set; }
}
CompareAttribute is a new, very useful validator that is not actually
part of
System.ComponentModel.DataAnnotations,
but has been added to the
System.Web.Mvc DLL by the team. Whilst
not particularly well named (the only
comparison it makes is to check for
equality, so perhaps EqualTo would be
more obvious), it is easy to see from
the usage that this validator checks
that the value of one property equals
the value of another property. You can
see from the code, that the attribute
takes in a string property which is
the name of the other property that
you are comparing. The classic usage
of this type of validator is what we
are using it for here: password
confirmation.
It took a little while since your question was asked, but if you still like metadata (at least sometimes), below there is yet another alternative solution, which allows you provide various logical expressions to the attributes:
[Required]
public DateTime? StartDate { get; set; }
[Required]
[AssertThat("StartDate != null && EndDate > StartDate")]
public DateTime? EndDate { get; set; }
It works for server as well as for client side. More details can be found here.
Because the methods of the DataAnnotations of .NET 3.5 don't allow you to supply the actual object validated or a validation context, you will have to do a bit of trickery to accomplish this. I must admit I'm not familiar with ASP.NET MVC, so I can't say how to do this exactly in conjunction with MCV, but you can try using a thread-static value to pass the argument itself. Here is an example with something that might work.
First create some sort of 'object scope' that allows you to pass objects around without having to pass them through the call stack:
public sealed class ContextScope : IDisposable
{
[ThreadStatic]
private static object currentContext;
public ContextScope(object context)
{
currentContext = context;
}
public static object CurrentContext
{
get { return context; }
}
public void Dispose()
{
currentContext = null;
}
}
Next, create your validator to use the ContextScope:
public class CustomValidationAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
Event e = (Event)ObjectContext.CurrentContext;
// validate event here.
}
}
And last but not least, ensure that the object is past around through the ContextScope:
Event eventToValidate = [....];
using (var scope new ContextScope(eventToValidate))
{
DataAnnotations.Validator.Validate(eventToValidate);
}
Is this useful?

Resources