Search code examples
c#.netreflectiondependency-injectioninversion-of-control

Create instance and resolve dependencies


I have c# library that referenced in main project. The library

  1. Gets main project assembly;
  2. Retrieves all types using System.Reflection;
  3. Should creates type using Activator.CreateInstance (I’m not sure this is the best way).

The library doesn’t know anything about main project, only a metadata that can be obtained through reflection. How the dependencies can be resolved?

private readonly Assembly _assembly;

public Injector()
{
    _assembly = Assembly.GetEntryAssembly();
}

public List<string> GetTypes()
{
    return _assembly
        .GetTypes()
        .Select(x => x.FullName)
        .ToList();
}

public object GetType(string typeName)
{
    Type type = _assembly
        .GetTypes()
        .First(x => x.FullName == typeName);

    object instance = Activator.CreateInstance(type);

    return instance;
}

Possible issue: different IoC containers (third-party libraries, own-written).

What is the best way to handle this problem and keep library more automatic without forcing users provide a lot of settings? If it's not possible, could you please offer any other solutions? Thanks.

EDIT: How to provide dependencies to instance in Activator.CreateInstanceor create instance directly from main(source) project? It should be allowed to create any instance that contains in main project. Yes, main project also doesn’t know anything about library. So, it’s desirable to make minimum code changes in main project.

EDIT 2: The library won't be used in source project, it will have own UI interface. For example, Swagger API


Solution

  • I've found a way how to handle this.

    .NET Standard featured a new interface IServiceProvider that must be implemented by any IoC container in Startup.ConfigureServices(). We can pass this IServiceProvider to the library constructor and use method GetService(Type type) to create service with all resolved dependency injections.

    Source project (e.g. Autofac container in Startup.cs):

        // This method gets called by the runtime. Use this method to add services to the container.
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    
            var builder = new ContainerBuilder();
            builder.RegisterType<CalculationService>().As<ICalculationService>();
            builder.RegisterType<Logger>().As<ILogger>();
            builder.Populate(services);
            var container = builder.Build();
            return new AutofacServiceProvider(container);
        }
    
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider diService)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
            }
    
            app.UseHttpsRedirection();
            app.UseMvc();
    
            var injection = new Injector(diService);
            var result = injection.RunMethod(typeof(ICalculationService).FullName, "Sum", new int[] { 1, 2, 3 });
        }
    

    Library project:

    public class Injector
    {
        private readonly Assembly _assembly;
        private readonly IServiceProvider _serviceProvider;
    
        public Injector(IServiceProvider serviceProvider)
        {
            _assembly = Assembly.GetEntryAssembly();
            _serviceProvider = serviceProvider;
        }
    
        public object RunMethod(string className, string methodName, params object[] parameters)
        {
            var classType = _assembly
                .GetType(className);
    
            object instance = _serviceProvider.GetService(classType);
    
            var method = classType.GetMethod(methodName);
    
            var result = method.Invoke(instance, parameters);
    
            return result;
        }
    }