Autowiring XConnect Models

I’ve been playing around with XConnect for the last few weeks and so far I like what I see.

While playing around with custom models, I noticed that model schemas need to be manually registered. While not actually a big deal, this is an additional step that can be forgotten. I prefer to eliminate these “forgettable” tasks. I sought to determine a way of automatically wiring in these models. I’m pretty handy with reflection and figured if I could tag these models with an attribute, I could figure something out.

The Standard Configuration

For starters, let’s take a look at the configuration to see how you would normally add a model:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <xconnect>
      <runtime type="Sitecore.XConnect.Client.Configuration.RuntimeModelConfiguration,Sitecore.XConnect.Client.Configuration">
        <schemas hint="list:AddModelConfiguration">
          <!-- value of 'name' property must be unique -->
          <schema name="documentationmodel" type="Sitecore.XConnect.Client.Configuration.StaticModelConfiguration,Sitecore.XConnect.Client.Configuration" patch:after="schema[@name='collectionmodel']">
            <param desc="modeltype">Documentation.Model.CollectionModel, Documentation.Model</param>
          </schema>
        </schemas>
      </runtime>
    </xconnect>
  </sitecore>
</configuration>

Let’s break this down a bit. RuntimeModelConfiguration is the runtime model the XConnect client uses. It has a public method AddModelConfiguration This method is called in the XML at runtime. This entire config block essentially translates to:

var runtime = new RuntimeModelConfiguration();
var documentationmodel = new StaticModelConfiguration();
runtime.AddModelConfiguration(modeltype: documentationmodel);

The Model

First, I need an attribute:

public class XModelAttribute : Attribute
{
}

That was easy. Now I need a model:

[XModel]
public class TestModel
{
    public static XdbModel Model => BuildModel();
    public static string ModelName => typeof(TestModel).FullName;
    protected static XdbModelVersion ModelVersion => new XdbModelVersion(0, 1);

    private static XdbModel BuildModel()
    {
        var builder = new XdbModelBuilder(ModelName, ModelVersion);
        builder.ReferenceModel(CollectionModel.Model);
        return builder.BuildModel();
    }
}

The above is a sample model. I used the dev docs as an example, but trimmed it down a bit.

The Registration

I wanted to customize as little as possible to make upgrades a bit easier. I also wanted to ensure compatibility in case someone wanted to manually register models. I decided the cleanest way to do this would be to extend the RuntimeModelConfiguration

public class CustomRuntimeModelConfiguration : RuntimeModelConfiguration
{
    public CustomRuntimeModelConfiguration()
    {
        var assemblies = AppDomain.CurrentDomain.GetAssemblies();
        foreach (var assembly in assemblies)
        {
            var types = assembly.GetTypes().Where(t => t.IsDefined(typeof(XModelAttribute), true));
            foreach (var type in types)
            {
                AddModelConfiguration(new StaticModelConfiguration($"{type.FullName}, {type.Assembly}"));
            }
        }
    }
}

When this configuration is initialized, it scans the current assemblies for any classes decorated with the XModel attribute, and creates a new StaticModelConfiguration for it. It’s then added to the runtime model.

Remember this from before?

var runtime = new RuntimeModelConfiguration();
var documentationmodel = new StaticModelConfiguration();
runtime.AddModelConfiguration(modeltype: documentationmodel);

We’re doing the same thing now, but from the constructor.

All that’s left is to patch the config so that we’re using the Custom runtime config.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <xconnect>
      <runtime type="Sitecore.XConnect.Client.Configuration.RuntimeModelConfiguration,Sitecore.XConnect.Client.Configuration">
        <patch:attribute name="type" value="Foundation.XConnect.CustomRuntimeModelConfiguration, Foundation" />
      </runtime>
    </xconnect>
  </sitecore>
</configuration>

The Conclusion

Our custom XConnect models will now autowire themselves! The actual customization is fairly light. Manual registration is still possible, as well.

Now you’re probably wondering, “isn’t tagging these classes with an attribute is almost as forgettable as adding an entry to a config file?”

Well… yeah, technically. At least it’s in the same file now, though!