How can I create a class instance from inside my method?

Dec 30, 2011 at 9:01 AM

I have registered my classes like this:

        private static IUnityContainer BuildUnityContainer()
        {
            container.RegisterType<   IService<Product>, ProductService   >("Product");

        }

Normally I use constructor injection but I have one case where I would like to create an instance of a class for use inside a method. In that method I need to do something like the following:

        public void Update(string serviceClassName) {

             var serviceClass = new container.Resolve<IService<Product>>();


I need to create a service class for a service of type IService<Product> if the serviceClassName passed in is "Product".
I need to create a service class for a service of type IService<Package> if the serviceClassName passed in is "Package"

I keep reading how I should be using Constructor Injection but for this case I am not sure how I can use it. All I need is a temporary copy of the service instance just for the lifetime of when my method executes.

Can someone help me out and tell me how I can do this.  In the above I have two problems. Firstly I don't know how to pass the container to the child and second I want to get a IService< passedParameter >  based on the parameter passed to the method.

Hope I can get some good advice.

Thanks,

 

Dec 31, 2011 at 3:18 AM

I came up with the following solution to resolve inside of a method when the class name is given to me by a string parameter en. It does not look to be a very good way to resolve but it works. Note that this method gets the assemblyQualifiedName of a known class Product and the replaces the word Product with the name of class passed in as a parameter. 

Is there anyone out there who could verify if this is a good way to do this:

        public void Update(string en, string pk, string rk, string fld, string val) {

            string assemblyQualifiedName = typeof(Storage.Models.Product)
                .AssemblyQualifiedName
                .Replace("Product", en);
            Type entityType = Type.GetType(assemblyQualifiedName, false, true);
            Type openGenericType = typeof(IService<>);
            Type myClosedType = openGenericType.MakeGenericType(entityType);
            var something = _container.Resolve(myClosedType);


This seems like a very complicated way to resolve container.RegisterType<   IService<Product>, ProductService   >();   from inside a method. In comparison the constructor way seems so easy but my problem is that I do not know the type of service I am using until run time so I need to use resolve.

Dec 31, 2011 at 3:59 AM

Maintaining a reference to the container inside of your classes is typically discouraged.  Ideally all dependencies would be injected.

For your second problem there is a bit of a disconnect between using IService<T> (e.g. IService<Product>) and IService<passedParameter>.  The former is determined at compile time whereas the latter is not known until runtime when the value of passedParameter is specified.  (IService<passedParameter> is actually not valid because passedParameter is not a Type.)  Typically one would be dealing with only one type such as IService.  One option to handle the runtime requirement would be to use reflection.

IService<Product> and IService<Package> are totally different types so I don't see a need to register them by name ( container.RegisterType<   IService<Product>, ProductService   >("Product"); ).

Can you post the code for the Update method (or something like how you envision) and also IService<T>?  That might help.

My first thought was a factory to abstract away the runtime instantiation.  Here is something I wrote which probably doesn't work for you because I don't have the full context/knowledge of the complexities around exactly what you are doing.  Even so, hopefully it can help.  There is a factory that creates a processor that the Update method can use to perform the update: 

    #region Things_I_Have_No_Knowledge_Of

    public class Product {}
    public class Package {}

    public interface IService<T>
    {
        void DoIt();
    }

    public class ProductService : IService<Product>
    {
        public void DoIt()
        {
            Console.WriteLine("ProductServices do it");
        }
    }

    public class PackageService : IService<Package>
    {
        public void DoIt()
        {
            Console.WriteLine("PackageServices do it");
        }
    }

    #endregion

    public class ServiceProcessorFactory
    {
        // Use Funcs to avoid instantiating services that are never used by this ServiceProcessorFactory instance
        private Func<IService<Product>> productService;
        private Func<IService<Package>> packageService;

        private ServiceProcessor serviceProcessor;

        public ServiceProcessorFactory(Func<IService<Product>> productService,
            Func<IService<Package>> packageService, 
            ServiceProcessor serviceProcessor)
        {
            this.productService = productService;
            this.packageService = packageService;

            this.serviceProcessor = serviceProcessor;
        }

        public ServiceProcessor Create(string serviceClassName)
        {
            switch (serviceClassName)
            {
                case "Product" :
                    serviceProcessor.UpdateAction = () =>
                        {
                            IService<Product> service = productService();
                            service.DoIt();
                        };
                    break;

                case "Package" :
                    serviceProcessor.UpdateAction = () =>
                        {
                            IService<Product> service = productService();
                            service.DoIt();
                        };
                    break;

                default:
                    throw new ArgumentOutOfRangeException("serviceClassName");
            }

            return serviceProcessor;
        }
    }

    public class ServiceProcessor
    {
        public Action UpdateAction
        {
            private get;
            set;
        }

        public void Update()
        {
            UpdateAction();
        }
    }

    // This is the class that you showed the Update snippet from in your question
    public class MyClass
    {
        private ServiceProcessorFactory serviceProcessorFactory;

        public MyClass(ServiceProcessorFactory serviceFactory)
        {
            this.serviceProcessorFactory = serviceFactory;
        }

        public void Update(string serviceClassName)
        {
            var serviceProcessor = serviceProcessorFactory.Create(serviceClassName);
            serviceProcessor.Update();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IUnityContainer container = new UnityContainer();
            container.RegisterType<IService<Product>, ProductService>();
            container.RegisterType<IService<Package>, PackageService>();

            var myClass = container.Resolve<MyClass>();
            myClass.Update("Product");
        }
    }

 

 

--

Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Dec 31, 2011 at 3:02 PM

my problem is that I do not know the type of service I am using until run time so I need to use resolve.

Yes, I agree, that is the major issue.  

If you are OK with your current design and OK using reflection and unbounded generic types then I would still recommend encapsulating that logic into a factory and avoiding if possible maintaining a reference to the container.

Another issue with the reflection approach is that at compile time the compiler has no knowledge about what type "var something" is so it treats it as an object.  If you want to perform any useful work you will have to perform a cast so you are almost back where you started from since you now need to know to cast the object at compile time.  Or you could continue down the reflection road to invoke methods on the object.  If you are using C# 4 you could also use dynamic.  Either way I would try to minimize use of the container.

    public class MyClass2
    {
        private ServiceFactory factory;

        public MyClass2(ServiceFactory factory)
        {
            this.factory = factory;
        }

        public void Update(string serviceName)
        {
            dynamic obj = factory.Create(serviceName);
            obj.DoIt();
        }
    }

    public class ServiceFactory
    {
        IUnityContainer container;

        public ServiceFactory(IUnityContainer container)
        {
            this.container = container;
        }

        public object Create(string name)
        {
            string assemblyQualifiedName = typeof(Product)
                  .AssemblyQualifiedName
                  .Replace("Product", name);
            Type entityType = Type.GetType(assemblyQualifiedName, false, true);
            Type openGenericType = typeof(IService<>);
            Type myClosedType = openGenericType.MakeGenericType(entityType);
            return container.Resolve(myClosedType);
        }
    }

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com