Never mind why I want to do this, the above code is just to show the bug. The AdditionalValues.Count is different inside the Objects.asxc template than in the specific template even though the metadata subscribe the exact same property. This is big problem
with you implement a custom ModelMetadataProvider and want to do advanced templated forms for example. It is even a problem with the built in meta data like IsRequired, this property is set to true in prop.IsRequired in the foreach loop in the objects.ascx
template but false in the specific template, again these Metadata object point to the same property and should be identical!
It looks like there might be a bug in whatever metadata provider you're using.
Calling Html.Editor() will cause the system to get a new copy of the model metadata, and it will happen via a different path than the original. The original one came from a call to Properties() on the metadata of the container object, whereas the new one
will come a call to the metadata provider's GetMetadataForProperty.
Marked as answer by ricka6 on Mar 09, 2010 09:22 PM
Yes by going through the Editor html helper code I also discovered that the Metadata is contructed by a different method. But why? The metadata object for this property is already constructed, why is it created again by a different path?
Also the IsRequired property is not set on the metadata object for this other path, so it is set to true in the top level Object.asmx template but not in the subtemplate, but I think you are already aware of this bug (it was pointed out int the comments
to one of your posts on the mvc template system).
Anyway I managed to solve the issue by overriding GetMetadataForProperty instead of CreateMetadata.... (cannot remember the exact name dont have it infront of me).
There is no caching of ModelMetadata objects, so the Editor method can't assume anything and goes back to the provider to get new metadata.
I'm surprised that a simple CreateMetadata isn't doing the right thing, unless you're doing something we don't expect. Share the code if you want and I'll take a look.
Marked as answer by ricka6 on Mar 09, 2010 09:22 PM
It was not CreateMetadata, I remembered incorrectly. So this method is not used for the "sub-templates", however when I override GetMetadataForProperty instead and did the same thing (adding custom metadata) it worked for both top level templates that iterate
over metadata "properties" and for the subtemplate that use ViewData.ModelMetadata.
Regarding a IsRequired, I am unable to get this property on the metadata to be true using the [Required] attribute, maybe that is not supposed to happen. I disabled my metadata provider to check, and it is the default behavior with the default metadataprovider.
It was false both in top level and sub templates so this does not appear to be related to this issue.
Anyway a tip for V3 would be to have something like a ModelMetadataAttribute with a method like AddMetadata, makes it dead easy to add attributes that add additional metadata without having to touch the metadata provider.
Regarding a IsRequired, I am unable to get this property on the metadata to be true using the [Required] attribute, maybe that is not supposed to happen. I disabled my metadata provider to check, and it is the default behavior with the default metadataprovider.
It was false both in top level and sub templates so this does not appear to be related to this issue.
Correct, this attribute doesn't trigger IsRequired.
torkelodegaard
<div></div>
Anyway a tip for V3 would be to have something like a ModelMetadataAttribute with a method like AddMetadata, makes it dead easy to add attributes that add additional metadata without having to touch the metadata provider.
We actually have something like that going into the Futures library for RTM.
Marked as answer by ricka6 on Mar 13, 2010 04:30 AM
torkelodegaa...
0 Points
3 Posts
Bug with Metadata in template
Mar 08, 2010 02:06 PM|LINK
Lets say we have this default editor template (Objects.ascx):
<% foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForEdit && !ViewData.TemplateInfo.Visited(pm))) { %>
<%= prop.AdditionalValues.Values.Count %>
<%= Html.Editor(prop.PropertyName) %>
<% } %>
<div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"><% foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForEdit && !ViewData.TemplateInfo.Visited(pm))) { %> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> <%= prop.AdditionalValues.Values.Count %></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> <%= Html.Editor(prop.PropertyName) %> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"><% } %></div>I then have a specific template for a specific Type called: ProductFilter.ascx
<h2><%= ViewData.ModelMetadata.AdditionalValues.Values.Count %> </h2>
Never mind why I want to do this, the above code is just to show the bug. The AdditionalValues.Count is different inside the Objects.asxc template than in the specific template even though the metadata subscribe the exact same property. This is big problem with you implement a custom ModelMetadataProvider and want to do advanced templated forms for example. It is even a problem with the built in meta data like IsRequired, this property is set to true in prop.IsRequired in the foreach loop in the objects.ascx template but false in the specific template, again these Metadata object point to the same property and should be identical!
bradwils
Contributor
5779 Points
691 Posts
Microsoft
Re: Bug with Metadata in template
Mar 08, 2010 04:45 PM|LINK
It looks like there might be a bug in whatever metadata provider you're using.
Calling Html.Editor() will cause the system to get a new copy of the model metadata, and it will happen via a different path than the original. The original one came from a call to Properties() on the metadata of the container object, whereas the new one will come a call to the metadata provider's GetMetadataForProperty.
torkelodegaa...
0 Points
3 Posts
Re: Bug with Metadata in template
Mar 08, 2010 05:52 PM|LINK
Thanks for your reply Brad.
Yes by going through the Editor html helper code I also discovered that the Metadata is contructed by a different method. But why? The metadata object for this property is already constructed, why is it created again by a different path?
Also the IsRequired property is not set on the metadata object for this other path, so it is set to true in the top level Object.asmx template but not in the subtemplate, but I think you are already aware of this bug (it was pointed out int the comments to one of your posts on the mvc template system).
Anyway I managed to solve the issue by overriding GetMetadataForProperty instead of CreateMetadata.... (cannot remember the exact name dont have it infront of me).
/Thanks Torkel
bradwils
Contributor
5779 Points
691 Posts
Microsoft
Re: Bug with Metadata in template
Mar 09, 2010 12:37 AM|LINK
There is no caching of ModelMetadata objects, so the Editor method can't assume anything and goes back to the provider to get new metadata.
I'm surprised that a simple CreateMetadata isn't doing the right thing, unless you're doing something we don't expect. Share the code if you want and I'll take a look.
torkelodegaa...
0 Points
3 Posts
Re: Bug with Metadata in template
Mar 09, 2010 06:13 AM|LINK
Hi Brad,
Here my original code for my custom value provider:
public override IEnumerable<ModelMetadata> GetMetadataForProperties(object container, Type containerType) { IEnumerable<ModelMetadata> metadataForProperties = GetDefaultMetadata(container, containerType); foreach (ModelMetadata metadataForProperty in metadataForProperties) { PropertyInfo property = metadataForProperty.ContainerType.GetProperty(metadataForProperty.PropertyName); object[] metadataAttributes = property.GetCustomAttributes(typeof(ModelMetadataAttribute), true); foreach (var attribute in metadataAttributes) { ((ModelMetadataAttribute)attribute).AddMetadata(metadataForProperty); } yield return metadataForProperty; } }It was not CreateMetadata, I remembered incorrectly. So this method is not used for the "sub-templates", however when I override GetMetadataForProperty instead and did the same thing (adding custom metadata) it worked for both top level templates that iterate over metadata "properties" and for the subtemplate that use ViewData.ModelMetadata.
Regarding a IsRequired, I am unable to get this property on the metadata to be true using the [Required] attribute, maybe that is not supposed to happen. I disabled my metadata provider to check, and it is the default behavior with the default metadataprovider. It was false both in top level and sub templates so this does not appear to be related to this issue.
Anyway a tip for V3 would be to have something like a ModelMetadataAttribute with a method like AddMetadata, makes it dead easy to add attributes that add additional metadata without having to touch the metadata provider.
Cheers
Torkel
bradwils
Contributor
5779 Points
691 Posts
Microsoft
Re: Bug with Metadata in template
Mar 10, 2010 05:45 AM|LINK
Correct, this attribute doesn't trigger IsRequired.
We actually have something like that going into the Futures library for RTM.