Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Barry Fogarty 493 posts 1129 karma points
    Jul 15, 2020 @ 16:47
    Barry Fogarty
    0

    Boot fail when using an injected service (recursive dependency: IUmbracoContextFactory )

    First of all, this is great little package Lee. It's gonna be an essential bit of kit for v8 site builds.

    As the title says, I ran into a problem when trying to inject a simple service (which uses IUmbracoContextFactory to fetch some Umbraco content) into my custom DataList class (which implements IDataListSource):

    public class CountryDataList : IDataListSource
    {
        private readonly ISiteService _siteService;
    
        public CountryDataList(ISiteService siteService)
        {
            _siteService = siteService ?? throw new ArgumentNullException(nameof(siteService));
        }
    
        public IEnumerable<DataListItem> GetItems(Dictionary<string, object> config)
        {
            var items = new List<DataListItem>();
    
            foreach (var country in _siteService.GetCountries())
            {
                items.Add(new DataListItem
                {
                    Name = country.Name,
                    Value = country.IsoCode
                });
            }
    
            return items;
        }
    }
    

    The SiteService is identical in structure to the (excellent) Services Implementation documentation. It is registered with the DI container as per the docs too:

    public class SiteServiceComposer : IUserComposer
    {
        public void Compose(Composition composition)
        {
            // Reference: https://our.umbraco.com/documentation/Implementation/Services/
            composition.Register<ISiteService, SiteService>(Lifetime.Singleton);
        }
    }
    

    This works great when I consume it in a controller or view, but if I try and inject it into my Contentment DataListSource class, I get a Boot Fail: Recursive dependency detected: IUmbracoContextFactory. I believe the problem is to do with the service registration order, I tried the following, which looked like it might work:

    [ComposeAfter(typeof(Umbraco.Community.Contentment.Composing.ContentmentComposer))]
    

    But this is internal and the public types in Umbraco.Community.Contentment.Composing are invalid here because they do not implement Umbraco.Core.Composing.IComposer.

    Not sure if ContentmentComposer needs exposing within the package or if I'm completely barking up the wrong tree. Any thoughts much appreciated!

  • Barry Fogarty 493 posts 1129 karma points
    Jul 15, 2020 @ 17:05
    Barry Fogarty
    0

    Update: I forked the reop to open up ContentmentComposer so I could decorate my own service composer with

    [ComposeAfter(typeof(ContentmentComposer))]
    

    But also I still have the same error with a recursive dependency:

    System.InvalidOperationException: Recursive dependency detected: ServiceType:Umbraco.Web.IUmbracoContextFactory, ServiceName:] at LightInject.ServiceContainer.<>cDisplayClass153_0.0(IEmitter ms) in C:\projects\lightinject\src\LightInject\LightInject.cs:line 3849 at LightInject.ServiceContainer.EmitConstructorDependency(IEmitter emitter, Dependency dependency) in C:\projects\lightinject\src\LightInject\LightInject.cs:line 4158

  • Lee Kelleher 3995 posts 15625 karma points MVP 12x admin c-trib
    Jul 16, 2020 @ 09:39
    Lee Kelleher
    0

    Hi Barry,

    I'll take a look at it this morning, see what I can find.

    Just checking, so this is with the latest build from the 'develop' branch?

    Thanks,
    - Lee

  • Lee Kelleher 3995 posts 15625 karma points MVP 12x admin c-trib
    Jul 16, 2020 @ 09:54
    Lee Kelleher
    0

    Hi Barry,

    To clarify, when you inject the ISiteService into an IDataListSource it works fine? but IUmbracoContextFactory fails?

    If so, I can reproduce it. (Not sure what the resolution is yet, figuring it out - DI still feels like dark magic to me).

  • Barry Fogarty 493 posts 1129 karma points
    Jul 16, 2020 @ 10:09
    Barry Fogarty
    0

    Correct, the DI container complains about a recursive dependency for IUmbracoContextFactory. To clarify, this only happens when I inject the ISiteService into the IDataListSource class - if I exclude the Contentment stuff and inject it into a controller, (or get the instance from a View) it works fine.

  • Lee Kelleher 3995 posts 15625 karma points MVP 12x admin c-trib
    Jul 16, 2020 @ 10:13
    Lee Kelleher
    0

    Sorry Barry, so I get my reproduction test correct, where is the IUmbracoContextFactory being injected? into the ISiteService or IDataListSource directly?

  • Lee Kelleher 3995 posts 15625 karma points MVP 12x admin c-trib
    Jul 16, 2020 @ 09:57
    Lee Kelleher
    100

    Alternatively, you could try using IUmbracoContextAccessor instead? That has been working for me.

    (I'm tempted to think there's an issue with using IUmbracoContextFactory ... but probably needs someone who's more knowledgeable with Umbraco/DI to comment on that.)

  • Barry Fogarty 493 posts 1129 karma points
    Jul 16, 2020 @ 10:30
    Barry Fogarty
    0

    Good find Lee, thanks! Injecting IUmbracoContextAccessor into my service contructor accessing .UmbracoContext from there worked a charm. From the docs, it sounds like this would only work on a request thread, and I may need to catch UmbracoContext being null in other scenarios. Not sure, my service is a singleton so perhaps it is held with the single class instance when created. The service is for fetching some site-wide data so does not vary based on the context in any case. Be interesting to know if my implementation is wrong or if it's a problem upstream. Anyway - it works for my purposes, thank you for your help!

    H5YR

  • Lee Kelleher 3995 posts 15625 karma points MVP 12x admin c-trib
    Jul 16, 2020 @ 10:37
    Lee Kelleher
    0

    Cool, glad there's a workaround/solution.

    re: IUmbracoContextFactory, I've been trying to figure out what the issue might be - probably something to do with the other injected params on its constructor:

    https://github.com/umbraco/Umbraco-CMS/blob/release-8.6.1/src/Umbraco.Web/UmbracoContextFactory.cs#L38

    But that's where my DI knowledge is limited, no idea how to dig deeper. I'm facing the same concern, I wouldn't be surprised if my implementation was wrong, or needed tweaking in some way.

    We live and learn.

    Cheers,
    - Lee

  • Matt Brailsford 3689 posts 19400 karma points MVP 9x c-trib
    Jul 16, 2020 @ 14:08
    Matt Brailsford
    5

    What is the code for the SiteService instance?

    Another trick you can try if you have a circular dependency is to request a lazy version of the dependency so then it wouldn't evaluate immediately and instead would only evaluate when you try to access it

    public class SiteService : ISiteService
    {
        private Lazy<IUmbracoContextFactory> _contextFactory;
    
        public SiteService(Lazy<IUmbracoContextFactory> contextFactory)
        {
            _contextFactory = contextFactory;
        }
    
        public IEnumerable<Country> GetCountries()
        {
             using(var ctx = _contextFactory.Value.EnsureUmbracoContext()) 
             {
                 // Do your thing
             }
        }
    }
    
  • Barry Fogarty 493 posts 1129 karma points
    Jul 17, 2020 @ 10:02
    Barry Fogarty
    0

    Nice Matt! Thanks for the chipping in, this works a treat and feels a bit more elegant than @Lee's suggestion. This should make it into the documentation I think.

    Update: Made a PR for it :-)

  • Shannon Deminick 1523 posts 5250 karma points MVP
    Jul 20, 2020 @ 11:57
    Shannon Deminick
    1

    Normally those errors are just due to circular dependencies one way or another, since LazyT solves it, that certainly sounds like a circular dependency issue :) many times in our code circular dependencies are caused by PropertyEditorCollection because property editors do far too much and when you resolve this collection you also create all of it's instances. If it's not this collection, than it could be another collection that is creating instances that result in circular references.

Please Sign in or register to post replies

Write your reply to:

Draft