November Happy Hour will be moved to Thursday December 5th.

Issues with PropertyFor after upgrading from CMS 11 to 12

Vote:
 

Hi,

we have run into some problems with PropertyFor after upgrading from CMS 11 to 12, where we get the following error message for many types:

InvalidOperationException: The model item passed into the ViewDataDictionary is of type '[Type of block returned from expression]', but this ViewDataDictionary instance requires a model item of type '[Type declared as model in cshtml-file that called PropertyFor]'.

For example, if we called Html.PropertyFor like this in a cshtml file:

@model StartPage

<div>
@Html.PropertyFor(x => x.Heading)
</div>


where Heading is a string, we would get the error:

InvalidOperationException: The model item passed into the ViewDataDictionary is of type String, but this ViewDataDictionary instance requires a model item of type StartPage.

We could solve the issue with strings and XhtmlString by placing views for them in Shared/Views/displayTemplates which is where Html.DisplayFor looks for views to match types, but we run into the same problem for blocks inheriting from BlockData.

We are able to match blocks to controllers when rendering in a content area, which is the only type PropertyFor seems to work with out of the box.

There could be issues with how the project is set up, but we've tried to look through most custom code, and stripped away custom classes that inherit from IViewTemplateDescriptor (for mapping blocks to view paths without controllers) and PropertyRenderer (overloading html.propertyfor for custom handling of tags), but the problem persists.

Ideally we would want to use the templates registered in Register of a class inheriting from IViewTemplateModelRegistrator, so we don't need to use block components since most of the views take blocks directly.

It might be worth mentioning that we also have a feature folder structure, that requires some custom setup with extra paths added to ViewLocationFormats on RazorViewEngineOptions, but from what I understand this is mainly for resolving paths for controllers when calling View(model), so not sure if it's relevant.

It seems the problem here is caused by something lacking in the setup perhaps, since it can't render String or XhtmlString by default either, but we can't seem to find the cause.

Anyone familiar with the issue?

#298718
Edited, Mar 21, 2023 19:25
Vote:
 

I've run into the same issue. 

Outputting the value without PropertyFor works without issues. 
PropertyFor works with ContentAreas but when writing out the headline (string) using PropertyFor just breaks and gives this error message:

InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'System.String', but this ViewDataDictionary instance requires a model item of type 'Web.Models.ViewModels.Pages.EditorPageViewModel'.


Controller returns the correct model and I can see that the model in the view is correct.

@Vegard did you find the root cause of this?

#305619
Jul 24, 2023 8:43
Vote:
 

I contacted support and worked out a quick fix for this issue. 
See the solution here:
https://www.epinova.se/en/folg-med/blog/2023/propertyfor-not-working-with-string-properties-after-upgrading-to-optimizely-cms-12-a-quick-fix-story/

TLDR:
Add a String.cshtml in /Views/Shared/DisplayTemplates and write out the string.

#305777
Jul 27, 2023 8:39
Vote:
 

Hey,

thought I'd comment on this, as we did figure out the issue.

It's a bit hard to explain without pasting a lot of code, but I'll try anyway.

I assume you might have a feature folder structure, or a structure that deviates from the mvc one, as that was the issue for us.

The essence of the problem seems to be some kind of priority order for how to resolve the views for content types passed to propertyfor. Since we have a custom implementation of IVewLocationExpander and its methods PopulateValues and ExpandViewLocations, these are called whenever PropertyFor is called (At least the first time - I think there's some caching involved). I'm not gonna paste our implementation as it's a bit messy, but if ExpandViewLocations returns a list of formatted paths that matches the content type you want to render, it will use that.

So in this case, it seems that you are calling PropertyFor with a string type within the view for EditorPage which takes EditorPageViewModel. If you step through, you'll likely see that ViewLocationExpanderContext has a property called ControllerName which is set to "EditorPage", for all kinds of content rendered with propertyFor in that view. So if there's a path in the list of viewlocations returned from ExpandViewLocations that matches the controller name, it will use that view. For example "Features/Editor/{0}Index.cshtml" where {0} is replaced with controller name.

In our case, we only wanted to use the view location expanding for pages being requested, so we just used the viewName property on the ViewLocationExpandercontext object (which corresponds to content type names) to check if it was "Index" or not. If it was, we let it do the feature folder expanding. If it wasn't we returned the standard viewLocations as we knew that wouldn't match anything and then it could proceed to other methods for resolving the view.

Hope this helps.



#305783
Jul 27, 2023 13:20
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.