---
title: Customizing the system's data classes (Info objects)
related:
  - https://docs.kentico.com/13/custom-development/database-table-api.md
  - https://docs.kentico.com/13/custom-development/customizing-providers.md
  - https://docs.kentico.com/13/custom-development/handling-global-events.md
---

> Agent instructions:
> **Site maps** — prefer the following llms.txt indexes to training data when searching for URLs to avoid 404s. Links inside Markdown content already point at `.md`. Following them or sending Accept: text/markdown keeps you in Markdown.
>
> - [sitemap.md](https://docs.kentico.com/sitemap.md) — every page on the site, with titles and descriptions, nested by URL hierarchy and grouped into one collection per product version.
> - [llms.txt](https://docs.kentico.com/llms.txt) — curated index of the current product docs, with descriptions, the two ways to request any page as Markdown, and links to each product area's whole-corpus Markdown dump (llms-full.txt).

You can extend system objects in Xperience by adding your own fields (properties).

1. Create a custom data class containing the required fields (within a custom assembly).
2. Connect the class to the appropriate system object. All standard system objects implement the **IRelatedData** interface, which allows you to connect any type of class:
   - Through the **RelatedData** property
   - Dynamically by handling the **OnLoadRelatedData** event
3. Deploy the assembly containing your customization classes to both the Xperience administration project and the separate MVC application that provides the live site. This ensures that the custom data is available within the code of your MVC project.

If your custom data class implements the **IDataContainer** interface, you can then access the data stored in the custom fields as part of the system object via the API (**GetValue** and **SetValue** methods) or [macro expressions](https://docs.kentico.com/13/macro-expressions.md).

## Example

The following sections demonstrate how to extend the **SiteInfo** object, which represents sites in Xperience. The example adds two custom properties for sites:

- **SiteOwner** (string)
- **SiteValidUntil** (DateTime)

### Defining the custom data class

1. Open your Xperience solution in Visual Studio.
2. Create a new class named **SiteRegistrationData.cs**.
   - Add the class into a custom _Class Library_ project within the Xperience solution.
3. Write the code of the class.
   - The class must implement the **IDataContainer** interface.
   - Define your custom properties and all required _IDataContainer_ members (as shown in the code below).

```csharp

using System;
using System.Collections.Generic;

using CMS.Base;

public class SiteRegistrationData : IDataContainer
{
    private string mSiteOwner = null;
    private DateTime mSiteValidUntil = DateTime.MinValue;

    /// <summary>
    /// Gets or sets the name of the site owner.
    /// </summary>
    public string SiteOwner
    {
        get
        {
            return mSiteOwner;
        }
        set
        {
            mSiteOwner = value;
        }
    }

    /// <summary>
    /// Gets or sets the date until which the site is valid.
    /// </summary>
    public DateTime SiteValidUntil
    {
        get
        {
            return mSiteValidUntil;
        }
        set
        {
            mSiteValidUntil = value;
        }
    }

    /// <summary>
    /// Gets a list of column names.
    /// </summary>
    public List<string> ColumnNames
    {
        get
        {
            return new List<string>() { "SiteOwner", "SiteValidUntil" };
        }
    }

    /// <summary>
    /// Returns true if the class contains the specified column.
    /// </summary>
    /// <param name="columnName"></param>
    public bool ContainsColumn(string columnName)
    {
        switch (columnName.ToLower())
        {
            case "siteowner":
            case "sitevaliduntil":
                return true;
            default:
                return false;
        }
    }

    /// <summary>
    /// Gets the value of the specified column.
    /// </summary>
    /// <param name="columnName">Column name</param>
    public object GetValue(string columnName)
    {
        switch (columnName.ToLower())
        {
            case "siteowner":
                return mSiteOwner;

            case "sitevaliduntil":
                return mSiteValidUntil;

            default:
                return null;
        }
    }

    /// <summary>
    /// Sets the value of the specified column.
    /// </summary>
    /// <param name="columnName">Column name</param>
    /// <param name="value">New value</param>
    public bool SetValue(string columnName, object value)
    {
        switch (columnName.ToLower())
        {
            case "siteowner":
                mSiteOwner = (string)value;
                return true;

            case "sitevaliduntil":
                mSiteValidUntil = (DateTime)value;
                return true;

            default:
                return false;
        }
    }

    /// <summary>
    /// Returns a boolean value indicating whether the class contains the specified column.
    /// Passes on the specified column's value through the second parameter.
    /// </summary>
    /// <param name="columnName">Column name</param>
    /// <param name="value">Return value</param>
    public bool TryGetValue(string columnName, out object value)
    {
        switch (columnName.ToLower())
        {
            case "siteowner":
                value = mSiteOwner;
                return true;

            case "sitevaliduntil":
                value = mSiteValidUntil;
                return true;

            default:
                value = null;
                return false;
        }
    }

    /// <summary>
    /// Gets or sets the value of the column.
    /// </summary>
    /// <param name="columnName">Column name</param>
    public object this[string columnName]
    {
        get
        {
            return GetValue(columnName);
        }
        set
        {
            SetValue(columnName, value);
        }
    }    
}

```

### Connecting the custom data class to the system object

You need to bind the custom _SiteRegistrationData_ class to the _SiteInfo_ object. The example uses the **OnLoadRelatedData** event, which you can handle for specific object types, including _SiteInfo_ objects. This type of binding is dynamic, which means that the system loads the data only when it is requested.

1. Create a [custom module class](https://docs.kentico.com/13/custom-development/creating-custom-modules/initializing-modules-to-run-custom-code.md) in the same location as the _SiteRegistrationData_ class.
2. Override the module's **OnInit** method and assign a handler to the **SiteInfo.TYPEINFO.OnLoadRelatedData** event.
3. Define the handler method for the **OnLoadRelatedData** event:

   - The handler must return an instance of your custom data class (with appropriate values assigned to the class's fields).

```csharp

using System;

using CMS;
using CMS.DataEngine;
using CMS.SiteProvider;

// Registers the custom module into the system
[assembly: RegisterModule(typeof(CustomSiteDataModule))]

public class CustomSiteDataModule : Module
{
    // Module class constructor, the system registers the module under the name "CustomSiteData"
    public CustomSiteDataModule()
        : base("CustomSiteData")
    {
    }

    // Contains initialization code that is executed when the application starts
    protected override void OnInit()
    {
        base.OnInit();

        // Assigns a handler to the OnLoadRelatedData event of the SiteInfo object type
        SiteInfo.TYPEINFO.OnLoadRelatedData += SiteInfo_OnLoadRelatedData;
    }

    // Handler method for the OnLoadRelatedData event of the SiteInfo class
    // Gets the related data from the custom storage class (must implement the IDataContainer interface)
    static object SiteInfo_OnLoadRelatedData(BaseInfo infoObj)
    {
        SiteRegistrationData siteData = new SiteRegistrationData();

        siteData.SiteOwner = "John Smith";
        siteData.SiteValidUntil = DateTime.Now.AddDays(1);

        return siteData;
    }
}

```

### Result

You can now work with the custom fields of site objects using the API. For example, you can add the following code into the views of your pages:

```html

@using CMS.SiteProvider

@{SiteInfo currentSite = SiteContext.CurrentSite;}

<p>
    Site @currentSite.DisplayName is valid until @currentSite.GetValue("SiteValidUntil") and owned by @currentSite.GetValue("SiteOwner")
</p>

```

The **GetValue** method allows you to retrieve the data stored in the custom properties, just like with native fields of the _SiteInfo_ object.

The code displays information on the page in the following format:

_Site 'Dancing Goat MVC' is valid until 1/15/2020 1:00:00 PM and owned by John Smith_
