Working with strongly typed models (aka Code First approach)

Jan Cerman edited this page Dec 5, 2018 · 12 revisions

Contents

  1. Contents
  2. Strongly-typed models
  3. Defining a model
    1. Typing the properties
      1. Typing simple elements
      2. Typing Linked Items
      3. Typing Rich text
    2. Naming the properties
    3. Examples
  4. Generating models
  5. Retrieving content items
    1. Casting to strong types
  6. Customizing the strong-type binding logic
    1. Adding support for runtime type resolution
      1. Generating custom type providers
    2. Customizing the property matching
    3. Customizing the strong-type resolution mechanism

Strongly-typed models

Besides the basic methods for retrieving content items, the IDeliveryClient interface supports fetching of strongly-typed models.

// Initializes a client
IDeliveryClient deliveryClient = DeliveryClientBuilder
    .WithProjectId("975bf280-fd91-488c-994c-2f04416e5ee3")
    .Build();

// Basic retrieval
deliveryClient.GetItemAsync("article_about_coffee");

// Strongly-typed model retrieval
deliveryClient.GetItemAsync<Article>("article_about_coffee");

This approach is beneficial for its:

  • type safety during compile-time
  • convenience of usage by a developer (@Article.ArticleTitle vs. @Article.GetString("article_title"))
  • support of type-dependent functionalities (such as display templates in MVC)

Defining a model

The models are simple POCO classes, which means they don't have any attached behavior or dependency on an external framework.

Typing the properties

Typing simple elements

Here are the data types you can use for different content type elements:

  • built-in types such as string, decimal and their nullable equivalents for simple elements like Number or Text.
  • IEnumerable<KenticoCloud.Delivery.MultipleChoiceOption> for Multiple choice elements.
  • IEnumerable<KenticoCloud.Delivery.Asset> for Asset elements.
  • IEnumerable<KenticoCloud.Delivery.TaxonomyTerm> for Taxonomy elements.

Typing Linked Items

For linked items elements, use either IEnumerable<T> or any concrete implementation of ICollection<T> such as List<T> or HashSet<T>.

Depending on your scenario, use one of the following as the data type parameter:

  • Specific content type model (e.g., Article) - when the element contains content items based on a single content type.
  • ContentItem - when the element can contain mixed content types and you don't need type safety.
  • object - when the element contains mixed content types and you want the objects to be strongly-typed. See Adding support for runtime type resolution for more details.

Typing Rich text

For Rich text elements, use either string to receive HTML code resolved using string-based resolver as outlined in Rendering linked items in Rich text or IRichTextContent to retrieve rich text content as structured data.

Naming the properties

By default, the model properties and content elements are matched by codenames of the elements. The SDK tries to convert the element codenames to PascalCase. For example, a content element with the codename of article_title translates to a property called ArticleTitle.

If you need to change the codename of an element that a property is bound to, you can enrich the property with the Newtonsoft.Json.JsonPropertyAttribute attribute.

[JsonProperty("text_field")]
public string ArticleTitle { get; set; }

Examples

You can find sample models at https://github.com/Kentico/delivery-sdk-net/tree/master/KenticoCloud.Delivery.Tests/Models

Generating models

:information_source: You can save time by generating content type models for your project using the Kentico Cloud .NET code generator utility.

Retrieving content items

Both the GetItemAsync and GetItemsAsync client methods have their corresponding generic overloads, GetItemAsync<T> and GetItemsAsync<T>. The parameters are the same as for the non-generic variants. The only difference is that you need to specify the type parameter <T>.

You can either specify the type directly (e.g., GetItemAsync<Article>) or pass the type as the object class (e.g., GetItemAsync<object>). Use the second approach if you don't know what the type is to let the SDK resolve it during runtime.

This parameter represents the model you want to load. You can specify the parameter in two ways:

  • by using a content type model, for example GetItemAsync<Article>
  • by passing the object class, for example, GetItemAsync<object>

Use the second approach if you don't know what the content type will be and you want the application to resolve content types during runtime.

Casting to strong types

Note that it's possible to cast ContentItem and DeliveryItemResponse to strongly-typed equivalents by calling .CastTo<T>().

Customizing the strong-type binding logic

Adding support for runtime type resolution

The IDeliveryClient instance supports runtime type resolution. This means you can pass the object class instead of explicitly specifying the data type in the model or when calling the GetItemAsync<> method. The data type will be resolved dynamically during runtime.

For example:

object model = await client.GetItemAsync<object>(...);
Type type = model.Item.GetType(); // type will be e.g. 'Article'

For this to work, the SDK needs to know the mappings between the content types and your models.

If you want to use the runtime type resolution in your application, you need to implement the KenticoCloud.Delivery.ICodeFirstTypeProvider interface.

public class CustomTypeProvider : ICodeFirstTypeProvider
{
  public Type GetType(string contentType)
  {
    switch(contentType)
    {
      case "article":
        return typeof(Article);
      case "office":
        return typeof(Office);

      ...

      default:
        return null;
    }
  }
}

Next, you either register the type provider within IServiceCollection

services.AddSingleton<ICodeFirstTypeProvider, CustomTypeProvider>();
services.AddDeliveryClient(Configuration);

or pass it to the DeliveryClientBuilder.

CustomTypeProvider customTypeProvider = new CustomTypeProvider();
IDeliveryClient client = DeliveryClientBuilder
    .WithProjectId("975bf280-fd91-488c-994c-2f04416e5ee3")
    .WithCodeFirstTypeProvider(customTypeProvider)
    .Build();

Generating custom type providers

Similarly to models, you can generate implementations of ICodeFirstTypeProvider by using the .NET code generator tool and specifying the --withtypeprovider parameter.

Customizing the property matching

If you want to customize the way content elements and model properties are matched, you need to implement the KenticoCloud.Delivery.ICodeFirstPropertyMapper interface and pass it to the DeliveryClientBuilder class.

// Implements a custom property mapper
public class CustomPropertyMapper : ICodeFirstPropertyMapper
{
  public bool IsMatch(PropertyInfo modelProperty, string fieldName, string contentType)
  {
    // Add your logic here
    return modelProperty.Name.ToLower() == fieldName;
  }
}

// Registers the custom property mapper to the IServiceCollection or other framework you are using for dependency injection
services.AddSingleton<ICodeFirstPropertyMapper, CustomPropertyMapper>();
services.AddDeliveryClient(Configuration);

// Registers the custom property mapper within a delivery client using builder
CustomPropertyMapper customPropertyMapper = new CustomPropertyMapper();
IDeliveryClient client = DeliveryClientBuilder
    .WithProjectId("975bf280-fd91-488c-994c-2f04416e5ee3")
    .WithCodeFirstPropertyMapper(customPropertyMapper)
    .Build();

Customizing the strong-type resolution mechanism

If you wish to replace the whole mechanism, you will need to implement the KenticoCloud.Delivery.ICodeFirstModelProvider interface. Compared to the ICodeFirstTypeProvider and ICodeFirstPropertyMapper interfaces, it contains one extra member:

T GetContentItemModel<T>(JToken item, JToken linkedItems);

Your task is to create an instance of <T> by utilizing the data in the item and linkedItems properties.

Analytics

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.