November Happy Hour will be moved to Thursday December 5th.

ContentResultService is executed instead of the injected one

Vote:
 

I'm strugling with super weird issue. I wanted to test an example from

 https://world.episerver.com/documentation/developer-guides/content-delivery-api/how-to-customize-data-returned-to-clients

Unfortuantely my CustomContentResultService is not executed. Base ContentResultService is executed all the time. I've tried to debug 

ContentApiResult.cs becasue ContentResultService is called from there. In debuger 

 private readonly ContentResultService _contentResultService;

seems to be an instance of my CustomContenrService which is expected. But for some reason it's not executed. I've asked my colleague to do the same with the same codebase and everything is working for him. How is this possible? Is there any cache or anything which might cause this issue? 

#200946
Jan 31, 2019 13:33
Vote:
 
 [ServiceConfiguration(typeof(ContentResultService))]

this does not guarantee that your implementation will be registered after the default implementation. To make sure yours will be registered and override the default one, register it in a IConfigurableModule.ConfigureContainer instead 

#200948
Jan 31, 2019 14:04
Vote:
 

would recommend also to reject all the other implementations (if that's the "singleton" case) and then register your implementation in the way Quan mentioned.

#200949
Jan 31, 2019 14:13
Vote:
 

Unless you have a good reason to eject all other implementations, I'd let them be. You never know if you might break a code somewhere that relies on a collection of ContentResultService

#200951
Jan 31, 2019 14:19
Vote:
 

I already tried that:

context.ConfigurationComplete += (o, e) =>
{
    context.Services.AddTransient<ContentResultService, CustomContentResultService>();
};

I also tried to intercept but result is the same.

When I'm debugging place where ContentResultService is used, it seems that my implementation is injected but the base one is used.

and when I step into

So as you can see from the call stack, my custom implementation is completely ignored. Again, my colleague executed the same code and everthing is fine. Super weird.

#200956
Edited, Jan 31, 2019 14:35
Vote:
 

You don't need 

context.ConfigurationComplete

Go straight to 

context.Services.AddSingleton<ContentResultService, CustomContentResultService>();
#200957
Jan 31, 2019 14:38
Vote:
 

Thx Quan, unfortunately nothing has changed. Still the base one is executed. 

#200958
Jan 31, 2019 14:43
Vote:
 

What do your IConfigurableModule and CustomContentResultService look like? 

#200961
Jan 31, 2019 15:10
Vote:
 

CustomContentResultService is taken from the documentation so,


CustomContentResultService.cs

[ServiceConfiguration(typeof(ContentResultService))]
    public class CustomContentResultService : ContentResultService
    {
        public CustomContentResultService(IContentApiSerializer contentApiSerializer) : base(contentApiSerializer)
        {

        }

        /// <summary>
        /// Build string content from object use given serializer
        /// (1) Only return needed fields to clients (2) Only applied for content api not search api
        /// </summary>
        public override StringContent BuildContent(object value)
        {
            var fields = System.Web.HttpContext.Current.Request.Params["fields"];
            if (string.IsNullOrEmpty(fields) || !(value is ContentApiModel))
            {
                return base.BuildContent(value);
            }

            var returnedProperties = fields.Split(',');
            var convertedObj = new ExpandoObject() as IDictionary<string, Object>;

            Func<string[], string, bool> shouldIncludeProperty = (propertyList, property) =>
            {
                return propertyList.Any(prop => string.Equals(prop, property, StringComparison.InvariantCultureIgnoreCase));
            };

            foreach (var prop in value.GetType().GetProperties())
            {
                var propertyType = prop.PropertyType;
                if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
                {
                    var propertyDataDict = prop.GetValue(value, null);
                    foreach (var item in propertyDataDict as IDictionary<string, object>)
                    {
                        if (!shouldIncludeProperty(returnedProperties, item.Key))
                        {
                            continue;
                        }
                        convertedObj.Add(item.Key, item.Value);
                    }

                    continue;
                }

                if (!shouldIncludeProperty(returnedProperties, prop.Name))
                {
                    continue;
                }

                var propValues = prop.GetValue(value, null);
                convertedObj.Add(prop.Name, propValues);
            }

            return base.BuildContent(convertedObj);
        }
    }

IConfigurableModule


    [InitializableModule]
    [ModuleDependency(typeof(ServiceContainerInitialization), typeof(ContentApiCmsInitialization))]
    public class ContentApiInitialization : IConfigurableModule
    {
        public void ConfigureContainer(ServiceConfigurationContext context)
        {
            // Register the extended content model mapper to be able to provide custom models from content api
            context.Services.Intercept<IContentModelMapper>((locator, defaultModelMapper) =>
                new ExtendedContentModelMapper(
                    locator.GetInstance<IUrlResolver>(),
                    defaultModelMapper,
                    locator.GetInstance<ServiceAccessor<HttpContextBase>>(),
                    locator.GetInstance<IContentVersionRepository>()
                )
            );

            // set minimumRoles to empty to allow anonymous calls (for visitors to view site in view mode)
            context.Services.Configure<ContentApiConfiguration>(config =>
            {
                config.Default().SetMinimumRoles(string.Empty);
            });

            context.Services.AddSingleton<ContentResultService, CustomContentResultService>();
        }

        public void Initialize(InitializationEngine context) {}


        public void Uninitialize(InitializationEngine context) { }
    }

#200966
Jan 31, 2019 15:34
Vote:
 

Given it looks like an environment specific issue you could try some simple things like:

  • Clearing your temp ASP.net files
  • Setting optimizeCompilations="false" in web.config

David

#201037
Feb 02, 2019 22:16
Vote:
 

what about is you remove `[ServiceConfiguration]` attribute? Also you can check what exactly you have registered in StructureMap container. Fastest way is either during breakpoint `container.WhatDoIHave()` or via EPiServer DeveloperTools (https://nuget.episerver.com/package/?id=EPiServer.DeveloperTools). Latter is on your own risk :) Recommend not to go in production with that tool installed (unless you really need it).

#201038
Feb 03, 2019 12:35
Vote:
 

but as David said - if your fellows can execute code successfully, this is something with your env. good timing to ask for new laptop :troll:

#201039
Feb 03, 2019 12:36
Vote:
 

@David I've already tried to remove project build files and \AppData\Local\Temp\Temporary ASP.NET Files, also tried to disable optimizeCompilations. Unfortunately result is the same :( 

@valdis removing [ServiceConfiguration] was also my first thought but it basically register my CustomService twice. 

From registered services, in structure map I don't see anything that might cause the issue.

I'm not 100% sure that it is my env issue but as my colleague was able to run it without any problems then it seems so. 

Unfortunately my laptop is quite new, in other case you are right, it would be a good opportunity to request a new one :)

Anyway, thx guys for your help

#201050
Edited, Feb 04, 2019 9:59
Vote:
 

I don't see your custom provider to be injected in ServiceAccessor<T> plugin as well. I had similar issue back in days when looking for answer to one of the forum questions. This might give you maybe some hints - https://blog.tech-fellow.net/2014/04/03/pragmatic-problem-solving-answer-to-the-episerver-forum-question/

#201051
Feb 04, 2019 10:06
Vote:
 

Thx I will take a look on that.

In case I would find the reason of my issue I will update the topic.

#201055
Feb 04, 2019 10:57
Dileep D - Mar 05, 2020 21:42
Were you able to find out the issue. I am setting up Content delivery API currently and also have same issue. The CustomContentDeliveryService is not triggered.
Pascal - May 18, 2021 7:17
Did you find a solution? Because I am strugling also with the executing the CustomContentDeliveryService. The main goal is to load all the data, also from the content areas throught a custom property model. Also want to reduce the json output by escluding the metadata fields of the CMS.

If there is any update on this, please let the community know.
Maciej Mickiewicz - May 18, 2021 7:43
We are not working on that project anymore and If I remember correctly we've abandoned this solution and took completely different approach.

@Pascal Loading all data from content areas shouldn't be a problem. Custom property model should do the work. We are doing this in the current project and it's working fine. For reducing json data you can have a look on ContentApiModelFilter, it can be used to modify the model before it's returned to the client
Pascal - May 18, 2021 8:04
@Maciej thank you for your reply. Is it possible to share some codesnippets? or do you have any referecing blogposts or aticles where I can find the code snippets? Thank you in advance.
Maciej Mickiewicz - May 18, 2021 8:17
Filtering: https://world.episerver.com/forum/developer-forum/episerver-content-delivery-api/thread-container/2019/7/ignore-base-contentapi-model-properties-from-json-response/

CustomContentAreaModel: https://world.episerver.com/documentation/developer-guides/content-delivery-api/getting-started/how-to-customize-data-returned-to-clients/

Be careful with adding "*" in content area property model as it will expand all items in your content area so it might happen you will get a loop reference. It strongly depends how your models look like but in our project we had this situation so we introduced our Custom ConverterContext where we have a property with current depth that we are expanding and if it's deeper then our max we are not expanding anymore.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.