Generate code files for system objects
Certain types of objects in the system can contain custom fields that are used to model and structure the content stored within them. A typical example is a content type object with a title, article summary, and article text. Custom fields are assigned either via the field editor or the Form Builder editing interface and correspond to a single database column.
Since each object can contain a different set of custom fields, it’s impossible to provide strongly-typed access to individual fields via a shared general class. For this reason, accessing custom fields can be unwieldy, often needing additional code to be written. The process requires developers to:
- specify database column names to manipulate data from particular fields, and
- manually perform value type conversion and validation on the data as it gets retrieved from and stored to the database.
Therefore, to make working with such objects easier, the system allows you to generate object-specific code files. A code file contains properties that correspond to custom fields defined for the source object (e.g., title, article summary, article text for a content type). Each such property is assigned a data type and getter/setter logic suitable for manipulating the data of the underlying field. This allows you to work with the field’s data using the underlying C# data type without writing additional code. For more information about field data types, see Field data type management.
Code generation is available for the following objects:
- Reusable content types – generates a class with the reusable content type’s fields as properties. See Content item API to learn about available API that can be used with generated content type classes.
- Page content types – generates a class with the page content type’s fields as properties. See Content item API to learn about available API that can be used with generated page classes.
- Reusable field schemas – generates an interface that defines the corresponding schema’s fields as its properties.
- Forms – generates a class with the form’s fields as properties. See Content retrieval to learn about available API that can be used with generated form classes.
- Object types – generates classes that facilitate working with object types. See Database table API to learn about the architecture and available API of such objects.
- Code generation for object types can be further configured in the administration, see Configure code generation for data classes.
Generate code files
The code generator is command line-based. Code files are generated via the dotnet run --kxp-codegen
.NET CLI command. You can run the generator from a command line interface of your choice (cmd, PowerShell, Bash).
Run the command from the root directory of your Xperience project:
cd my/Xperience/app/root
dotnet run --no-build -- --kxp-codegen --type "ReusableContentTypes"
Note that if you generate the code files to a custom class library, the library must use the AssemblyDiscoverable
assembly-level attribute.
Command parameters
Add the --no-build
parameter to the dotnet run command if you don’t wish to rebuild the project before the classes are generated.
Add the --
syntax separator before any command parameters that you wish to pass directly to the Xperience application (--kxp-codegen
). This prevents conflicts with general .NET CLI parameters, for example when using the --help
option.
The code generator supports the following parameters. You can also view the supported parameters by running the generator with the --help
option:
Parameter | Required | Description |
--type | Yes | Sets the type of objects for which the code files are generated. Available types:
The switches are case-sensitive. CMD Example
|
--skip-confirmation | No | Skips the confirmation message displayed before the code files are generated. |
--include | No | Pattern to limit the included objects. Use ‘;’ to separate multiple object names. Use the ‘*’ wildcard character to match all objects with the same prefix. Reusable field schemas don’t use a namespace prefix, but you can use a shared code name prefix to allow bulk include. For example: CMD
|
--exclude | No | Pattern to define the excluded objects. Use ‘;’ to separate multiple object names. Use the ‘*’ wildcard character to match all objects with the same prefix. Reusable field schemas don’t use a namespace prefix, but you can use a shared code name prefix to allow bulk exclude. For example: CMD
|
--location | No | The absolute or relative path of the target folder where the code files are generated. Path values support the following macros:
If the location is not provided, the code file target is the /{type}/{dataClassNamespace}/{name}/ folder hierarchy in the application directory. |
--namespace | No | The custom namespace added to the code of the generated classes. If not provided, a default system namespace is used based on the generated object type. The default values for the namespace of each type are the following:
When setting a custom namespace, you can use the following macros in the parameter value:
|
--with-provider-class | No Supported values: | Applies only when generating object type classes ( Defaults to False. You must explicitly call We don’t recommend generating dedicated provider classes per object type. Instead, use the generic CMD Example
|
The system generates the code files in the specified location and folder structure.
Discovery of generated classes in custom assemblies
When using the generated code files in a separate library or external application, you need to allow the system to detect the classes. Make sure class discovery is enabled for the project – see Integrate custom code.
Note: Don’t directly add generated code files into external projects that compile into a different output type than a DLL assembly (for example console applications). The system cannot discover the code in these cases. Instead, add the code into a Class Library project with the AssemblyDiscoverable
attribute and reference the assembly from your project.
Reusable field schema interfaces
Code files generated via ReusableContentTypes or PageContentTypes for content types that use at least one reusable field schema implement I<SchemaName>
interfaces. Each such interface corresponds to a schema assigned to the given content type. You must generate these interfaces separately using the ReusableFieldSchemas --type
parameter. Make sure to place them under the same namespace as the content type classes that use them. The generator outputs uncompilable code otherwise.
For example, assume an Article page content type that uses the PageMetadata and SEO reusable field schemas.
To generate the page content type, run:
dotnet run -- --kxp-codegen --type "PageContentTypes" --namespace "My.Project"
Which outputs:
namespace My.Project
{
public partial class Article : IPageMetadata, ISEO, IWebPageFieldsSource
{
// Properties that represent the 'PageMetadata' and 'SEO' reusable schema fields
// The SystemFields property
// Properties that represent 'Article' content type fields
}
}
To generate the corresponding reusable schema interfaces, run:
dotnet run -- --kxp-codegen --type "ReusableFieldSchemas" --namespace "My.Project"
Which outputs:
namespace My.Project
{
public interface IPageMetadata
{
public const string REUSABLE_FIELD_SCHEMA_NAME = "PageMetadata";
// Properties that represent 'PageMetadata' reusable schema fields
}
}
namespace My.Project
{
public interface ISEO
{
public const string REUSABLE_FIELD_SCHEMA_NAME = "SEO";
// Properties that represent 'SEO' reusable schema fields
}
}
Configure model mapping for properties storing content item references to reusable field schemas
Content types can store references to other content items via fields of the Pages and reusable content data type and the combined content selector component.
In content type model classes output by the code generator, these fields are represented as an IEnumerable
collection of model classes that correspond to the content type which the field references. For example, assume the following content types:
- Book – contains information about various books (ISIN, title, etc.).
- Author – contains information about authors (name, bio, etc.) and a Books field that references content items of the Book content type (the books a given author has written).
Then the Books field, a reference from the Author content type to the Book content type, is in the generated model class represented as a property of the IEnumerable<Book>
type.
If the target of the reference is not a specific content type but a reusable field schema that can be used by multiple content types, the property representing the reference is a collection of reusable field schema interface objects.
As an example, imagine a PageMetadata
reusable field schema that declares fields shared by all page content types in the system. References to this schema are in generated model classes defined using the IEnumerable<IPageMetadata>
type.
When mapping the results of content item queries to model classes that contain properties representing references to reusable field schemas, the system by default instantiates the referenced content items using the target reusable field schema interface type (IPageMetadata
, for example).
You can override this default behavior and directly set which model class the system should use via the MapContentTypeReferenceTo
and MapReferenceTo
property-level attributes.
// Maps content items of the 'Blog.BLOG_CLASS_NAME' content type from the collection
// of items referenced by the 'IPageMetadata' schema to the 'Blog' model class.
[MapContentTypeReferenceTo(Blog.BLOG_CLASS_NAME, typeof(Blog))]
// Maps content items of the 'Article.ARTICLE_CLASS_NAME' content type from the
// collection of items referenced by the 'IPageMetadata' schema to the 'Article' model class.
[MapContentTypeReferenceTo(Article.ARTICLE_CLASS_NAME, typeof(Article))]
// Map all other content items to the 'PageMetadata' model class
[MapReferenceTo(typeof(PageMetadata))]
// Property representing reusable field schema references
public IEnumerable<IPageMetadata> Pages { get; set; }
// A custom implementation of the 'IPageMetadata' interface containing
// a subset of the schema's properties.
public class PageMetadata : IPageMetadata
{
public string PageMetadataTitle { get; set; }
}
If neither of these attributes is used, the system behaves depending on the chosen model mapping approach:
- When mapping content query results using IContentQueryExecutor.GetMappedResult, the system defaults first to the type of the property representing the reusable schema reference (
IPageMetadata
in the example above) and finally to the type registered via the RegisterContentTypeMapping attribute. - When mapping content query results using IContentQueryResultMapper, the system defaults only to the type of the property representing the reusable schema reference.
RegisterContentTypeMapping attribute
All generated model classes come annotated with the RegisterContentTypeMapping
class-level attribute. The attribute controls which model class gets used when the system maps content item database data to models. The mapping functionality (via GetMappedResult and IContentQueryResultMapper) supports a single model class per content type.
// Directs the system to use the annotated model class when model binding
// content items of the 'DancingGoat.Coffee' content type
[RegisterContentTypeMapping("DancingGoat.Coffee")]
IContentItemFieldsSource and IWebPageFieldsSource interfaces
Classes generated for reusable content types and web pages respectively implement the IContentItemFieldsSource
and IWebPageFieldsSource
interfaces. These interfaces are useful when accessing system properties across multiple content types, or when defining extension methods across all generated classes.
// Instances of services obtained via dependency injection
private readonly IContentQueryExecutor contentQueryExecutor;
private readonly IContentQueryResultMapper contentQueryResultMapper;
public async Task<IEnumerable<IContentItemFieldsSource>> FetchMultipleContentTypes()
{
// Gets multiple content types in a single query
var builder = new ContentItemQueryBuilder()
.ForContentTypes(query =>
{
query.OfContentType(Coffee.CONTENT_TYPE_NAME, Event.CONTENT_TYPE_NAME)
});
// Uses 'IContentItemFieldsSource' as the common type
return await contentQueryExecutor.GetMappedResult<IContentItemFieldsSource>(builder);
}
public static class WebPageFieldsSourceExtensionMethods
{
public static object GetSystemProperties(this IWebPageFieldsSource source)
{
return new { Guid = source.SystemFields.ContentItemGUID,
Id = source.SystemFields.ContentItemID };
}
}
Customize generated classes
In most scenarios, we do not recommend directly modifying the generated classes.
The classes as generated as partial classes. This means that you can extend them in a separate code file. Using this approach, you avoid the need to merge custom changes made to the generated code every time the class is generated again after the object’s properties are changed.
Example
namespace DancingGoat
{
// Partial class generated by the code generator for content type 'DancingGoat.Store'
public partial class Store : IWebPageFieldsSource
{
...
/// <summary>
/// Store name.
/// </summary>
public string StoreName { get; set; }
/// <summary>
/// Store location.
/// </summary>
public string StoreLocation { get; set; }
...
}
}
namespace DancingGoat
{
// Partial class extending the generated 'Store' class
public partial class Store
{
public string StoreFullName { get => $"{StoreName}-{StoreLocation}" }
}
}