0

I am having issue with kentico kontent a bit. Basically when I call _deliveryClient.GetItemsAsync<object> I am getting null even though the json below is being brought back.

{
  "item": {
    "system": {
      "id": "0b9e6cf0-a9aa-422b-9e14-1576adfb6324",
      "name": "Home",
      "codename": "home",
      "language": "default",
      "type": "home",
      "sitemap_locations": [],
      "last_modified": "2020-04-30T17:16:48.706142Z"
    },
    "elements": {
      "header": {
        "type": "text",
        "name": "Header",
        "value": "This is my name"
      },
      "description": {
        "type": "text",
        "name": "Description",
        "value": ".net specialist"
      },
      "background": {
        "type": "modular_content",
        "name": "Background",
        "value": [
          "universe"
        ]
      }
    }
  },
  "modular_content": {
    "universe": {
      "system": {
        "id": "a8898eef-0f4b-4646-af72-c0a1e41ab165",
        "name": "Universe",
        "codename": "universe",
        "language": "default",
        "type": "background",
        "sitemap_locations": [],
        "last_modified": "2020-04-30T17:19:02.9586245Z"
      },
      "elements": {
        "user_vid_or_imag": {
          "type": "multiple_choice",
          "name": "User Vid or Imag",
          "value": [
            {
              "name": "Video",
              "codename": "video"
            }
          ]
        },
        "background_item": {
          "type": "asset",
          "name": "Background Item",
          "value": [
            {
              "name": "Time Lapse Video Of Night Sky.mp4",
              "description": null,
              "type": "video/mp4",
              "size": 2076845,
              "url": "https://preview-assets-us-01.kc-usercontent.com:443/..."
            }
          ]
        }
      }
    }
  }
}

However, if I use the concretion I get the Model back as expected. This is an issue even for members of a class e.g. linked items. The problem is that we have lots of models so we have opted to use the ModelGenerator kentico provides. The thing is we cannot tell the the Generator not to generate some of the objects so it will overwrite everything even though we only want to update one model. So this means I cannot go into every model and change the to some concretion because that will be overwritten.

The documentation says that should always work so is this a bug? or am I making a mistake somewhere.

Taf Munyurwa
  • 1,444
  • 2
  • 16
  • 22

1 Answers1

2

You should always use the model generator in combination with partial classes. To enable customization via partial classes use the --generatepartials true switch. This will output something like:

  • Article.Generated.cs (will be always regenerated)
    public partial class Article
    {
        public const string Codename = "article";
        public const string TitleCodename = "title";
        public const string BodyCopyCodename = "body_copy";
        public const string RelatedArticlesCodename = "related_articles";

        public string Title { get; set; }
        public IRichTextContent BodyCopy { get; set; }
        public IEnumerable<object> RelatedArticles { get; set; }
    }

  • Article.cs (will be generated when it doesn't already exist, it will never be overwritten by the generator)
    public partial class Article
    {
        // Space for your customizations
        public IEnumerable<Article> ArticlesTyped => RelatedArticles.Cast<Article>();
    }

Feel free to suggest improvements for the code generator at https://github.com/Kentico/kontent-generators-net/issues/new/choose

The most probable reason why your code doesn't work at the moment is that you haven't registered the implementation of the ITypeProvider interface. You can do so by adding it to the DI container (IServiceCollection):

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

or by passing it to the DeliveryClientBuilder

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

as described in the docs. You can either generate the CustomTypeProvider by the model generator utility or implement it by hand. It should look similar to this:

public class CustomTypeProvider : ITypeProvider
    {
        private static readonly Dictionary<Type, string> _codenames = new Dictionary<Type, string>
        {
            // <CLI type, model codename in Kontent>
            {typeof(Article), "article"}
        };

        public Type GetType(string contentType)
        {
            return _codenames.Keys.FirstOrDefault(type => GetCodename(type).Equals(contentType));
        }

        public string GetCodename(Type contentType)
        {
            return _codenames.TryGetValue(contentType, out var codename) ? codename : null;
        }
    }

Alternatively, you can specify the type when calling GetItemsAsync() in the following way: _deliveryClient.GetItemsAsync<Article>() but this will resolve only the 1st level type. Any nested models will be null because the SDK won't know how to resolve them (that's what the ITypeProvider is for). So I'd avoid this.

rocky
  • 7,506
  • 3
  • 33
  • 48
  • Do I still need the ITypeProvider if I am using JsonProperty("kontent_field"). Shouldn't the system be able to resolve this? – Taf Munyurwa May 04 '20 at 09:20
  • Yes, the `ITypeProvider` tells the system which CLI type should be used for which Kentico Kontent Content Type. `JsonPropertyAttribute` just determines the name of the element in KK that a property is bound to. There is no way the system could infer the CLI types, you have to give it a hint - either using the `ITypeProvider` or via the `` type parameter. Go with the `ITypeProvider`. – rocky May 04 '20 at 10:05
  • Ok, So this what I am confused about. Sorry for being tenacious :(. Just need to understand. So Say I have this Model class Article{ [JsonProperty("content_authors")] public IEnumarable Authors {get; set;} // is linked item in content } Here I would expect the system to simply know that the type I want to use is Authors. Essentially at this point I am doing exactly what the provider is doing no? Mapping a CLR type(Author) to a kontent codename (content_authors). Is this logical? Can it be an enhancement if this is not supported? – Taf Munyurwa May 04 '20 at 11:24
  • Oh, now I get your point. Yes, it should work this way already. If you look at the [`ModelProvider`](https://github.com/Kentico/kontent-delivery-sdk-net/blob/master/Kentico.Kontent.Delivery/StronglyTyping/ModelProvider.cs#L268) you'll see that it always try to infer the type from the generic type `` and if not successful, it uses the [`ITypeProvider`](https://github.com/Kentico/kontent-delivery-sdk-net/blob/d195db9440751890a0b44a95afbca1a8e64c0737/Kentico.Kontent.Delivery/StronglyTyping/ModelProvider.cs#L126). – rocky May 04 '20 at 20:45
  • Unfortunately, the model generator is not able to generate better types for linked items elements (https://github.com/Kentico/kontent-generators-net/issues/90) because the Delivery API doesn't contain the information about the element's constraints yet. This problem will probably be addressed in the next version of the Delivery API. Since the linked item collection can contain 0-N items of various types, the information about the constraints is essential for proper model generation (e.g. `IEnumerable
    ` or even just `Article` for elements with only one item allowed.
    – rocky May 04 '20 at 20:51
  • The downside of relying solely on the `` (in the models or when calling GetItemsAsync) is that your models won't be resolved if they're part of rich-text elements (https://docs.kontent.ai/tutorials/write-and-collaborate/structure-your-content/structure-content-with-components#a-using-content-items-in-the-rich-text-element). So, as I stated before - I'd go all-in with the code generator or at least provide an implementation of the `ITypeProvider` to the `DeliveryClient` to make sure nested items in rich-text get resolved properly. – rocky May 04 '20 at 20:54
  • 1
    Ok. That makes sense. Thank you so much for the help. I have just seen the ModelProvider code. Makes complete sense now – Taf Munyurwa May 05 '20 at 08:01