How to register your own dependencies
# help-with-umbraco
c
Can someone please tell me how to register a class so it can be injected because these docs aren't working for me at all. I think they are written for experts rather than people that want to learn, but I may be wrong: https://docs.umbraco.com/umbraco-cms/reference/using-ioc I have a static helper class I wish to inject into a surface controller to use it's methods but I think I need to register it first. So trying to work out from the docs how to do that and failing miserably. I'm happy with the concept of DI and can do it all day long with Umbraco services, but registering my own is the issue here. Cobbling instructions together from the Vendr DI page I tried adding:-
services.AddTransient<Helpers>();
to ConfigureServices in startup.cs but that just gives me:
Copy code
Unhandled exception. System.AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Web.Core.Services.Helpers Lifetime: Transient ImplementationType: Web.Core.Services.Helpers': Unable to resolve service for type 'Umbraco.Cms.Core.Models.PublishedContent.IPublishedContent' while attempting to activate 'Web.Core.Services.Helpers'.)
So I have: Namespace: myProj.Core.Services Class: Helpers (with various methods) Then what? Thanks.
m
How's your helper class looking in terms of constructors?
c
Copy code
public class Helpers {
    private readonly IPublishedContent _publishedContent;
    private readonly IPublishedContentQuery _contentQuery;

    public Helpers(IPublishedContent publishedContent, IPublishedContentQuery contentQuery) {
        _publishedContent = publishedContent;
        _contentQuery = contentQuery;
    } 
    
    public IPublishedContent? GetMemorialListNode() {
            
        IPublishedContent? siteRoot = _contentQuery.ContentAtRoot().FirstOrDefault();
        var memorialListNode = siteRoot?.FirstChild<MemorialListPage>();
            
        return memorialListNode;
    }
    
}
This is the first of what I expect to be half a dozen helper methods. It's just trying to find the listing container so I can add content nodes underneath it.
d
Remove the
IPublishedContent
from the constructor, as it can't be injected.
d
The reason behind Dan's answer being: a dependency injection container can only produce services of types that are explicitly registered during startup. A class that you register may only take a dependency on another type if that other type is registered in the container as well.
IPublishedContent
is not registered in the container: it is produced by methods on the Umbraco context, therefore you cannot take dependency on
IPublishedContent
.
c
Begs the question how do I get the site root? I'll need an Umbraco context I guess.
r
You can inject an
IUmbracoContextAccessor
to get access to that
You'll need an if statement to get access to the context itself, e.g.
Copy code
csharp
if (umbracoContextAccessor.TryGetUmbracoContext(out var umbracoContext))
{
    [... code here]
}
c
Thanks for that. Using the docs (https://docs.umbraco.com/umbraco-cms/reference/querying/umbraco-context) I now have this, but no idea what Problem is meant to be so it's a bit inelegant around there, but it works.
Copy code
public class Helpers {
    
    private readonly IUmbracoContextAccessor _umbracoContextAccessor;

    public Helpers(IUmbracoContextAccessor umbracoContextAccessor)
    {
        _umbracoContextAccessor = umbracoContextAccessor;
    }  
    
    public IPublishedContent GetMemorialListNode() {
        
        if (_umbracoContextAccessor.TryGetUmbracoContext(out IUmbracoContext? context) == false) {
            return null; //this.Problem("unable to get content");
        }

        if (context.Content == null) {
            return null; //this.Problem("Content Cache is null");
        } 
        
        IPublishedContent? siteRoot = context.Content.GetAtRoot().FirstOrDefault();
        var memorialListNode = siteRoot?.FirstChild<MemorialListPage>();
            
        return memorialListNode;
    }
}
r
Awesome :)
When it comes to IoC, I find the thing to remember is when you're injecting things they're going to usually stay the same through the whole process, so you don't want to inject things that are going to change at some point - pass in stuff like IPublishedContent and queries and the like as method parameters, and inject services and helper classes like this
c
I should also mention the actual registration has been done like this:-
Copy code
public void ConfigureServices(IServiceCollection services)
        {
            services.AddUmbraco(_env, _config)
                .AddBackOffice()
                .AddWebsite()
                .AddSlimsy()
                .AddComposers()
                .Build();

            services.AddTransient<Helpers>();
        }
Do you mean you shouldn't inject services and helper classes or you should?
r
You should inject them, sorry
d
@Craig100 A good way of getting around the "inelegance" of querying for known content in your services is to have something like the Site Service mentioned in the Umbraco IoC docs - https://docs.umbraco.com/umbraco-cms/implementation/services#custom-services-and-helpers So you let this get your "root" content nodes (usually for known key pages, such as your home page or blog root etc) and then you inject your Site Service in your own services to access those nodes. This keeps them "cleaner" and neater. And, as Rachel says, for stuff that changes (such as the current page etc) then pass that in as a parameter to methods within your service. So if you need to reference the the current page, pass it in.
3 Views