Copied to clipboard

Flag this post as spam?

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


  • Bo Jacobsen 608 posts 2406 karma points
    Jan 10, 2020 @ 23:49
    Bo Jacobsen
    0

    Dependency Injection on Activator Createinstance Reflection

    Hi all.

    I am trying to reflect all classes of a specific interface and run a method. This works until i try to use services into the class constructor, like the IScopeProvider.

    So i need to be able to get my Activator.CreateInstance(type) to pick up all paramaters in the constructor with Dependency Injection.

    Does anyone know what to do?

    This is what i have:

    My interface to find

    public interface IFindMe
    {
        string Name { get; }
        void PerformRun();
    }
    

    Class to reflect

    public class TestJob : IFindMe
    {
        private readonly IScopeProvider _scopeProvider;
    
        public TestJob(IScopeProvider scopeProvider)
        {
            _scopeProvider = scopeProvider;
        }
    
        public string Name => "Find me ffs";
    
        public void PerformRunAsync()
        {
            using (var scope = _scopeProvider.CreateScope(autoComplete: true))
            {
                // Just to try something
                var instanceId = scope.InstanceId;
            }
        }
    }
    

    IComponent, where all the interfaces are picked up

    public class FindMeComponent : IComponent
    {
        private readonly ILogger _logger;
    
        public FindMeComponent(ILogger logger)
        {
            // When it works i wanna log things :)
            _logger = logger;
        }
    
        public void Initialize()
        {
            var listOfTypes = new List<Type>();
    
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach (var assembly in assemblies)
            {
                try
                {
                    var foundTypes = assembly.GetTypes();
                    listOfTypes.AddRange(foundTypes);
                }
                catch (ReflectionTypeLoadException e)
                {
                    var exceptionTypes = e.Types.Where(t => t != null);
                    listOfTypes.AddRange(exceptionTypes);
                }
            }
    
            var type = typeof(IFindMe);
            var types = listOfTypes.Where(t => type.IsAssignableFrom(t) && !t.IsInterface);
            foreach (var type in types)
            {
                // This works until i use a class that has parameters in the contructor.
                object calcInstance = Activator.CreateInstance(type);
                var name = (string)type.GetProperty("Name").GetValue(calcInstance, null);
                var methodInfo = _type.GetMethod("PerformRun");
                methodInfo.Invoke(_calcInstance, null);
            }
        }
    
        public void Terminate()
        {
    
        }
    }
    

    IUserComposer, where i register the FindMeComponent

    public class Installer : IUserComposer
    {
        public void Compose(Composition composition)
        {
            composition.Components().Append<FindMeComponent>();
        }
    }
    
  • Bo Jacobsen 608 posts 2406 karma points
    Jan 10, 2020 @ 23:50
    Bo Jacobsen
    0

    I dunno why it is always putting it under Using Umbraco And Getting Started

  • jake williamson 207 posts 873 karma points
    Feb 25, 2022 @ 01:38
    jake williamson
    0

    I dunno why it is always putting it under Using Umbraco And Getting Started

    this has been driving me nuts for months now! makes finding anything a real challenge...

  • Bo Jacobsen 608 posts 2406 karma points
    Jan 11, 2020 @ 20:25
    Bo Jacobsen
    100

    I got it to work. I dunno if its the best solution, but its the only one i can come around.

    So basically you have to make sure there is only one constructor. And that constructor either have to be parameterless or only contain of Dependency Injection services. And from what i could search me to, its called Bastard Injection DI anti-pattern.

    Then you need to find out if the constructor have parameters or not. If the constructor have parameters, you find each parameter and get the instance of its type Umbraco.Web.Composing.Current.Factory.GetInstance and then you pass the instances as an object array into the Activator.CreateInstance

    public void Initialize()
    {
        var listOfTypes = new List<Type>();
    
        var assemblies = AppDomain.CurrentDomain.GetAssemblies();
        foreach (var assembly in assemblies)
        {
            try
            {
                var foundTypes = assembly.GetTypes();
                listOfTypes.AddRange(foundTypes);
            }
            catch (ReflectionTypeLoadException e)
            {
                var exceptionTypes = e.Types.Where(t => t != null);
                listOfTypes.AddRange(exceptionTypes);
            }
        }
    
        var typeOfInterface = typeof(IFindMe);
        var types = listOfTypes.Where(t => typeOfInterface.IsAssignableFrom(t) && !t.IsInterface);
        foreach (var type in types)
        {
            object calcInstance = null;
    
            var constructors = type.GetConstructors();
            var firstConstrutor = constructors.FirstOrDefault(); // Bastard Injection DI anti-pattern
            if (firstConstrutor != null)
            {
                var constructorParameters = firstConstrutor.GetParameters();
                if (constructorParameters != null && constructorParameters.Any())
                {
                    var objectList = new List<object>();
    
                    foreach (var constructorParameter in constructorParameters)
                    {
                        var cpType = constructorParameter.ParameterType;
                        var instance = Umbraco.Web.Composing.Current.Factory.GetInstance(cpType);
                        objectList.Add(instance);
                    }
    
                    calcInstance = Activator.CreateInstance(type, objectList.ToArray());
                }
            }
    
            if (calcInstance == null)
            {
                calcInstance = Activator.CreateInstance(type);
            }
    
            var name = (string)type.GetProperty("Name").GetValue(calcInstance, null);
            var methodInfo = type.GetMethod("PerformRun");
            methodInfo.Invoke(calcInstance, null);
        }
    }
    
  • Lee 1130 posts 3088 karma points
    Nov 22, 2021 @ 17:30
    Lee
    2

    Old post, but thought I would update this for Umbraco 9 in case anyone else wants to do this. You can use the following instead of Activator.CreateInstance()

    ActivatorUtilities.CreateInstance(serviceProvider, myType)
    

    The service provider can be obtained via Dependency Injection in the constructor of your class. i.e.

    public class MyClassHere
    {
        private readonly IServiceProvider _serviceProvider;
        public MyClassHere(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }        
    }
    
  • jake williamson 207 posts 873 karma points
    Feb 25, 2022 @ 01:41
    jake williamson
    0

    lee, can't thank you enough for posting this!

    i have a service class i've been using in virtually every umbraco 8 project i've worked on in the last 3 years...

    one of the methods looked like this:

    public T As<T>(IPublishedContent content)
    {
        if (typeof(T) == typeof(IPublishedContent) || content == null)
        {
            return (T)content;
        }
    
        return (T)Activator.CreateInstance(typeof(T), content);
    }
    

    which quickly blew up when i dropped it into the umbraco 9 project i've been working on!

    but you're post helped me convert it to this:

    public T As<T>(IPublishedContent content)
    {
        if (typeof(T) == typeof(IPublishedContent) || content == null)
        {
            return (T)content;
        }
    
        return (T)ActivatorUtilities.CreateInstance(_serviceProvider, typeof(T), content);
    }
    

    and i'm back in business!

Please Sign in or register to post replies

Write your reply to:

Draft