---
title: Customizing the system's data classes (Info objects)
related:
  - https://docs.kentico.com/k9/custom-development/database-table-api.md
  - https://docs.kentico.com/k9/custom-development/customizing-providers.md
  - https://docs.kentico.com/k9/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 Kentico by adding your own fields (properties).

1. Create a custom data class containing the required fields.
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

If the connected 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/k9/macro-expressions.md).

## Example

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

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

### Defining the custom data class

1. Open your web project in Visual Studio.
2. Create a new class named **SiteRegistrationData.cs**.
   - Place the class file into the **App\_Code** folder if your Kentico installation is a _Web site_ project.
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 System.Linq;
using System.Web;

using CMS.Base;

public class SiteRegistrationData : IDataContainer
{
    #region "Variables"

    private string mSiteOwner = null;
    private DateTime mSiteValidUntil = DateTime.MinValue;

    #endregion

    #region "Properties"

    /// <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;
        }
    }

    #endregion

    #region "IDataContainer members"

    /// <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);
        }
    }

    #endregion

}

```

### 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 new class file in the **App\_Code** folder (or **CMSApp\_AppCode -> Old\_App\_Code** if your project was installed as a _Web application_).
2. Delete the default class declaration and instead extend the **CMSModuleLoader** partial class.
3. Create a new class inside the CMSModuleLoader that inherits from **CMSLoaderAttribute**.
4. Add the attribute defined by the internal class before the declaration of the **CMSModuleLoader** partial class.
5. Override the **Init** method inside the attribute class, and assign a handler to the **SiteInfo.TYPEINFO.OnLoadRelatedData** event.
6. 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 System.Collections.Generic;
using System.Linq;
using System.Web;

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

[SystemObjectCustomizationLoader]
public partial class CMSModuleLoader
{
    /// <summary>
    /// Custom attribute class for the CMSModuleLoader.
    /// </summary>
    private class SystemObjectCustomizationLoaderAttribute : CMSLoaderAttribute
    {
        /// <summary>
        /// Called automatically when the application starts.
        /// </summary>
        public override void Init()
        {
            // Assigns a handler to the OnLoadRelatedData event of the SiteInfo object type
            SiteInfo.TYPEINFO.OnLoadRelatedData += SiteInfo_OnLoadRelatedData;
        }

        /// <summary>
        /// Handler method for the OnLoadRelatedData event of the SiteInfo class.
        /// Gets the related data from the custom storage class (must implement the IDataContainer interface).
        /// </summary>
        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, log in to the Kentico administration interface and open your website in the **Pages** application. Add the following code into the ASCX [layout](https://docs.kentico.com/k9/developing-websites/developing-websites-using-the-portal-engine/editing-page-layouts.md) of one of your website's pages:

```html

<asp:Label runat="server" id="lblSiteInfo" />
<script runat="server">
    protected void Page_PreRender(object sender, EventArgs e)
    {
        CMS.SiteProvider.SiteInfo currentSite = CMS.SiteProvider.SiteContext.CurrentSite;

        this.lblSiteInfo.Text = String.Format("Site '{0}' is valid until {1} and owned by {2}.", currentSite.DisplayName, currentSite.GetValue("SiteValidUntil"), currentSite.GetValue("SiteOwner"));
    }
</script>

```

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

The label control displays information on the page in the following format:

_Site 'Corporate Site' is valid until 1/15/2014 1:00:00 PM and owned by John Smith._

#### Accessing properties through macros

You can also load the values of custom properties using [macro expressions](https://docs.kentico.com/k9/macro-expressions.md). For example:

1. In the **Pages** application, edit a page on the **Design** tab.
2. [Add](https://docs.kentico.com/k9/developing-websites/developing-websites-using-the-portal-engine/using-and-configuring-web-parts.md) the **Static text** web part to the page.
3. Copy the following text into the web part's **Text** property.

   ```html

   <ul>
       <li>Site: {% CurrentSite.SiteDisplayName %}</li>
       <li>Valid until: {% CurrentSite.SiteValidUntil %}</li>
       <li>Owned by: {% CurrentSite.SiteOwner %}</li>
   </ul>

   ```

The web part displays a list of information about the current website, including the values of the custom fields.
