How to use Data Annotation Validators in Winforms? - asp.net-mvc

I like the Validation Application Block from the Enterprise Library :-)
Now i would like to use the DataAnnotations in Winforms, as we use asp.net Dynamic Data as well. So that we have common technologies over the whole company.
And also the Data Annotations should be easier to use.
How can I do something similiar in Winforms like Stephen Walter did within asp.net MVC?

I adapted a solution found at http://blog.codeville.net/category/validation/page/2/
public class DataValidator
{
public class ErrorInfo
{
public ErrorInfo(string property, string message)
{
this.Property = property;
this.Message = message;
}
public string Message;
public string Property;
}
public static IEnumerable<ErrorInfo> Validate(object instance)
{
return from prop in instance.GetType().GetProperties()
from attribute in prop.GetCustomAttributes(typeof(ValidationAttribute), true).OfType<ValidationAttribute>()
where !attribute.IsValid(prop.GetValue(instance, null))
select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty));
}
}
This would allow you to use the following code to validate any object using the following syntax:
var errors = DataValidator.Validate(obj);
if (errors.Any()) throw new ValidationException();

Related

How to bind view model property with different name

Is there a way to make a reflection for a view model property as an element with different name and id values on the html side.
That is the main question of what I want to achieve. So the basic introduction for the question is like:
1- I have a view model (as an example) which created for a filter operation in view side.
public class FilterViewModel
{
public string FilterParameter { get; set; }
}
2- I have a controller action which is created for GETting form values(here it is filter)
public ActionResult Index(FilterViewModel filter)
{
return View();
}
3- I have a view that a user can filter on some data and sends parameters via querystring over form submit.
#using (Html.BeginForm("Index", "Demo", FormMethod.Get))
{
#Html.LabelFor(model => model.FilterParameter)
#Html.EditorFor(model => model.FilterParameter)
<input type="submit" value="Do Filter" />
}
4- And what I want to see in rendered view output is
<form action="/Demo" method="get">
<label for="fp">FilterParameter</label>
<input id="fp" name="fp" type="text" />
<input type="submit" value="Do Filter" />
</form>
5- And as a solution I want to modify my view model like this:
public class FilterViewModel
{
[BindParameter("fp")]
[BindParameter("filter")] // this one extra alias
[BindParameter("param")] //this one extra alias
public string FilterParameter { get; set; }
}
So the basic question is about BindAttribute but the usage of complex type properties. But also if there is a built in way of doing this is much better.
Built-in pros:
1- Usage with TextBoxFor, EditorFor, LabelFor and other strongly typed view model helpers can understand and communicate better with each other.
2- Url routing support
3- No framework by desing problems :
In general, we recommend folks don’t write custom model binders
because they’re difficult to get right and they’re rarely needed. The
issue I’m discussing in this post might be one of those cases where
it’s warranted.
Link of quote
And also after some research I found these useful works:
Binding model property with different name
One step upgrade of first link
Here some informative guide
Result: But none of them give me my problems exact solution. I am looking for a strongly typed solution for this problem. Of course if you know any other way to go, please share.
Update
The underlying reasons why I want to do this are basically:
1- Everytime I want to change the html control name then I have to change PropertyName at compile time. (There is a difference Changing a property name between changing a string in code)
2- I want to hide (camouflage) real property names from end users. Most of times View Model property names same as mapped Entity Objects property names. (For developer readability reasons)
3- I don't want to remove the readability for developer. Think about lots of properties with like 2-3 character long and with mo meanings.
4- There are lots of view models written. So changing their names are going to take more time than this solution.
5- This is going to be better solution (in my POV) than others which are described in other questions until now.
Actually, there is a way to do it.
In ASP.NET binding metadata gathered by TypeDescriptor, not by reflection directly. To be more precious, AssociatedMetadataTypeTypeDescriptionProvider is used, which, in turn, simply calls TypeDescriptor.GetProvider with our model type as parameter:
public AssociatedMetadataTypeTypeDescriptionProvider(Type type)
: base(TypeDescriptor.GetProvider(type))
{
}
So, everything we need is to set our custom TypeDescriptionProvider for our model.
Let's implement our custom provider. First of all, let's define attribute for custom property name:
[AttributeUsage(AttributeTargets.Property)]
public class CustomBindingNameAttribute : Attribute
{
public CustomBindingNameAttribute(string propertyName)
{
this.PropertyName = propertyName;
}
public string PropertyName { get; private set; }
}
If you already have attribute with desired name, you can reuse it. Attribute defined above is just an example. I prefer to use JsonPropertyAttribute because in most cases I work with json and Newtonsoft's library and want to define custom name only once.
The next step is to define custom type descriptor. We will not implement whole type descriptor logic and use default implementation. Only property accessing will be overridden:
public class MyTypeDescription : CustomTypeDescriptor
{
public MyTypeDescription(ICustomTypeDescriptor parent)
: base(parent)
{
}
public override PropertyDescriptorCollection GetProperties()
{
return Wrap(base.GetProperties());
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return Wrap(base.GetProperties(attributes));
}
private static PropertyDescriptorCollection Wrap(PropertyDescriptorCollection src)
{
var wrapped = src.Cast<PropertyDescriptor>()
.Select(pd => (PropertyDescriptor)new MyPropertyDescriptor(pd))
.ToArray();
return new PropertyDescriptorCollection(wrapped);
}
}
Also custom property descriptor need to be implemented. Again, everything except property name will be handled by default descriptor. Note, NameHashCode for some reason is a separate property. As name changed, so it's hash code need to be changed too:
public class MyPropertyDescriptor : PropertyDescriptor
{
private readonly PropertyDescriptor _descr;
private readonly string _name;
public MyPropertyDescriptor(PropertyDescriptor descr)
: base(descr)
{
this._descr = descr;
var customBindingName = this._descr.Attributes[typeof(CustomBindingNameAttribute)] as CustomBindingNameAttribute;
this._name = customBindingName != null ? customBindingName.PropertyName : this._descr.Name;
}
public override string Name
{
get { return this._name; }
}
protected override int NameHashCode
{
get { return this.Name.GetHashCode(); }
}
public override bool CanResetValue(object component)
{
return this._descr.CanResetValue(component);
}
public override object GetValue(object component)
{
return this._descr.GetValue(component);
}
public override void ResetValue(object component)
{
this._descr.ResetValue(component);
}
public override void SetValue(object component, object value)
{
this._descr.SetValue(component, value);
}
public override bool ShouldSerializeValue(object component)
{
return this._descr.ShouldSerializeValue(component);
}
public override Type ComponentType
{
get { return this._descr.ComponentType; }
}
public override bool IsReadOnly
{
get { return this._descr.IsReadOnly; }
}
public override Type PropertyType
{
get { return this._descr.PropertyType; }
}
}
Finally, we need our custom TypeDescriptionProvider and way to bind it to our model type. By default, TypeDescriptionProviderAttribute is designed to perform that binding. But in this case we will not able to get default provider that we want to use internally. In most cases, default provider will be ReflectTypeDescriptionProvider. But this is not guaranteed and this provider is inaccessible due to it's protection level - it's internal. But we do still want to fallback to default provider.
TypeDescriptor also allow to explicitly add provider for our type via AddProvider method. That what we will use. But firstly, let's define our custom provider itself:
public class MyTypeDescriptionProvider : TypeDescriptionProvider
{
private readonly TypeDescriptionProvider _defaultProvider;
public MyTypeDescriptionProvider(TypeDescriptionProvider defaultProvider)
{
this._defaultProvider = defaultProvider;
}
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
return new MyTypeDescription(this._defaultProvider.GetTypeDescriptor(objectType, instance));
}
}
The last step is to bind our provider to our model types. We can implement it in any way we want. For example, let's define some simple class, such as:
public static class TypeDescriptorsConfig
{
public static void InitializeCustomTypeDescriptorProvider()
{
// Assume, this code and all models are in one assembly
var types = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.GetProperties().Any(p => p.IsDefined(typeof(CustomBindingNameAttribute))));
foreach (var type in types)
{
var defaultProvider = TypeDescriptor.GetProvider(type);
TypeDescriptor.AddProvider(new MyTypeDescriptionProvider(defaultProvider), type);
}
}
}
And either invoke that code via web activation:
[assembly: PreApplicationStartMethod(typeof(TypeDescriptorsConfig), "InitializeCustomTypeDescriptorProvider")]
Or simply call it in Application_Start method:
public class MvcApplication : HttpApplication
{
protected void Application_Start()
{
TypeDescriptorsConfig.InitializeCustomTypeDescriptorProvider();
// rest of init code ...
}
}
But this is not the end of the story. :(
Consider following model:
public class TestModel
{
[CustomBindingName("actual_name")]
[DisplayName("Yay!")]
public string TestProperty { get; set; }
}
If we try to write in .cshtml view something like:
#model Some.Namespace.TestModel
#Html.DisplayNameFor(x => x.TestProperty) #* fail *#
We will get ArgumentException:
An exception of type 'System.ArgumentException' occurred in System.Web.Mvc.dll but was not handled in user code
Additional information: The property Some.Namespace.TestModel.TestProperty could not be found.
That because all helpers soon or later invoke ModelMetadata.FromLambdaExpression method. And this method take expression we provided (x => x.TestProperty) and takes member name directly from member info and have no clue about any of our attributes, metadata (who cares, huh?):
internal static ModelMetadata FromLambdaExpression<TParameter, TValue>(/* ... */)
{
// ...
case ExpressionType.MemberAccess:
MemberExpression memberExpression = (MemberExpression) expression.Body;
propertyName = memberExpression.Member is PropertyInfo ? memberExpression.Member.Name : (string) null;
// I want to cry here - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// ...
}
For x => x.TestProperty (where x is TestModel) this method will return TestProperty, not actual_name, but model metadata contains actual_name property, have no TestProperty. That is why the property could not be found error thrown.
This is a design failure.
However despite this little inconvenience there are several workarounds, such as:
The easiest way is to access our members by theirs redefined names:
#model Some.Namespace.TestModel
#Html.DisplayName("actual_name") #* this will render "Yay!" *#
This is not good. No intellisense at all and as our model change we will have no any compilation errors. On any change anything can be broken and there is no easy way to detect that.
Another way is a bit more complex - we can create our own version of that helpers and forbid anybody from calling default helpers or ModelMetadata.FromLambdaExpression for model classes with renamed properties.
Finally, combination of previous two would be preferred: write own analogue to get property name with redefinition support, then pass that into default helper. Something like this:
#model Some.Namespace.TestModel
#Html.DisplayName(Html.For(x => x.TestProperty))
Compilation-time and intellisense support and no need to spend a lot of time for complete set of helpers. Profit!
Also everything described above work like a charm for model binding. During model binding process default binder also use metadata, gathered by TypeDescriptor.
But I guess binding json data is the best use case. You know, lots of web software and standards use lowercase_separated_by_underscores naming convention. Unfortunately this is not usual convention for C#. Having classes with members named in different convention looks ugly and can end up in troubles. Especially when you have tools that whining every time about naming violation.
ASP.NET MVC default model binder does not bind json to model the same way as it happens when you call newtonsoft's JsonConverter.DeserializeObject method. Instead, json parsed into dictionary. For example:
{
complex: {
text: "blabla",
value: 12.34
},
num: 1
}
will be translated into following dictionary:
{ "complex.text", "blabla" }
{ "complex.value", "12.34" }
{ "num", "1" }
And later these values along with others values from query string, route data and so on, collected by different implementations of IValueProvider, will be used by default binder to bind a model with help of metadata, gathered by TypeDescriptor.
So we came full circle from creating model, rendering, binding it back and use it.
The short answer is NO and long answer still NO. There is no built-in helper, attribute, model binder, whatever is it (Nothing out of box).
But what I did in before answer (I deleted it) was an awful solution that I realized yesterday. I am going to put it in github for who still wants to see (maybe it solves somebody problem) (I don't suggest it also!)
Now I searched it for again and I couldn't find anything helpful. If you are using something like AutoMapper or ValueInjecter like tool for mapping your ViewModel objects to Business objects and if you want to obfuscate that View Model parameters also, probably you are in some trouble. Of course you can do it but strongly typed html helpers are not going to help you alot. I even not talking about the if other developers taking branch and working over common view models.
Luckily my project (4 people working on it, and its commercial use for) not that big for now, so I decided to change View Model property names! (It is still lot work to do. Hundreds of view models to obfuscate their properties!!!) Thank you Asp.Net MVC !
There some ways in the links which I gave in question. But also if you still want to use the BindAlias attribute, I can only suggest you to use the following extension methods. At least you dont have to write same alias string which you write in BindAlias attribute.
Here it is:
public static string AliasNameFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression)
{
var memberExpression = ExpressionHelpers.GetMemberExpression(expression);
if (memberExpression == null)
throw new InvalidOperationException("Expression must be a member expression");
var aliasAttr = memberExpression.Member.GetAttribute<BindAliasAttribute>();
if (aliasAttr != null)
{
return MvcHtmlString.Create(aliasAttr.Alias).ToHtmlString();
}
return htmlHelper.NameFor(expression).ToHtmlString();
}
public static string AliasIdFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression)
{
var memberExpression = ExpressionHelpers.GetMemberExpression(expression);
if (memberExpression == null)
throw new InvalidOperationException("Expression must be a member expression");
var aliasAttr = memberExpression.Member.GetAttribute<BindAliasAttribute>();
if (aliasAttr != null)
{
return MvcHtmlString.Create(TagBuilder.CreateSanitizedId(aliasAttr.Alias)).ToHtmlString();
}
return htmlHelper.IdFor(expression).ToHtmlString();
}
public static T GetAttribute<T>(this ICustomAttributeProvider provider)
where T : Attribute
{
var attributes = provider.GetCustomAttributes(typeof(T), true);
return attributes.Length > 0 ? attributes[0] as T : null;
}
public static MemberExpression GetMemberExpression<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression)
{
MemberExpression memberExpression;
if (expression.Body is UnaryExpression)
{
var unaryExpression = (UnaryExpression)expression.Body;
memberExpression = (MemberExpression)unaryExpression.Operand;
}
else
{
memberExpression = (MemberExpression)expression.Body;
}
return memberExpression;
}
When you want to use it:
[ModelBinder(typeof(AliasModelBinder))]
public class FilterViewModel
{
[BindAlias("someText")]
public string FilterParameter { get; set; }
}
In html:
#* at least you dont write "someText" here again *#
#Html.Editor(Html.AliasNameFor(model => model.FilterParameter))
#Html.ValidationMessage(Html.AliasNameFor(model => model.FilterParameter))
So I am leaving this answer here like this. This is even not an answer (and there is no answer for MVC 5) but who searching in google for same problem might find useful this experience.
And here is the github repo: https://github.com/yusufuzun/so-view-model-bind-20869735

Automapper + EF4 + ASP.NET MVC - getting 'context disposed' error (I know why, but how to fix it?)

I have this really basic code in a MVC controller action. It maps an Operation model class to a very basic OperationVM view-model class .
public class OperationVM: Operation
{
public CategoryVM CategoryVM { get; set; }
}
I need to load the complete list of categories in order to create a CategoryVM instance.
Here's how I (try to) create a List<OperationVM> to show in the view.
public class OperationsController : Controller {
private SomeContext context = new SomeContext ();
public ViewResult Index()
{
var ops = context.Operations.Include("blah...").ToList();
Mapper.CreateMap<Operation, OperationVM>()
.ForMember(
dest => dest.CategoryVM,
opt => opt.MapFrom(
src => CreateCatVM(src.Category, context.Categories)
// trouble here ----------------^^^^^^^
)
);
var opVMs = ops.Select(op => Mapper.Map<Operation, OperationVM>(op))
.ToList();
return View(opVMs);
}
}
All works great first time I hit the page. The problem is, the mapper object is static. So when calling Mapper.CreateMap(), the instance of the current DbContext is saved in the closure given to CreateMap().
The 2nd time I hit the page, the static map is already in place, still using the reference to the initial, now disposed, DbContext.
The exact error is:
The operation cannot be completed because the DbContext has been disposed.
The question is: How can I make AutoMapper always use the current context instead of the initial one?
Is there a way to use an "instance" of automapper instead of the static Mapper class?
If this is possible, is it recommended to re-create the mapping every time? I'm worried about reflection slow-downs.
I read a bit about custom resolvers, but I get a similar problem - How do I get the custom resolver to use the current context?
It is possible, but the setup is a bit complicated. I use this in my projects with help of Ninject for dependency injection.
AutoMapper has concept of TypeConverters. Converters provide a way to implement complex operations required to convert certain types in a separate class. If converting Category to CategoryVM requires a database lookup you can implement that logic in custom TypeConverter class similar to this:
using System;
using AutoMapper;
public class CategoryToCategoryVMConverter :
TypeConverter<Category, CategoryVM>
{
public CategoryToCategoryVMConverter(DbContext context)
{
this.Context = context;
}
private DbContext Context { get; set; }
protected override CategoryVM ConvertCore(Category source)
{
// use this.Context to lookup whatever you need
return CreateCatVM(source, this.Context.Categories);
}
}
You then to configure AutoMapper to use your converter:
Mapper.CreateMap<Category, CategoryVM>().ConvertUsing<CategoryToCategoryVMConverter>();
Here comes the tricky part. AutoMapper will need to create a new instance of our converter every time you map values, and it will need to provide DbContext instance for constructor. In my projects I use Ninject for dependency injection, and it is configured to use the same instance of DbContext while processing a request. This way the same instance of DbContext is injected both in your controller and in your AutoMapper converter. The trivial Ninject configuration would look like this:
Bind<DbContext>().To<SomeContext>().InRequestScope();
You can of course use some sort of factory pattern to get instance of DbContext instead of injecting it in constructors.
Let me know if you have any questions.
I've found a workaround that's not completely hacky.
Basically, I tell AutoMapper to ignore the tricky field and I update it myself.
The updated controller looks like this:
public class OperationsController : Controller {
private SomeContext context = new SomeContext ();
public ViewResult Index()
{
var ops = context.Operations.Include("blah...").ToList();
Mapper.CreateMap<Operation, OperationVM>()
.ForMember(dest => dest.CategoryVM, opt => opt.Ignore());
var opVMs = ops.Select(
op => {
var opVM = Mapper.Map<Operation, OperationVM>(op);
opVM.CategoryVM = CreateCatVM(op.Category, context.Categories);
return opVM;
})
.ToList();
return View(opVMs);
}
}
Still curious how this could be done from within AutoMapper...
The answer from #LeffeBrune is perfect. However, I want to have the same behavior, but I don't want to map every property myself. Basically I just wanted to override the "ConstructUsing".
Here is what I came up with.
public static class AutoMapperExtension
{
public static void ConstructUsingService<TSource, TDestination>(this IMappingExpression<TSource, TDestination> mappingExression, Type typeConverterType)
{
mappingExression.ConstructUsing((ResolutionContext ctx) =>
{
var constructor = (IConstructorWithService<TSource, TDestination>)ctx.Options.ServiceCtor.Invoke(typeConverterType);
return constructor.Construct((TSource)ctx.SourceValue);
});
}
}
public class CategoryToCategoryVMConstructor : IConstructorWithService<Category, CategoryVM>
{
private DbContext dbContext;
public DTOSiteToHBTISiteConverter(DbContext dbContext)
{
this.dbContext = dbContext;
}
public CategoryVM Construct(Category category)
{
// Some commands here
if (category.Id > 0)
{
var vmCategory = dbContext.Categories.FirstOrDefault(m => m.Id == category.Id);
if (vmCategory == null)
{
throw new NotAllowedException();
}
return vmCategory;
}
return new CategoryVM();
}
}
// Initialization
Mapper.Initialize(cfg =>
{
cfg.ConstructServicesUsing(type => nInjectKernelForInstance.Get(type));
cfg.CreateMap<Category, CategoryVM>().ConstructUsingService(typeof(CategoryToCategoryVMConstructor));
};

Adding DataAnnotation to class when using FluentValidation

I use the FluentValidation framework to add validation and annotations to my models in an MVC project.
I need to add data annotations to the class level of a model. Namely, the model needs to have the DisplayColumn attribute added. But, since I use FluentValidation (and have the application's ModelMetadataProvider set to use FluentValidation), even if I put the DisplayColumn attribute on the model class, it isn't used. However, I can't find a way to add that annotation by using FluentValidation.
Does anyone have any idea how I can get that to work?
Thanks
I wouldn't actually recommend using the FluentValidationModelMetadataProvider - this was only really ever an experimental addition (which very well may be removed from the next release), and it doesn't support any of the class-level DataAnnotations (such as DisplayColumn). I would suggest that you use FluentValidation only for validation, but stick with attributes for metadata.
That being said, if you really want to get this working then you could implement by using a custom no-op validator that's used only for metadata:
public static class MetadataExt {
public static IRuleBuilderOptions<T, TProperty> DisplayColumn<T, TProperty>(this IRuleBuilder<T, TProperty> rule) {
var ruleBuilder = (FluentValidation.Internal.RuleBuilder<T, TProperty>)rule;
ruleBuilder.Rule.AddValidator(new DisplayColumnWrapper(ruleBuilder.Rule.PropertyName));
return ruleBuilder;
}
public class DisplayColumnWrapper : NoopPropertyValidator, IAttributeMetadataValidator {
private string name;
public DisplayColumnWrapper(string name) {
this.name = name;
}
public override IEnumerable<ValidationFailure> Validate(PropertyValidatorContext context) {
return Enumerable.Empty<ValidationFailure>();
}
public Attribute ToAttribute() {
return new DisplayColumnAttribute(name);
}
}
}
... Which you could then use like this:
public class Validator : AbstractValidator<SomeModel> {
public Validator() {
RuleFor(x => x.DisplayColumnProperty)
.DisplayColumn();
}
}
You'd then need to create a custom ModelMetadataProvider that knows how to process this:
public class ExtendedFVModelMetadataProvider : FluentValidationModelMetadataProvider {
IValidatorFactory _validatorFactory;
public ExtendedFVModelMetadataProvider(IValidatorFactory validatorFactory)
: base(validatorFactory) {
this._validatorFactory = validatorFactory;
}
public override ModelMetadata GetMetadataForType(Func<object> modelAccessor, Type modelType) {
var validator = _validatorFactory.GetValidator(modelType);
if (validator == null) {
return base.GetMetadataForType(modelAccessor, modelType);
}
// Only look for the DisplayColumnWrapper
// There is a mismatch as MVC expects this to be defined at class-level, but FV defines everything at the property level.
var displayColumns = from memberWithValidator in validator.CreateDescriptor().GetMembersWithValidators()
from propertyValidator in memberWithValidator
let wrapper = propertyValidator as MetadataExt.DisplayColumnWrapper
where wrapper != null
select wrapper.ToAttribute();
var displayColumn = displayColumns.FirstOrDefault();
// we found a displaycolumn, so pass it over to MVC to build the metadata.
if (displayColumn != null) {
return CreateMetadata(new[] { displayColumn }, null /* containerType */, modelAccessor, modelType, null /* propertyName */);
}
return base.GetMetadataForType(modelAccessor, modelType);
}
}
The provider overrides the GetMetadataForModel method and looks for any properties that use DisplayColumn. This could probably be extended if you want to support any other custom metadata extensions too. You could then use this provider in place of the metadata provider that comes with FluentValidation.
However, I still wouldn't recommend this approach...the library is designed for performing validation, not for generating UI metadata.

Access the error message in ModelState error dictionary in ASP.net MVC unit test

I have added a key-value pair in the action result like this:
[HttpPost, Authorize]
public ActionResult ListFacilities(int countryid)
{
...
ModelState.AddModelError("Error","No facilities reported in this country!");
...
}
I have some cumbersome codes like these in a unit test to :
public void ShowFailforFacilities()
{
//bogus data
var facilities = controller.ListFacilities(1) as PartialViewResult;
Assert.AreSame("No facilities reported in this country!",
facilities.ViewData.ModelState["Error"].Errors.FirstOrDefault().ErrorMessage);
}
Of course, it works whenever I have only one error.
I don't like facilities.ViewData.ModelState["Error"].Errors.FirstOrDefault().ErrorMessage.
Is there an easier way for me to fetch the value from that dictionary?
Your FirstOrDefault isn't needed, because you'll get a NullReferenceException when accessing ErrorMessage. You can just use First().
Either way, I couldn't find any built-in solution. What I've done instead is create an extension method:
public static class ExtMethod
{
public static string GetErrorMessageForKey(this ModelStateDictionary dictionary, string key)
{
return dictionary[key].Errors.First().ErrorMessage;
}
}
Which works like this:
ModelState.GetErrorMessageForKey("error");
If you need better exception handling, or support for multiple errors, its easy to extend...
If you want this to be shorter you can create an extension method for the ViewData...
public static class ExtMethod
{
public static string GetModelStateError(this ViewDataDictionary viewData, string key)
{
return viewData.ModelState[key].Errors.First().ErrorMessage;
}
}
and usage:
ViewData.GetModelStateError("error");
Have you tried this?
// Note: In this example, "Error" is the name of your model property.
facilities.ViewData.ModelState["Error"].Value
facilities.ViewData.ModelState["Error"].Error

Validate object based on external factors (ie. data store uniqueness)

Description
My solution has these projects:
DAL = Modified Entity Framework
DTO = Data Transfer objects that are able to validate themselves
BL = Business Layer Services
WEB = presentation Asp.net MVC application
DAL, BL and WEB all reference DTO which is great.
The process usually executes this way:
A web request is made to the WEB
WEB gets DTOs posted
DTOs get automagically validated via custom ActionFilter
validation errors are auto-collected
(Validation is OK) WEB calls into BL providing DTOs
BL calls into DAL by using DTOs (can either pass them through or just use them)
DTO Validation problem then...
My DTOs are able to validate themselves based on their own state (properties' values). But right now I'm presented with a problem when this is not the case. I need them to validate using BL (and consequently DAL).
My real-life example: User registers and WEB gets a User DTO that gets validated. The problematic part is username validation. Its uniqueness should be checked against data store.
How am I supposed to do this?
There's additional info that all DTOs implement an interface (ie. User DTO implements IUser) for IoC purposes and TDD. Both are part of the DTO project.
Impossible tries
I can't reference BL in DTO because I'll get circular reference.
Compilation error
I can't create an additional DTO.Val project that would reference partial DTO classes and implement their validation there (they'd reference BL + DTO).
Partial classes can't span assemblies.
Possible tries
Create a special ActionFilter that would validate object against external conditions. This one would be created within WEB project thus seeing DTO and BL that would be used here.
Put DTOs in BL and keep DTO interfaces as actual DTOs referenced by other projects and refactor all code to use interfaces instead of concrete classes.
Don't handle external dependant validation and let external dependencies throw an exception - probably the worst solution to this issue
What would you suggest?
I would suggest an experiment that i have only been trialling for the last week or so.
Based on this inspiration i am creating DTOs that validate a little differently to that of the DataAnnotations approach. Sample DTO:
public class Contact : DomainBase, IModelObject
{
public int ID { get; set; }
public string Name { get; set; }
public LazyList<ContactDetail> Details { get; set; }
public DateTime Updated { get; set; }
protected override void ConfigureRules()
{
base.AddRule(new ValidationRule()
{
Properties = new string[] { "name" },
Description = "A Name is required but must not exceed 300 characters in length and some special characters are not allowed",
validator = () => this.Name.IsRequired300LenNoSpecial()
});
base.AddRule(new ValidationRule()
{
Properties = new string[] { "updated" },
Description = "required",
validator = () => this.Updated.IsRequired()
});
}
}
This might look more work than DataAnnotations and well, that's coz it is, but it's not huge. I think it's more presentable in the class (i have some really ugly DTO classes now with DataAnnotations attributes - you can't even see the properties any more). And the power of anonymous delegates in this application is almost book-worthy (so i'm discovering).
Base class:
public partial class DomainBase : IDataErrorInfo
{
private IList<ValidationRule> _rules = new List<ValidationRule>();
public DomainBase()
{
// populate the _rules collection
this.ConfigureRules();
}
protected virtual void ConfigureRules()
{
// no rules if not overridden
}
protected void AddRule(ValidationRule rule)
{
this._rules.Add(rule);
}
#region IDataErrorInfo Members
public string Error
{
get { return String.Empty; } // Validation should call the indexer so return "" here
} // ..we dont need to support this property.
public string this[string columnName]
{
get
{
// get all the rules that apply to the property being validated
var rulesThatApply = this._rules
.Where(r => r.Properties.Contains(columnName));
// get a list of error messages from the rules
StringBuilder errorMessages = new StringBuilder();
foreach (ValidationRule rule in rulesThatApply)
if (!rule.validator.Invoke()) // if validator returns false then the rule is broken
if (errorMessages.ToString() == String.Empty)
errorMessages.Append(rule.Description);
else
errorMessages.AppendFormat("\r\n{0}", rule.Description);
return errorMessages.ToString();
}
}
#endregion
}
ValidationRule and my validation functions:
public class ValidationRule
{
public string[] Properties { get; set; }
public string Description { get; set; }
public Func<bool> validator { get; set; }
}
/// <summary>
/// These extention methods return true if the validation condition is met.
/// </summary>
public static class ValidationFunctions
{
#region IsRequired
public static bool IsRequired(this String str)
{
return !str.IsNullOrTrimEmpty();
}
public static bool IsRequired(this int num)
{
return num != 0;
}
public static bool IsRequired(this long num)
{
return num != 0;
}
public static bool IsRequired(this double num)
{
return num != 0;
}
public static bool IsRequired(this Decimal num)
{
return num != 0;
}
public static bool IsRequired(this DateTime date)
{
return date != DateTime.MinValue;
}
#endregion
#region String Lengths
public static bool IsLengthLessThanOrEqual(this String str, int length)
{
return str.Length <= length;
}
public static bool IsRequiredWithLengthLessThanOrEqual(this String str, int length)
{
return !str.IsNullOrTrimEmpty() && (str.Length <= length);
}
public static bool IsRequired300LenNoSpecial(this String str)
{
return !str.IsNullOrTrimEmpty() &&
str.RegexMatch(#"^[- \r\n\\\.!:*,#$%&""?\(\)\w']{1,300}$",
RegexOptions.Multiline) == str;
}
#endregion
}
If my code looks messy well that's because i've only been working on this validation approach for the last few days. I need this idea to meet a few requirements:
I need to support the IDataErrorInfo interface so my MVC layer validates automatically
I need to be able to support complex validation scenarios (the whole point of your question i guess): I want to be able to validate against multiple properties on the same object (ie. StartDate and FinishDate); properties from different/multiple/associated objects like i would have in an object graph; and even other things i haven't thought of yet.
I need to support the idea of an error applying to more than one property
As part of my TDD and DDD journey i want my Domain Objects to describe more my 'domain' than my Service layer methods, so putting these complex conditions in the model objects (not DTOs) seems to achieve this
This approach i think will get me what i want, and maybe you as well.
I'd imagine if you jump on board with me on this that we'd be pretty 'by ourselves' but it might be worth it. I was reading about the new validation capabilities in MVC 2 but it still doesn't meet the above wish list without custom modification.
Hope this helps.
The S#arp Architecture has an [DomainSignature] method identifier that used with the class level validator [HasUniqueDomainSignature] will do the work. See the sample code below:
[HasUniqueDomainSignature]
public class User : Entity
{
public User()
{
}
public User(string login, string email) : this()
{
Login = login;
Email = email;
}
[DomainSignature]
[NotNullNotEmpty]
public virtual string Login { get; set; }
[DomainSignature]
public virtual string Email { get; set; }
}
Take a closer look at http://www.sharparchitecture.net/
I had this exact same problem and after trying to find a work around for days and days and days, I ended up merging my DTO, DAL, and BL into one library. I kept my presentation layer separate.
Not sure if that is an option for you or not. For me, I figured that my chances of ever changing the data store were very slight, and so the separate tier wasn't really needed.
I also have implemented the Microsoft Validation Application Block for all my DTO validations. They have a "Self Validation" method that lets you perform complex validations.
Resulting solution
I ended up using controller action filter that was able to validate object against external factors that can't be obtained from the object itself.
I created the filter that takes the name of the action parameter to check and validator type that will validate that particular parameter. Of course this validator has to implement certain interface to make it all reusable.
[ValidateExternalFactors("user", typeof(UserExternalValidator))]
public ActionResult Create(User user)
validator needs to implement this simple interface
public interface IExternalValidator<T>
{
bool IsValid(T instance);
}
It's a simple and effective solution to a seemingly complex problem.

Resources