Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing nested properties in a Kentico custom object macro method

I am trying to write a single macro method in Kentico (v8.2.x, or v9.0) that is cached appropriately, and exposes a POCO with a few public members.

Debugging in Visual Studio, I can see that the query is running fine, and an object instance is returned exactly how I want. Furthermore, inspecting the cached items using the Debug application in Kentico also shows the full POCO instance data is cached as expected.

However, when invoking the macro, I only seem to be able to read my string representation of the object.

It is a macro that extends the CurrentUserInfo type, so I am trying to invoke it like this:

{% CurrentUser.ClientStatus() %}

But attempts to retrieve any of the nested properties fails.

I am sure it is just that I have failed to do something (like register these properties correctly). But from the documentation, I have seen lots of things that it could be. For example:

  • Named source
  • Named callback source
  • Anonymous source
  • Or registering them as separate fields somehow

Here is the macro itself:

/// <summary>
/// A class containing custom user-extension macros.
/// </summary>
[assembly: RegisterExtension(typeof(CustomUserMacros), typeof(CurrentUserInfo))]
public class CustomUserMacros : MacroMethodContainer
{
    /// <summary>
    /// Retrieves data regarding user client.
    /// </summary>
    /// <param name="context">The context.</param>
    /// <param name="parameters">The parameters.</param>
    /// <returns>Data regarding user client information.</returns>
    [MacroMethod(typeof(ClientInfo), "Retrieves client info.", 1)]
    [MacroMethodParam(0, "user", typeof(CurrentUserInfo), "The user.")]
    public static object ClientStatus(
            EvaluationContext context,
            params object[] parameters)
    {
        ClientInfo retVal = null;
        if (parameters != null && parameters.Length > 0
                && parameters[0].GetType() == typeof(CurrentUserInfo))
        {
            var siteName = SiteContext.CurrentSiteName;
            var userGuid = ((CurrentUserInfo)parameters[0]).UserGUID;
            var uInfo = UserInfoProvider.GetUserInfoByGUID(userGuid);
            retVal = CacheHelper.Cache(
                cs => new ClientInfo(uInfo, siteName),
                new CacheSettings(
                    60, 
                    typeof(CustomUserMacros), 
                    "ClientStatus", 
                    userGuid));
        }

        return retVal;
    }
}

And the ClientInfo class is pretty straight-forward:

public class ClientInfo
{
    public string Summary { get; private set; }

    public CustomTableItem ClientRecord { get; private set; }

    public IEnumerable<string> MediaPaths { get; private set; }

    public ClientInfo(UserInfo userInfo, string siteCodeName) {
        // ...
        // Set properties, etc...
    }

    public override string ToString()
    {
        return Summary;
    }
}

What is the easiest way for me to be able to access the properties, in a manner similar to the following?

{% CurrentUser.ClientStatus().ClientRecord["< Column Name >"] %}
like image 900
ne1410s Avatar asked Feb 02 '16 09:02

ne1410s


2 Answers

To be able to access the properties in the way you describe, the parent object has to be of the DataRow or DataRowView type or implement one of the following interfaces: IVirtualHierarchicalObject, IHierarchicalObject, IDataContainer, ISimpleDataContainer.

In your case, ClientInfo should implement IDataContainer. The nested CustomTableItem already implements one of the interfaces as it inherits from AbstractInfo.

You'll have to implement several members which mainly work with a parameter called columnName identifying the member whose value you should return:

public class ClientInfo : IDataContainer
{
    ...
    public object GetValue(string columnName)
    {
        switch (columnName)
        {
            case "ClientRecord":
                return ClientRecord;

            case "Summary":
                return Summary;

            case "MediaPaths":
                return MediaPaths;
        }
        return null;
    }
    ...
}
like image 124
rocky Avatar answered Nov 19 '22 09:11

rocky


Probably would not have worked in this case but worth noting as well that you can return simple object values using CMS.Base.DataContainer or IEnumerable<CMS.Base.DataContainer>:

return myCollection.Select(myObj => new DataContainer
                {
                    ["Value1"] = myObj.Value1,
                    ["Value2"] = myObj.Value2
                });
like image 1
nickwesselman Avatar answered Nov 19 '22 09:11

nickwesselman