Documentation

Ucommerce includes full API reference documentation and lots of helpful articles to help you build your e-commerce site as effortlessly as possible.

Topics Payment Providers
v7.18

Constructor and Property Injection

Following best practice in the platform, if you are writing custom code and components each component will only have a single responsibility, so there's a good chance you need to compose components together by injecting business logic into your classes.

This could for example be that your components needs tax, in which you have to inject the right component for that, rather than writing the logic to do it yourself.

To do so, all you have to do is adding a Public property and mark it with [Mandatory] or inject it into your components constructor. Let's take an example of both ways.

Imagine you are writing a pipeline task that needs to look up members in the shop and then do something with that information. The actual logic is not attached as it is not important in this case.

So you start off by implementing the interface, and you now have an empty pipeline task that right now does nothing.

    public class MemberPipelineTask : IPipelineTask<Ucommerce.EntitiesV2.PurchaseOrder>
    {
        public MemberPipelineTask()
        {
            
        }
        public PipelineExecutionResult Execute(PurchaseOrder subject)
        {
            throw new System.NotImplementedException();
        }
    }
    

Since we want to inject memberservice logic into our components we have to use either the constructor

    public class MemberPipelineTask : IPipelineTask<Ucommerce.EntitiesV2.PurchaseOrder>
    {
        private readonly IMemberService _memberService;
    
        public MemberPipelineTask(Ucommerce.Security.IMemberService memberService)
        {
            _memberService = memberService;
        }
        public PipelineExecutionResult Execute(PurchaseOrder subject)
        {
            Member currentMember = _memberService.GetCurrentMember();
    
            //Implementation left out :-)
            
            return PipelineExecutionResult.Success;
        }
    }
    

or use public properties tagged with the Mandatory Attribute

    public class MemberPipelineTask : IPipelineTask<Ucommerce.EntitiesV2.PurchaseOrder>
    {
        [Ucommerce.Infrastructure.Components.Windsor.Mandatory]
        public Ucommerce.Security.IMemberService MemberService { get; set; }
        
        public PipelineExecutionResult Execute(PurchaseOrder subject)
        {
            Member currentMember = MemberService.GetCurrentMember();
    
            //Implementation left out :-)
            
            return PipelineExecutionResult.Success;
        }
    }
    

All this works as long as the IOC container can match up an existing component in the container that matches what you are injecting. If you have your own service that you want to inject, you have to register that as well. Otherwise you will get the following exception:

No component for supporting the service type you injected was found.

The way the container matches up what to inject is via the type injected and matching it up with the service attribute of your component. Typically the service attribute is an interface based on how the platform was designed, but it might as well be an abstract class, or just a class.

So in the example above we are injecting Ucommerce.Security.IMemberService memberService

The IOC container will now pick the first component where the service attribute matches Ucommerce.Security.IMemberService memberService. Out of the box the following component will be matched:

    
    
        <component id="MemberService"
                service="Ucommerce.Security.IMemberService, Ucommerce"
                type="Ucommerce.Security.MemberService, Ucommerce" />
    
    

If no components are matched by the service attribute you will end up in an exception saying no components was found.

Specific injection with parameters

Typically it's good enough to just have the IOC container resolve your components. But in case you need to explicitly set which component to use (or inject simple values as show later in the article) you can specifically tell windsor which component to use.

This is done with the parameters element on your components. This is typically used if you have multiple of the same service registered like multiple pipeline tasks or similar.

To do this, simply set the parameters element and match each property or constructor argument by name. Taken the example above i want to control which of the member services to use.

Let's imagine this simple example where there are multiple of the same memberservice. If we don't do anything like in the example below, the IOC will always pick MemberService over AnotherMemberService. So to force the IOC to pick the specific one, we can use the properties element and specify the id explicitly.

    
    
    <component id="MemberService"
            service="Ucommerce.Security.IMemberService, Ucommerce"
            type="Ucommerce.Umbraco8.Security.MemberService, Ucommerce.Umbraco8" />
            
    <component id="AnotherMemberService"
            service="Ucommerce.Security.IMemberService, Ucommerce"
            type="myextensions.MemberService, myextensions" />
    
    <component
    	id="Basket.MemberPipelineTask"
    	service="Ucommerce.Pipelines.IPipelineTask`1[[Ucommerce.EntitiesV2.PurchaseOrder, Ucommerce]], Ucommerce"
    	type="UcommerceCodeSamples.ExtendingUcommerce.MemberPipelineTask, UcommerceCodeSamples">
        <parameters>
            <productThreshold>${AnotherMemberService}</productThreshold>
        </parameters>
    </component>
    
    

Injection of simple values

Not only is it possible to inject other compoents, you may also inject simple values in case you need a little more flexibility in your components. It could be APIkeys, connection strings, simple numbers for something. Simple types include:

  • strings
  • ints
  • decimals
  • booleans

Take a look at the example below where we in our pipelinetask now looks for a threshold of products on the order. But we want the threshold to be configurable, so we inject an int into our constructor (could also be property injected of course)

    public class MemberPipelineTask : IPipelineTask<Ucommerce.EntitiesV2.PurchaseOrder>
    {
        private readonly IMemberService _memberService;
        private readonly int _productThreshold;
    
        public MemberPipelineTask(Ucommerce.Security.IMemberService memberService, int productThreshold)
        {
            _memberService = memberService;
            _productThreshold = productThreshold;
        }
        public PipelineExecutionResult Execute(PurchaseOrder subject)
        {
            if (subject.OrderLines.Sum(x => x.Quantity) >= _productThreshold)
            {
                Member currentMember = _memberService.GetCurrentMember();
    
                //Implementation left out :-)
            }
            
            return PipelineExecutionResult.Success;
        }
    }
    

If you don't specifically inject the value using the properties element, you will end up in a similar exception as before, as simple values are not registered in the container. You must explicitly inject them into your components, like below:

    
    
    <component
    	id="Basket.MemberPipelineTask"
    	service="Ucommerce.Pipelines.IPipelineTask`1[[Ucommerce.EntitiesV2.PurchaseOrder, Ucommerce]], Ucommerce"
    	type="UcommerceCodeSamples.ExtendingUcommerce.MemberPipelineTask, UcommerceCodeSamples">
        <parameters>
            <productThreshold>42</productThreshold>
        </parameters>
    </component>