I encountered a problem when reviewing some legacy code using Glass Mapper v4.3. Glass Mapper no longer supports dynamically build properties, while in Sitecore Experience Editor “Edit” mode.
View renderings had expressions like this:
using (BeginRenderLink(Model.GetListHeader(sList), x => x, isEditable: true))
{
@Html.Raw(Model.GetListHeader(sList).Text);
}
The supporting Model class had the following:
[SitecoreField("List1_Header")]
public virtual Link List1Header { get; set; }
[SitecoreField("List2_Header")]
public virtual Link List2Header { get; set; }
[SitecoreField("List3_Header")]
public virtual Link List3Header { get; set; }
[SitecoreField("List4_Header")]
public virtual Link List4Header { get; set; }
public virtual Link GetListHeader(string sRenderingParameter)
{
Link oReturn = null;
int iList = 0;
iList = Int32.Parse(sRenderingParameter);
switch (iList)
{
case 1:
oReturn = List1Header;
break;
case 2:
oReturn = List2Header;
break;
case 3:
oReturn = List3Header;
break;
case 4:
oReturn = List4Header;
break;
default:
break;
}
return oReturn;
}
While in Experience Editor Edit mode, the following exception was thrown:
Expression doesn't evaluate to a member x
...
at Glass.Mapper.Utilities.GetTargetObjectOfLamba[T](Expression`1 field, T model, MemberExpression& memberExpression) at Glass.Mapper.Sc.GlassHtml.MakeEditable[T](Expression`1 field, Expression`1 standardOutput, T model, Object parameters, Context context, Database database, TextWriter writer)
I did some digging on the web and found a few other references to this issue:
https://stackoverflow.com/questions/44059614/can-i-use-glassmappers-editable-on-a-reflected-property
Glass Mapper checks to make sure the lambda expression is a MemberExpression, and throws an exception when it’s not. This is necessary due to updates in newer versions of Glass Mapper.
Here is what I did to work around this issue.
Remove the Dynamic Property References?
I could refactor the View rendering, adding an endless number of if statements, effectively pulling the model code into the View rendering, so that the property is statically referenced. That would create an even bigger mess. This wasn’t going to work for me. No thank you.
Building a Dynamic MemberExpression in a Helper Method
After doing some reading about lambda expressions and coming across this question on Stack Overflow, I added the following method to the model class:
public virtual Expression<Func<T, object>> GetListHeaderExp(string sRenderingParameter)
{
Expression<Func<T, object>> oReturn = null;
int iList = 0;
iList = Int32.Parse(sRenderingParameter);
PropertyInfo propertyInfo = null;
switch (iList)
{
case 1:
propertyInfo = typeof(GenericPage).GetProperty("List1Header");
break;
case 2:
propertyInfo = typeof(GenericPage).GetProperty("List2Header");
break;
case 3:
propertyInfo = typeof(GenericPage).GetProperty("List3Header");
break;
case 4:
propertyInfo = typeof(GenericPage).GetProperty("List4Header");
break;
default:
break;
}
var entityParam = Expression.Parameter(typeof(GenericPage), "e");
Expression columnExpr = Expression.Property(entityParam, propertyInfo);
if (propertyInfo.PropertyType != typeof(Link))
columnExpr = Expression.Convert(columnExpr, typeof(Link));
oReturn = Expression.Lambda<Func<T, object>>(columnExpr, entityParam);
return oReturn;
}
Call the MemberExpression Builder Method from the View
I then refactored the call in the View rendering to:
using (BeginRenderLink(Model, Model.GetListHeaderExp(sList), isEditable: true))
{
@Html.Raw(Model.GetListHeader(sList).Text);
}
Conclusion
That did the trick. I can now edit the link using the Sitecore Experience Editor, while keeping the dynamic property reference.
I’m hoping to write about my experience migrating an enterprise Sitecore installation from 7.2 to 8.2. Hopefully, I’ll get to that soon.