Content item API

Xperience by Kentico provides the following API to manage and work with content items:

Content item query

Content item query is the default system API for content item retrieval. It allows you to retrieve content items based on their content type. Each query can be modified using SQL-like fluent API.

Using content query consists of the following steps:

  1. Building the query using ContentItemQueryBuilder.
  2. Running the query using IContentQueryExecutor and mapping the result to a model class for further use.

Build queries

To build content item queries, use ContentItemQueryBuilder. The class provides fluent API that allows you to tailor each query to your requirements.

Build a query

using CMS.ContentEngine;
// ...

// The builder class must be directly instantiated
var builder = new ContentItemQueryBuilder();
// Selects all items of the 'Acme.Article' content type
builder.ForContentType("Acme.Article");

Each ForContentType call begins a subquery where you can further adjust the retrieval parameters for the corresponding content type. 

Parameterize a subquery

// Selects an article called 'Security'
builder.ForContentType("Acme.Article", subqueryConfiguration => 
{
    subqueryConfiguration
        .TopN(1)
        .Where(where => where.WhereEquals("ContentItemName", "Security"));
});

For all available parameterization options, see Reference - Content item query.

Finally, the entire query can be modified.

Parameterize the entire query

builder.ForContentTypes(parameters => 
        {
            parameters.OfContentType("Acme.Article", "Acme.NewsRelease")
        })
        // Sorts all records according to the 'ContentItemName' column
       .Parameters(globalParams => globalParams.OrderBy("ContentItemName"));

The following diagram illustrates the general structure of content queries:

Content item query structure diagram

Loading other objects

To learn how to retrieve other types of data from the Xperience database, see ObjectQuery API.

Run queries and map the result

Queries are executed using IContentQueryExecutor, which retrieves data according to the passed ContentItemQueryBuilder instance. When retrieved, the query result is a collection of database rows. To transform the data into a typed format suitable for C#, part of query execution is a process known as model binding.

Model binding maps the data from each row to a C# object, assigning each column an appropriate C# type in the process. The resulting object instance is called a strongly-typed representation of the database data. Xperience provides code generators that enable developers to generate model classes directly mirroring each content type’s database representation. These classes are directly used in the model binding process.

IContentQueryExecutor provides two approaches to facilitate model binding:

  • GetMappedResult<TModel> (GetMappedWebPageResult<TModel> for page content types). These methods fully abstract the model binding process, directly returning strongly-typed models.
  • GetResult<TModel> (GetWebPageResult<TModel> for page content types). These methods expose the model binding logic that gives you direct access to each row of the database data, allowing you to customize the mapping process.

Using GetMappedResult methods

Using GetMappedResult<TModel>, the system runs the query and binds the result to a collection of TModel classes automatically in the background. This approach is recommended for the majority of scenarios.

Run a query using GetMappedResult<TModel>

using CMS.ContentEngine;

// Contains an instance of 'IContentQueryExecutor'
// obtained using dependency injection
private readonly IContentQueryExecutor contentQueryExecutor;

var builder = new ContentItemQueryBuilder();
builder.ForContentType(Article.CONTENT_TYPE_NAME);

// The 'Article' class is generated by the code generator
// for a corresponding 'Article' content type
IEnumerable<Article> articles = 
            await contentQueryExecutor.GetMappedWebPageResult<Article>(builder);

The model binding logic matches database column names to the model’s properties, with certain exceptions made for system data.

When retrieving data that consist of multiple content types, you must, using the TModel generic, cast the result to a type shared by all model classes. Depending on the contents of the result, you have the following options:

  • Use IContentItemFieldsSource. This interface is by default implemented by all generated model classes.

    Getting items of multiple content types
    
      using CMS.ContentEngine;
    
      // Contains an instance of 'IContentQueryExecutor'
      // obtained using dependency injection
      private readonly IContentQueryExecutor contentQueryExecutor;
    
      var builder = new ContentItemQueryBuilder();
      builder.ForContentTypes(query =>
      {
          query.OfContentType(Article.CONTENT_TYPE_NAME, Blog.CONTENT_TYPE_NAME);
          query.WithContentTypeFields();
      });
    
      // Gets a mixed collection of articles and blogs
      IEnumerable<IContentItemFieldsSource> result = 
              await executor.GetMappedResult<IContentItemFieldsSource>(builder);
    
      // Gets all articles 
      List<Article> articles = result.OfType<Article>().ToList();
      // Gets all blogs
      List<Blog> blogs = result.OfType<Blog>().ToList();
      
  • When retrieving items that share a reusable field schemas, reference the schema interface in TModel:

    Getting items that share a reusable field schema
    
      using CMS.ContentEngine;
    
      // Contains an instance of 'IContentQueryExecutor'
      // obtained using dependency injection
      private readonly IContentQueryExecutor contentQueryExecutor;
    
      var builder = new ContentItemQueryBuilder();
      builder.ForContentTypes(query =>
      {
          query.OfReusableSchema("PageMetadata");
      });
    
      // Gets a collection if items with the 'PageMetadata' schema
      IEnumerable<IPageMetadata> result = 
              await executor.GetMappedWebPageResult<IPageMetadata>(builder);
      
  • Use System.Object in case the data shares no common ancestor type.

The GetMappedResult methods also provide overloads that allow you to manipulate the model after its data was bound.

Modify bound data

IEnumerable<Article> articles =
    await executor.GetMappedWebPageResult<Article>(builder, null, OverrideMapping);

// Called after each item is bound
// 'IContentQueryDataContainer' contains current row data
// 'Article' is the instance of the bound model class
private Article OverrideMapping(IContentQueryDataContainer container, Article article)
{
    // Custom logic to modify/extend the default mapping...

    return article;
}

Using GetResult methods

Using GetResult<TModel> allows you to take over the entire model binding process.

Run a query and map the data using GetResult<TModel>

using CMS.ContentEngine;

// Contains an instance of 'IContentQueryExecutor'
// obtained using dependency injection
private readonly IContentQueryExecutor contentQueryExecutor;

// Executes the query specified within 'contentItemQueryBuilder'
// and binds it using the logic in the 'ModelBinder' delegate
var result = 
    await contentQueryExecutor.GetResult(contentItemQueryBuilder, ModelBinder);

Where ModelBinder is a delegate function used to map the retrieved data to the result (can be asynchronous) that gives you direct access to each retrieved data row. You can use IContentQueryResultMapper.Map, or provide custom binding logic to map the data.

IContentQueryResultMapper usage

using CMS.ContentEngine;

// Contains an instance of 'IContentQueryResultMapper' (e.g., obtained using dependency injection)
private readonly IContentQueryResultMapper mapper;

// Maps the result to 'MyModelClass'
private MyModelClass ModelBinder(IContentQueryDataContainer container)
{
    // Maps the data from the container (representing 
    // one content item) to the model class
    // The mapper performs case-insensitive mapping from the type's 
    // database columns to the class's properties, with a few exceptions 
    // (see the method's API documentation)
    return mapper.Map<MyModelClass>(container);
}

Models can be either

  • generated classes – generated classes directly mirror content type fields as defined via the field editor and work seamlessly with the mapper API.
  • (advanced use case) custom classes – using custom classes enables you to map only desired columns. Before using custom classes, familiarize yourself with the mapper API or prepare custom mapping logic. Note that some Xperience APIs that work with content items expect certain system fields to be present in the model and will not work as expected otherwise.
Example - Retrieve and bind to a generated model class

using System;
using System.Collections.Generic;

using CMS.ContentEngine;

// Contains instances obtained using constructor dependency injection
private readonly IContentQueryExecutor contentQueryExecutor;
private readonly IContentQueryResultMapper mapper;

var contentItemQueryBuilder = new ContentItemQueryBuilder();

// Selects all objects of the 'VacationSpot' content type
contentItemQueryBuilder.ForContentType(VacationSpot.CONTENT_TYPE_NAME);

// Executes the query specified within 'contentItemQueryBuilder' and binds it to the 'VacationSpot' class generated for the 'VacationSpot' content type
IEnumerable<VacationSpot> result =
        await contentQueryExecutor
                .GetResult(contentItemQueryBuilder,
                           container => mapper.Map<VacationSpot>(container));

See the Content items section in the API Examples for more examples.

Example - Retrieve and bind to a custom model class

using System;
using System.Collections.Generic;

using CMS.ContentEngine;

// Contains instances obtained using constructor dependency injection
private readonly IContentQueryExecutor contentQueryExecutor;
private readonly IContentQueryResultMapper mapper;

// Executes the query specified within 'contentItemQueryBuilder' and binds it using the logic in 'DtoBinder'
IEnumerable<Dto> result = contentQueryExecutor.GetResult(contentItemQueryBuilder, DtoBinder);

// A function delegate that binds the returned records to a custom model.
// The content type of the item being bound is stored in 'IContentQueryDataContainer.ContentTypeName'
private Dto DtoBinder(IContentQueryDataContainer container)
{
    // 'IContentQueryResultMapper' maps column data to corresponding 
    // properties based on matching names. For the example 'Dto' object,
    // only columns named 'Title' and 'Content' get mapped in addition to 'SystemFields'.
    // All other fields of the content type are ignored.
    return mapper.Map<Dto>(container);
}

// A data transfer object used as a container for the retrieved data
// The structure of these objects is completely under your control
public class Dto
{
    // Maps Xperience-specific fields
    // When mapping pages, use the 'CMS.Websites.WebPageFields' type instead
    public ContentItemFields SystemFields { get; set; }
    // Maps the 'Title' column from the database
    public string Title { get; set; }
    // Maps the 'Content' column from the database
    public string Content { get; set; }
}

Query execution options

The ContentQueryExecutionOptions class allows you to optionally configure querying behavior. See Reference - Content item query for a list of available configuration options.

Example - Configure query execution

// Ensures the latest version of the selected content items, 
// regardless of workflow state (e.g., returns items in 'Draft')
contentQueryExecutor.GetResult(contentItemQueryBuilder,
                               ModelBinder,
                               new ContentQueryExecutionOptions()
                                    { ForPreview = true });

Content item manager

Xperience provides a management API for content items via the IContentItemManager class.

See the Content items section in the API Examples for examples of usage.

Pages manager

Xperience provides a management API for pages via the IWebPageManager class. 

See the Pages section in the API Examples for examples of usage.