0

In summary, I'm trying to create instance-specific data-annotation attributes at runtime, based on database fields. What I have now works fine for creating the initial model, but falls over when the model is posted-back and the server-validation happens.

(I have the same input model being used in a collection within a viewmodel, but different validation must be applied to each instance in the collection....for example the first occurrence of the input may be restricted to a range of 1-100 but the next occurrence of the same model, prompted for on the same input page, would be a range of 1000-2000. Another may be a date, or a string that has to be 6 characters long.......)

I'll explain what I've done and where my issues are:

I've inherited DataAnnotationsModelMetadataProvider and provided my own implementation of GetMetadataForProperty (This doesn't have any bearing on the validation problem....yet)

I've inherited DataAnnotationsModelValidatorProvider and provided a facade implementation of GetValidators. What I want to do here is create new attributes based on my database-records and then pass those attributes through to the base implementation so the Validators are created accordingly.

However...... GetValidators is called at a PROPERTY level....When it is called with a propertyname that I want to apply validators to, I need to find the applicable DB record for this propertyname so I can find out what attributes I need to create....BUT...I can't get the DB record's key from just a propertyname of the value field.....In fact, the DB key is in the parent model.....So how do I get hold of it?!

I've tried using a static variable (YUK) and storing the key during a call for one property, and retrieving it during another call for my value field property....But because the model is serialised one-way and deserialised the opposite way I end up with my key being out-of-sync with my required attributes.

To add a slight complication I'm also using a custom model binder. I've overridden CreateModel as advised elsewhere on here, but I can't find a way of attaching metadata or additionalvalues to a PROPERTY of my output model....Only to the model itself....but how do I get at MODEL metadata/additionalvalues inside the GetValidators call for a PROPERTY ?

So....My question is twofold.....

1) Can anyone help me get my database-key from my custom-Model-binder to my GetValidators method on my ValidationProvider? Or maybe using my custom Metadata provider?

2) Is there a different, simpler, way of creating validators at runtime based on database records?

Dave R
  • 1,626
  • 19
  • 28
  • Quick google search; will this do what you want? http://fluentvalidation.codeplex.com/wikipage?title=mvc – user1477388 Apr 17 '14 at 16:34
  • I have had a look at FluentValidation, but same problem....How do I build the "RuleFor"'s with dynamic values sourced from database fields? I couldn't see a way of doing it on a per-instance basis. Also looked at Enterprise Validation Block, but again....I need INSTANCE SPECIFIC rules, not model-specific...... – Dave R Apr 17 '14 at 16:37
  • 1
    @DaveR - I think you're confused. Attributes apply to types, not instances. They are compiled into the code, thus they are defined at compile time, not run time. – Erik Funkenbusch Apr 17 '14 at 16:51
  • Nope, not confused. Yes I know you would normally create attributes at compile time but my validation criteria isn't type based, it's instance based....so....I'm adding them at runtime using my own version of the DataAnnotationsModelValidatorProvider, as I said in the question. – Dave R Apr 18 '14 at 09:25
  • @DaveR - There is no "normally". Attributes *ARE ALWAYS* added at compile time and cannot be added at run time to existing classes. You can build attributes at runtime using emit, and add them to classes you build using emit, but then you are doing everything yourself, and this isn't what you want. – Erik Funkenbusch Apr 18 '14 at 13:46
  • Of course you can create them at runtime. See here: http://stackoverflow.com/questions/3607247/dataannotations-dynamically-attaching-attributes It works just fine. – Dave R Apr 27 '14 at 09:09

3 Answers3

1

I think you are making this far more complicated than it needs to be. You just need to make whatever your validation criteria selectors are part of your view model. They don't necessarily have to be displayed (they can be stored in hiddens if they need to be kept for postback purposes).

Then you can use something like FluentValidation to create rules that say

RuleFor(model => model.myprop)
   .When(model => model.criteria == whatever)
   .GreaterThan(100)
   .LessThan(1000);

Where criteria is whatever value you use to select when your property has to be in a certain range.

So that would mean you build your view model to include the criteria that is used for validation rule selection.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
0

I'd asked this on the FluentValidation forums also and the lack of answers here as well as the advice against using Fluent from there led me to find my own solution (I understand this almost certainly means I'm doing something really bad / unusual / unnecessary!)

What I've ended up doing is assigning my controller static variable in my Custom Model Binder's CreateModel method, where I have access to the entire client model, rather than trying to do it through a custom MetaDataProvider. This seems to work just fine and gets me towards v1 of my app.

I'm not really happy with this solution though so will look to refactor this whole area in the coming months so would still appreciate any other comments / ideas people have about how to implement dynamic validation in a generic way.

Dave R
  • 1,626
  • 19
  • 28
0

I know this is an old question, but I am answering this so that many others can be benefited from this.

Please see the below article where they are loading the attributes from an xml

Loading C# MVC .NET Data Annotation Attributes From XML, Form Validation

I think you can follow the same approach and instead of reading from xml you can read from database and add these rules dynamically based on the model data type

You can refer the below approach also

DataAnnotations dynamically attaching attributes

Kiran B
  • 683
  • 10
  • 21