Accessing nested properties in a Kentico custom object macro method
Asked Answered
S

3

6

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 >"] %}
Sholom answered 2/2, 2016 at 9:59 Comment(2)
On further reading, I think in my case all this is probably best off in a custom module instead. Nevertheless, I will leave the question here as I like the K# macros, and I believe it would still be useful to understand how I could get the above to work!Sholom
Good question! Documentation on macros is a bit complicated but this does address some questions I've had in the past.Bromic
F
5

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;
    }
    ...
}
Foveola answered 2/2, 2016 at 18:17 Comment(1)
I implemented all the interface members (except for the set ones), and it works like a charm! Thanks so muchSholom
K
1

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
                });
Killion answered 26/1, 2017 at 19:35 Comment(0)
M
-2

First of all I'd recommend you to use System -> Macros -> Console to test your macro - it very useful. In your case you should be able to access properties like this:

{% CurrentUser.ClientStatus().Summary %}
Misbehave answered 2/2, 2016 at 14:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.