Pascal
Pascal

Reputation: 2974

Unable to inject DBContext into my Web API 2 Controller with Unity

I've been at it for days, but I can't get Unity to inject anything with RegisterType<> into my Controller. I'm using Web Api 2, in Visual Studio 2015, with Unity 4. Whenever I try to inject IUnitOfWork or IRFContext, I get "message": "An error occurred when trying to create a controller of type 'ClPlayersController'. Make sure that the controller has a parameterless public constructor.". I'm using the Unity.AspNet.WebApi to bootstrapp into WebApi. Below is my UnityWebApiActivator

[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(mycompany.project.api.UnityWebApiActivator), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(mycompany.project.api.UnityWebApiActivator), "Shutdown")]

namespace mycompany.project.api
{
    public static class UnityWebApiActivator
    {
        public static void Start() 
        {
            var resolver = new UnityDependencyResolver(UnityConfig.GetConfiguredContainer());
            GlobalConfiguration.Configuration.DependencyResolver = resolver;
        }

        public static void Shutdown()
        {
            var container = UnityConfig.GetConfiguredContainer();
            container.Dispose();
        }
    }
}

I'm using a Start.cs due to Owin.

[assembly: OwinStartup(typeof(mycompany.project.api.Startup))]
namespace mycompany.project.api
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            HttpConfiguration config = new HttpConfiguration();

            ConfigureOAuth(app);

            config.DependencyResolver = new UnityDependencyResolver(UnityConfig.GetConfiguredContainer());
            WebApiConfig.Register(config);
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
            app.UseWebApi(config);
        }

        public void ConfigureOAuth(IAppBuilder app)
        {
            OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
            {
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
                Provider = new SimpleAuthorizationServerProvider(),
                RefreshTokenProvider = new SimpleRefreshTokenProvider()
            };

            // Token Generation
            app.UseOAuthAuthorizationServer(OAuthServerOptions);
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

        }
    }
}

My WebApiConfig.cs is below:

namespace mycompany.project.api
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            log4net.Config.XmlConfigurator.Configure();
            config.MapHttpAttributeRoutes();
            config.EnableSystemDiagnosticsTracing();
            config.Services.Add(typeof(IExceptionLogger),
                new SimpleExceptionLogger(new LogManagerAdapter()));
            config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
        }
    }
}

My UnityConfig.cs is below

namespace mycompany.project.api
{
    public class UnityConfig
    {
        #region Unity Container
        private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
        {
            var container = new UnityContainer();
            RegisterTypes(container);
            return container;
        });

        public static IUnityContainer GetConfiguredContainer()
        {
            return container.Value;
        }
        #endregion

        public static void RegisterTypes(IUnityContainer container)
        {
            var config = new MapperConfiguration(cfg =>
            {
                                //AutoMapper bindings
            });
            container.RegisterInstance<IMapper>(config.CreateMapper());
            container.RegisterType<IRFContext, RFContext>(new PerThreadLifetimeManager());
            container.RegisterType<IUnitOfWork, UnitOfWork>();
            XmlConfigurator.Configure();
            var logManager = new LogManagerAdapter();
            container.RegisterInstance<ILogManager>(logManager);
        }
    }
}

All that I have in my Global.asax is below:

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Error()
    {
        var exception = Server.GetLastError();
        if (exception != null)
        {
            var log = new LogManagerAdapter().GetLog(typeof(WebApiApplication));
            log.Error("Unhandled exception.", exception);
        }
    }
}

If my Controller is like this, it works fine:

public class ClPlayersController : ApiController
{
    private readonly IMapper mapper;

    public ClPlayersController(IMapper _mapper, IUnityContainer container)
    {
        mapper = _mapper;
    }

But placing IUnitOfWork, like below, or the IRFContext, I get the error:

    private readonly IMapper mapper;
    private readonly IUnitOfWork unitOfWork;

    public ClPlayersController(IMapper _mapper, IUnityContainer container, IUnitOfWork _unitOfWork)
    {
        mapper = _mapper;
        unitOfWork = _unitOfWork;
    }

I can't find, for the life of me, what I'm doing wrong. If I loop through the container.Registrations on the constructor, I find the mappings, but they refuse to get injected. Any hints?

EDIT

Below is the code for UnitOfWork and RFContext

namespace mycompany.project.data.configuracao
{
    public class UnitOfWork : IUnitOfWork
    {
        private readonly IRFContext _rfContext;
        private bool _disposed = false;

        public UnitOfWork(IRFContext rfContext)
        {
            _rfContext = rfContext;
        }
        public void Commit()
        {
            if (_disposed)
            {
                throw new ObjectDisposedException(this.GetType().FullName);
            }

            _rfContext.SaveChanges();
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (_disposed) return;

            if (disposing && _rfContext != null)
            {
                _rfContext.Dispose();
            }

            _disposed = true;
        }
    }
}

and

namespace mycompany.project.data.configuracao
{
    public interface IUnitOfWork : IDisposable
    {
        void Commit();
    }
}

and RFContext is a basic POCO generated DBContext

namespace mycompany.project.data.configuracao
{
    using System.Linq;

    public class RFContext : System.Data.Entity.DbContext, IRFContext
    {
        public System.Data.Entity.DbSet<ClGrupoEconomico> ClGrupoEconomicoes { get; set; }
                //all my DbSets
        public System.Data.Entity.DbSet<SpTipoLog> SpTipoLogs { get; set; }

        static RFContext()
        {
            System.Data.Entity.Database.SetInitializer<RFContext>(null);
        }

        public RFContext()
            : base("Name=RFContext")
        {
        }

        public RFContext(string connectionString)
            : base(connectionString)
        {
        }

        public RFContext(string connectionString, System.Data.Entity.Infrastructure.DbCompiledModel model)
            : base(connectionString, model)
        {
        }

        public RFContext(System.Data.Common.DbConnection existingConnection, bool contextOwnsConnection)
            : base(existingConnection, contextOwnsConnection)
        {
        }

        public RFContext(System.Data.Common.DbConnection existingConnection, System.Data.Entity.Infrastructure.DbCompiledModel model, bool contextOwnsConnection)
            : base(existingConnection, model, contextOwnsConnection)
        {
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }

        protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Configurations.Add(new ClGrupoEconomicoConfiguration());
                        //all my Configuration classes
            modelBuilder.Configurations.Add(new SpTipoLogConfiguration());
        }

        public static System.Data.Entity.DbModelBuilder CreateModel(System.Data.Entity.DbModelBuilder modelBuilder, string schema)
        {
            modelBuilder.Configurations.Add(new ClGrupoEconomicoConfiguration(schema));
                        //all my configuration classes
            modelBuilder.Configurations.Add(new SpTipoLogConfiguration(schema));
            return modelBuilder;
        }
    }
}

Upvotes: 1

Views: 1633

Answers (1)

smoksnes
smoksnes

Reputation: 10851

Unfortunately the exception you are seeing can occur for several reasons. One of them is when Unity cannot resolve one or more of your injections.

An error occurred when trying to create a controller of type 'FooController'. Make sure that the controller has a parameterless public constructor.

So, based on the information in your question your setup is apparently correct, since IMapper can be injected. Therefore I guess that UnitOfWork and RFContext have dependencies that Unity cannot resolve. Maybe a repository?

UPDATE:

The problem here is that your RFContext has several constructors.

https://msdn.microsoft.com/en-us/library/cc440940.aspx#cnstrctinj_multiple

When a target class contains more than one constructor with the same number of parameters, you must apply the InjectionConstructor attribute to the constructor that the Unity container will use to indicate which constructor the container should use. As with automatic constructor injection, you can specify the constructor parameters as a concrete type, or you can specify an interface or base class for which the Unity container contains a registered mapping.

In this case Unity doesn't know how to resolve your RFContext, and will try to use the constructor with the most parameters. You can solve it by using

container.RegisterType<IRFContext, RFContext>(new InjectionConstructor());

Upvotes: 2

Related Questions