I've developed on a small library for for ASP.NET MVC 3 that offers much better reusability of model metadata and easy mapping from data entities from / to custom viewmodels. For this I needed to provide my own implementation of ICustomTypeDescriptor for three
different areas of interest in ASP.NET MVC:
Scaffolding
Validation
Modelbinding
I'd like to distribute the library via Nuget, but the problem is that I overriding ICustomTypeDescriptor is not possible in a simple and consistent manner in ASP.NET MVC 3. So I had to pull a lot of rather ugly hacks to make this work in MVC 3 and I'm scared
of what I might have to do to support this library in MVC 4.
The problem is that setting a custom ICustomTypeDescriptor provider implementation is not possible via System.Web.Mvc.ModelMetadataProviders does not work properly . Several classes in System.Web.Mvc which call directly into this System.Web.TypeDescriptorHelper
which is not extensible because it looks like this:
internal static class TypeDescriptorHelper {
public static ICustomTypeDescriptor Get(Type type) {
return new AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type);
}
}
This makes it very difficult to implement to my own custom ICustomTypeDescriptor provider
pls explai why you need to furnish a custom implementation of your ICustomTypeProvider...you can attach a new metedataprovider by simply adding in the global asax a simple instruction like this:
ModelMetadataProviders.Current = new MyMetaDataProvider();
You cand do similar things for the other providers...
For Example, suppose I have the following EF Entity:
public class User {
[Required]
[Display(Name="First Name")]
public string FirstName { get; set; }
[DisplayName("Last Name")]
[Required]
public string LastName { get; set; }
[DisplayName("Password")]
[Required]
public string Password{ get; set; }
[DisplayName("Email")]
[Required]
public string Email{ get; set; }
}
And the following view model which is used for a login screen:
[MetadataType(typeof (User))]
public class LoginViewModel {
public string Email {get;set;}
public string Password{get;set;}
}
This would not work without my my own ICustomTypeDescriptor because AssociatedMetadataTypeTypeDescriptionProvider raises an exception if the viewmodel does not have all properties listed in the model.
The only way to work around this problem is to put metadata on the viewmodel (not very DRY as I want to use the same metadata on several viewmodels, and my actual metadata is much more complex than this) or implementing my own ICustomTypeDescriptor (hence my
request to fix this).
1) This is a problem of just metadataprovider, in you case the problem is with the Display attribute
2) Validation attributes are handled by the Validation provider, that has no problem if some property is missing.
3) The [required] attribute is checked by the model binder , and if the ViewModel has no matching property a validation error is raised(no exception...it behave just if the user misses the filed)
4) All validation attributes but the required attribute works fine also if the ViewModel has missing properties
5) Removing this behaviour from the required attribute create a security problem (that is why this behaviour was inserted). Moreover, the fact that an attribute is required in the model doesn't imply it is required in the ViewModel since the value of the
required fileld might come from a source different from the user, for instance another db table...or by looking at the logged user. I suggest to use a third Metadata class to be used as metaclass for both the model and the ViewModel, and to avoid putting
the required attribute in this metacdata class(just put them directly in the model or in the viewmodel as needed). For more infos, pls read my blog post: http://www.dotnet-programming.com/post/2011/04/13/Data-Dictionary-And-Data-Annotations.aspx
Summing up the problem is just with the data provided by the MetaData provider, not with the validation attributes.
Now why don't you simply write your Metadataprovider, reading yourself the attributes with the help of reflection, and then set your provider as the default one? You are not forced to use the function inherited by the base class to retrieve the metadata...you
can do it with your functions. You need just to use a little reflection.
Regarding 1 and 2: This is not a problem with the display attribute. The example I gave you is overly simplified, my library can actually do a lot more than this.
For example imagine a form where the user can add his clients. He can do this with or without supply a full address for each client. Usually you would need two different view models for this because the fields for the contact info are either required or not.
But with my library you can do this instead:
[DynamicBindExclude("WithContactInfo", "false", "ContactInfo", "")]
[MetadataType(typeof (Client))]
public class ClientViewModel{
[MetadataType(typeof (ContactInfo))]
public bool ContactViewModel{
public string Address {get;set;}
public string ZIP {get;set;}
...
}
public string ClientName {get;set;}
public bool WithContactInfo {get;set;}
}
Are you sure this can also be done just by deriving from ModelMetaDataProvider?
Regarding 3: That's why I have also overriden a part of the model validation code so that no error is raised in the example above. This wasn't too complicated.
Regarding 4: I am not sure what you mean by this. As I said, an exception is thrown if the viewmodel is missing properties listed in the data entity.
Regarding 5: It seems that there is a misunderstanding on why I wrote this library. I did it because I have several view models displaying parts of data entities in various kinds and there is a lot of model metadata (for validation AND scaffolding) which I
want to reuse. I asked about this in this forum before and the approach I took was recommended by another MVC. user. See
this thread for more info. If you have any better solution, I'd be happy to have a look, but so far the library I have written is the best approach I have seen to tackle this problem.
Hi I have implemented dynamic Validation Attributes where the validation depends on the value of other fields. The value of the other fields mybe either fixed on the sever, or may vary also on the client. In order to make this I just modified the metadataprovider
to make it "pacck" some property more see the Dynamic Range and DateRange attributes here: http://mvccontrolstoolkit.codeplex.com/wikipage?title=Data%20Annotations . The
attributes works with: server validation and Mvc2 Mvc3 client side validation
You can see the sources for understanding my implementation.
Is this what do you need?
If not plese state clearly what do you need, and let me understand if you extension is mainly concerned with Validation attributes or wwith attributes that control how a filed is displayed such as display, UIHint, DataType and son. This is important because
the two kind of attributes are processed in a completely differnt way.
The point is that some interfaces are intended for interna use, and one should use the "officially provided" extension points, otherwise yiour library might not work on further Mvc version, The official extension points in this case are: ModelBinder, medatata
and validation provider. Use of "internal pieces" of the code give no garantee....of working reliably.
The link you put in your pos ...doesn't work, so please correct it.
It's been a couple of months since I implemented this library, so I am not entirely sure about the details anymore. All I can say is that I did have a good look at the ASP.NET MVC source code when I wrote this and it did not seem to me as if this could be
covered by overriding the MetaDataProvider alone,b which is why I tackled the problem by providing my own ICustomTypeDescriptor. Also, I explained my problem to Brad Wilson at that time and he agreed and told me that he had put this on the list of bugs to
fix for ASP.NET MVC 4.
Right now I am lacking the time to have another look at this. When I do have more time I'll have another look and update this thread. Perhaps you could also have a look at my library when I release it in my blog. So far I have not done this yet because I
wanted to iron out the worst bugs before going public.
P.S.: I fixed the link above above. Not sure if it helps, though.
About a general solution for the exception it should be possible solving by rewriting a metadataprovider. However, the point is that if one doesn't use the pre-existing logics for retrieving attributes information one need to re-implent also the associated
caching mechanism...without caching the performance should be unacceptable.
However as I said the problem is just with the required attribute if you organize adequately your classes.
The best approach that solve the problem is:
1) define a metadata class whose properties have the right data type, and are defined as virtual but are "fake", that is get always return null(or default(T) for value types) and the set do nothing;
2) apply the metadata class to the model
3) all Viewmodel inherits from the MetaDataclass and override with "actual" properties just the properties they need. No memory is wasted because the "fake" properties doesn't waste memory in the inheriting classes since they have no storage associted with
them.
The above works fine if one avoid using required attributes in the MetaData class and apply the directly to the classes that need them.
This is reasonable because while all other validation attributes normally apply to all viewmodels an to the ViewModel, the Required attribute depends on the usage. In particular a property may be required in the model without being required in some ViewModels,
since the value of that property might be COMPUTED by the system instead of being provided by the user. The vice versa is also possible.
Sorry, but I disagree. You are missing several vital points:
1. Inheritance is not a good solution for reusing metadata. If it were the perfect solution , then there would be no MetaDataAttribute. Since there is no multiple inheritance in C#, so if you are forced to derive from an object to reuse metadata then you
can't build object hierarchies of view models anymore (which I actually had to do in for one very complex form which is the heart of my current MVC project).
2. The DyamicBindExclude attribute I showed in my example does just what the name implies: It restricts binding (and therefore with my library also Validation) of child classes properties of the view model. Again, this is for complex forms where you always
have to fill out a certain section A plus either Section B or Section C of the form. You can't do that with the regular Required attribute unless you build several POST action methods accepting different view models and change the form target dynamicall to
account for whatever optional section the user opted to fill out.
3. I like my metadata to be provided a consistent manner. Required attributes are part of that metadata. Why should I have to repeat them n times (and then have to test them n times) if my view models are representing data from one single data entity and
I can add them right there? IMO a cleaner, safer more DRY way is to do this is shown in my example: add the required attribute to the data entity, reference that data entity with a metadata attribute and add a property with the same name to the view model.
4. If combine take point 1 and 2 you also get another problem: Imagine a form where the user has to fill out a section A, plus a section B and / or section C. Then all of a sudden you have to build 4 different view models and 4 action methods to account
for all possibilities and you also have to switch the form target at runtime. But with my library you just define your view model and action method once, reference your meta data classes and you are done.
I agree that inheritance is not a good solution. However, applying it just to the View Model that has no business logics in it might be acceptable. It is used just to display data.
The main problem of the solution I scetched...that according to the way Mvc works NOW is the only way to reuse metadata is that "fake" properties cause some waste of computation resources in the modelbincer that try to bind them(it fails immediately...but
waste some resources). It causes also waste of time in the metadataprovider that wasye time retrieving metadataattributes that will not be used. The wate of time in the ModelBinder can be avoided by using the BindAttribute and listing there just the properties
to bind. However the waste in the metadataprovider...no.
Obviously if your library is able to overcome the problem...is better :)
About the dynamic bind it appears interesting but I don't understand how it is dymamic...that is where it takes the data to decide if binding or not a property. Does it block metadataprovider from retrieving attributes of peoperties that are excluded?
pluggy13
Member
48 Points
56 Posts
ASP.NET MVC 4 - Will it be easier to provide a custom ICustomTypeDescriptor Provider?
Jul 14, 2011 06:36 AM|LINK
Hi,
I've developed on a small library for for ASP.NET MVC 3 that offers much better reusability of model metadata and easy mapping from data entities from / to custom viewmodels. For this I needed to provide my own implementation of ICustomTypeDescriptor for three different areas of interest in ASP.NET MVC:
Scaffolding
Validation
Modelbinding
I'd like to distribute the library via Nuget, but the problem is that I overriding ICustomTypeDescriptor is not possible in a simple and consistent manner in ASP.NET MVC 3. So I had to pull a lot of rather ugly hacks to make this work in MVC 3 and I'm scared of what I might have to do to support this library in MVC 4.
The problem is that setting a custom ICustomTypeDescriptor provider implementation is not possible via System.Web.Mvc.ModelMetadataProviders does not work properly . Several classes in System.Web.Mvc which call directly into this System.Web.TypeDescriptorHelper which is not extensible because it looks like this:
internal static class TypeDescriptorHelper { public static ICustomTypeDescriptor Get(Type type) { return new AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type); } }This makes it very difficult to implement to my own custom ICustomTypeDescriptor provider
Is this going to be fixed in ASP.NET MVC 4?
Thanks,
Adrian
http://www.logmytime.de
francesco ab...
All-Star
20954 Points
3286 Posts
Re: ASP.NET MVC 4 - Will it be easier to provide a custom ICustomTypeDescriptor Provider?
Jul 14, 2011 07:36 AM|LINK
pls explai why you need to furnish a custom implementation of your ICustomTypeProvider...you can attach a new metedataprovider by simply adding in the global asax a simple instruction like this:
ModelMetadataProviders.Current = new MyMetaDataProvider();
You cand do similar things for the other providers...
Mvc Controls Toolkit | Data Moving Plug-in Videos
pluggy13
Member
48 Points
56 Posts
Re: ASP.NET MVC 4 - Will it be easier to provide a custom ICustomTypeDescriptor Provider?
Jul 14, 2011 08:47 AM|LINK
For Example, suppose I have the following EF Entity:
public class User { [Required] [Display(Name="First Name")] public string FirstName { get; set; } [DisplayName("Last Name")] [Required] public string LastName { get; set; } [DisplayName("Password")] [Required] public string Password{ get; set; } [DisplayName("Email")] [Required] public string Email{ get; set; } }And the following view model which is used for a login screen:
[MetadataType(typeof (User))] public class LoginViewModel { public string Email {get;set;} public string Password{get;set;} }This would not work without my my own ICustomTypeDescriptor because AssociatedMetadataTypeTypeDescriptionProvider raises an exception if the viewmodel does not have all properties listed in the model.
The only way to work around this problem is to put metadata on the viewmodel (not very DRY as I want to use the same metadata on several viewmodels, and my actual metadata is much more complex than this) or implementing my own ICustomTypeDescriptor (hence my request to fix this).
http://www.logmytime.de
francesco ab...
All-Star
20954 Points
3286 Posts
Re: ASP.NET MVC 4 - Will it be easier to provide a custom ICustomTypeDescriptor Provider?
Jul 14, 2011 10:14 AM|LINK
pls Note the following:
1) This is a problem of just metadataprovider, in you case the problem is with the Display attribute
2) Validation attributes are handled by the Validation provider, that has no problem if some property is missing.
3) The [required] attribute is checked by the model binder , and if the ViewModel has no matching property a validation error is raised(no exception...it behave just if the user misses the filed)
4) All validation attributes but the required attribute works fine also if the ViewModel has missing properties
5) Removing this behaviour from the required attribute create a security problem (that is why this behaviour was inserted). Moreover, the fact that an attribute is required in the model doesn't imply it is required in the ViewModel since the value of the required fileld might come from a source different from the user, for instance another db table...or by looking at the logged user. I suggest to use a third Metadata class to be used as metaclass for both the model and the ViewModel, and to avoid putting the required attribute in this metacdata class(just put them directly in the model or in the viewmodel as needed). For more infos, pls read my blog post: http://www.dotnet-programming.com/post/2011/04/13/Data-Dictionary-And-Data-Annotations.aspx
Summing up the problem is just with the data provided by the MetaData provider, not with the validation attributes.
Now why don't you simply write your Metadataprovider, reading yourself the attributes with the help of reflection, and then set your provider as the default one? You are not forced to use the function inherited by the base class to retrieve the metadata...you can do it with your functions. You need just to use a little reflection.
Just download the sources and mofify them.
Mvc Controls Toolkit | Data Moving Plug-in Videos
pluggy13
Member
48 Points
56 Posts
Re: ASP.NET MVC 4 - Will it be easier to provide a custom ICustomTypeDescriptor Provider?
Jul 14, 2011 12:39 PM|LINK
Francesco, Thanks for your quick reply:
Regarding 1 and 2: This is not a problem with the display attribute. The example I gave you is overly simplified, my library can actually do a lot more than this.
For example imagine a form where the user can add his clients. He can do this with or without supply a full address for each client. Usually you would need two different view models for this because the fields for the contact info are either required or not. But with my library you can do this instead:
[DynamicBindExclude("WithContactInfo", "false", "ContactInfo", "")] [MetadataType(typeof (Client))] public class ClientViewModel{ [MetadataType(typeof (ContactInfo))] public bool ContactViewModel{ public string Address {get;set;} public string ZIP {get;set;} ... } public string ClientName {get;set;} public bool WithContactInfo {get;set;} }Are you sure this can also be done just by deriving from ModelMetaDataProvider?
Regarding 3: That's why I have also overriden a part of the model validation code so that no error is raised in the example above. This wasn't too complicated.
Regarding 4: I am not sure what you mean by this. As I said, an exception is thrown if the viewmodel is missing properties listed in the data entity.
Regarding 5: It seems that there is a misunderstanding on why I wrote this library. I did it because I have several view models displaying parts of data entities in various kinds and there is a lot of model metadata (for validation AND scaffolding) which I want to reuse. I asked about this in this forum before and the approach I took was recommended by another MVC. user. See this thread for more info. If you have any better solution, I'd be happy to have a look, but so far the library I have written is the best approach I have seen to tackle this problem.
http://www.logmytime.de
francesco ab...
All-Star
20954 Points
3286 Posts
Re: ASP.NET MVC 4 - Will it be easier to provide a custom ICustomTypeDescriptor Provider?
Jul 14, 2011 04:06 PM|LINK
Hi I have implemented dynamic Validation Attributes where the validation depends on the value of other fields. The value of the other fields mybe either fixed on the sever, or may vary also on the client. In order to make this I just modified the metadataprovider to make it "pacck" some property more see the Dynamic Range and DateRange attributes here: http://mvccontrolstoolkit.codeplex.com/wikipage?title=Data%20Annotations . The attributes works with: server validation and Mvc2 Mvc3 client side validation
You can see the sources for understanding my implementation.
Is this what do you need?
If not plese state clearly what do you need, and let me understand if you extension is mainly concerned with Validation attributes or wwith attributes that control how a filed is displayed such as display, UIHint, DataType and son. This is important because the two kind of attributes are processed in a completely differnt way.
The point is that some interfaces are intended for interna use, and one should use the "officially provided" extension points, otherwise yiour library might not work on further Mvc version, The official extension points in this case are: ModelBinder, medatata and validation provider. Use of "internal pieces" of the code give no garantee....of working reliably.
The link you put in your pos ...doesn't work, so please correct it.
Mvc Controls Toolkit | Data Moving Plug-in Videos
pluggy13
Member
48 Points
56 Posts
Re: ASP.NET MVC 4 - Will it be easier to provide a custom ICustomTypeDescriptor Provider?
Jul 14, 2011 08:12 PM|LINK
It's been a couple of months since I implemented this library, so I am not entirely sure about the details anymore. All I can say is that I did have a good look at the ASP.NET MVC source code when I wrote this and it did not seem to me as if this could be covered by overriding the MetaDataProvider alone,b which is why I tackled the problem by providing my own ICustomTypeDescriptor. Also, I explained my problem to Brad Wilson at that time and he agreed and told me that he had put this on the list of bugs to fix for ASP.NET MVC 4.
Right now I am lacking the time to have another look at this. When I do have more time I'll have another look and update this thread. Perhaps you could also have a look at my library when I release it in my blog. So far I have not done this yet because I wanted to iron out the worst bugs before going public.
P.S.: I fixed the link above above. Not sure if it helps, though.
http://www.logmytime.de
francesco ab...
All-Star
20954 Points
3286 Posts
Re: ASP.NET MVC 4 - Will it be easier to provide a custom ICustomTypeDescriptor Provider?
Jul 14, 2011 11:43 PM|LINK
About a general solution for the exception it should be possible solving by rewriting a metadataprovider. However, the point is that if one doesn't use the pre-existing logics for retrieving attributes information one need to re-implent also the associated caching mechanism...without caching the performance should be unacceptable.
However as I said the problem is just with the required attribute if you organize adequately your classes.
The best approach that solve the problem is:
1) define a metadata class whose properties have the right data type, and are defined as virtual but are "fake", that is get always return null(or default(T) for value types) and the set do nothing;
2) apply the metadata class to the model
3) all Viewmodel inherits from the MetaDataclass and override with "actual" properties just the properties they need. No memory is wasted because the "fake" properties doesn't waste memory in the inheriting classes since they have no storage associted with them.
The above works fine if one avoid using required attributes in the MetaData class and apply the directly to the classes that need them.
This is reasonable because while all other validation attributes normally apply to all viewmodels an to the ViewModel, the Required attribute depends on the usage. In particular a property may be required in the model without being required in some ViewModels, since the value of that property might be COMPUTED by the system instead of being provided by the user. The vice versa is also possible.
Mvc Controls Toolkit | Data Moving Plug-in Videos
pluggy13
Member
48 Points
56 Posts
Re: ASP.NET MVC 4 - Will it be easier to provide a custom ICustomTypeDescriptor Provider?
Jul 15, 2011 07:09 AM|LINK
Sorry, but I disagree. You are missing several vital points:
1. Inheritance is not a good solution for reusing metadata. If it were the perfect solution , then there would be no MetaDataAttribute. Since there is no multiple inheritance in C#, so if you are forced to derive from an object to reuse metadata then you can't build object hierarchies of view models anymore (which I actually had to do in for one very complex form which is the heart of my current MVC project).
2. The DyamicBindExclude attribute I showed in my example does just what the name implies: It restricts binding (and therefore with my library also Validation) of child classes properties of the view model. Again, this is for complex forms where you always have to fill out a certain section A plus either Section B or Section C of the form. You can't do that with the regular Required attribute unless you build several POST action methods accepting different view models and change the form target dynamicall to account for whatever optional section the user opted to fill out.
3. I like my metadata to be provided a consistent manner. Required attributes are part of that metadata. Why should I have to repeat them n times (and then have to test them n times) if my view models are representing data from one single data entity and I can add them right there? IMO a cleaner, safer more DRY way is to do this is shown in my example: add the required attribute to the data entity, reference that data entity with a metadata attribute and add a property with the same name to the view model.
4. If combine take point 1 and 2 you also get another problem: Imagine a form where the user has to fill out a section A, plus a section B and / or section C. Then all of a sudden you have to build 4 different view models and 4 action methods to account for all possibilities and you also have to switch the form target at runtime. But with my library you just define your view model and action method once, reference your meta data classes and you are done.
http://www.logmytime.de
francesco ab...
All-Star
20954 Points
3286 Posts
Re: ASP.NET MVC 4 - Will it be easier to provide a custom ICustomTypeDescriptor Provider?
Jul 15, 2011 08:22 AM|LINK
I agree that inheritance is not a good solution. However, applying it just to the View Model that has no business logics in it might be acceptable. It is used just to display data.
The main problem of the solution I scetched...that according to the way Mvc works NOW is the only way to reuse metadata is that "fake" properties cause some waste of computation resources in the modelbincer that try to bind them(it fails immediately...but waste some resources). It causes also waste of time in the metadataprovider that wasye time retrieving metadataattributes that will not be used. The wate of time in the ModelBinder can be avoided by using the BindAttribute and listing there just the properties to bind. However the waste in the metadataprovider...no.
Obviously if your library is able to overcome the problem...is better :)
About the dynamic bind it appears interesting but I don't understand how it is dymamic...that is where it takes the data to decide if binding or not a property. Does it block metadataprovider from retrieving attributes of peoperties that are excluded?
Mvc Controls Toolkit | Data Moving Plug-in Videos