Unity Bootstrapper (Unity.Mvc), Unity 3, MVC 5, EF6 Receiving Error: Parameterless Public Constructor on Controller

17,785

You need to set the DependencyResolver. I cant see the code in the example you provided where this is done.

Once setting up your UnityContainer and registering your types, you need to set the System.Web.MVC.DependencyResolver.

 DependencyResolver.SetResolver(new UnityDependencyResolver(container));
Share:
17,785
SouthPlatte
Author by

SouthPlatte

Sr. Software Engineer, multi discipline, multi platform and multi tier. Full-stack experience using .Net, .Net Core, AngularJS, KnockoutJS, Sencha ExtJS, Sencha Touch, and numerous other libraries, utilities and tools. Focus has been on unique integrations for customer satisfaction, performance, scalability and usability. Out of the box ideas bring solutions that work, and I despise being shoved into a box.

Updated on June 22, 2022

Comments

  • SouthPlatte
    SouthPlatte almost 2 years

    Ok, after searching Google, here and several ASP/MVC forums I am bound to have to ask what the hell I am doing wrong here.

    I have a good start to my application, an ok understanding of DI, IoC and am using the Repository, Service and UnitOfWork patterns. When I attempt to load a controller that needs the DI from Unity, it's as if unity is not resolving any of the registered items, or that I have done it poorly. From all the examples I can see for this version (not the version that creates the Bootstrap.cs file that is then called from Global.asax) I am doing what others have done with no love from Unity.

    My core question is: Have I setup/configured Unity to inject the items into the controller constructor as needed or not. If I have, any ideas why it's not working like examples I have seen?

    I keep getting the error that the AssetController needs to have a parameterless public constructor. If I add one, then it uses it without the DI and if I don't add one, then it yells about not having it.

    Thanks, code below.

    UnityConfig.cs

    namespace CARS.web.App_Start
    {
        /// <summary>
        /// Specifies the Unity configuration for the main container.
        /// </summary>
        public class UnityConfig
        {
            #region Unity Container
            private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
            {
                var container = new UnityContainer();
                RegisterTypes(container);
                return container;
            });
    
            /// <summary>
            /// Gets the configured Unity container.
            /// </summary>
            public static IUnityContainer GetConfiguredContainer()
            {
                return container.Value;
            }
            #endregion
    
            /// <summary>Registers the type mappings with the Unity container.</summary>
            /// <param name="container">The unity container to configure.</param>
            /// <remarks>There is no need to register concrete types such as controllers or API controllers (unless you want to 
            /// change the defaults), as Unity allows resolving a concrete type even if it was not previously registered.</remarks>
            public static void RegisterTypes(IUnityContainer container)
            {
                // NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
                // container.LoadConfiguration();
    
                // TODO: Register your types here
                // container.RegisterType<IProductRepository, ProductRepository>();
                container.RegisterType<IDataContext, CARSDEMOContext>(new PerRequestLifetimeManager())
                    .RegisterType<IAssetService, AssetService>()
                    .RegisterType<IUnitOfWork, UnitOfWork>()
                    .RegisterType<IRepository<Asset>, Repository<Asset>>();
                    //.RegisterType<AssetController>(new InjectionConstructor(typeof(IAssetService), typeof(IUnitOfWork)));
            }
        }
    }
    

    AssetController.cs (constructor portion where I am doing the injection params)

    namespace CARS.web.Controllers
    {
        public class AssetController : Controller
        {
            private readonly IAssetService _assetService;
            private readonly IUnitOfWork _unitOfWork;
    
            public AssetController(IAssetService assetService, IUnitOfWork unitOfWork)
            {
                _assetService = assetService;
                _unitOfWork = unitOfWork;
            }
            //other methods for CRUD etc stripped for brevity
        }
    }
    

    IAssetService.cs (first param is the assetService )

    namespace CARS.service
    {
        public interface IAssetService : IService<Asset>
        {
            Task<IEnumerable<Asset>> GetAsync();
            Task<Asset> FindAsync(Guid id);
            Asset Add(Asset asset);
            Asset Update(Asset asset);
            void Remove(Guid id);
        }
    }
    

    AssetService.cs (concrete implementation for IAssetService interaction)

    namespace CARS.service
    {
    
    
        public class AssetService : Service<Asset>, IAssetService
        {
            private readonly IRepositoryAsync<Asset> _repository;
    
            public AssetService(IRepositoryAsync<Asset> repository) : base(repository)
            {
                _repository = repository;
            }
    
            public Task<IEnumerable<Asset>> GetAsync()
            {
                //return _repository.Query().SelectAsync();
                return _repository.Query().SelectAsync();
            }
    
            public Task<Asset> FindAsync(Guid id)
            {
                return _repository.FindAsync(id);
            }
    
            public Asset Add(Asset asset)
            {
                _repository.Insert(asset);
                return asset;
            }
    
            public Asset Update(Asset asset)
            {
                _repository.Update(asset);
                return asset;
            }
    
            public void Remove(Guid id)
            {
                _repository.Delete(id);
            }
        }
    
    }
    

    IUnitOfWork.cs (this is from Long Le's Generic UofW and Repository - http://genericunitofworkandrepositories.codeplex.com/)

    namespace Repository.Pattern.UnitOfWork
    {
        public interface IUnitOfWork : IDisposable
        {
            int SaveChanges();
            Task<int> SaveChangesAsync();
            void Dispose(bool disposing);
            IRepository<TEntity> Repository<TEntity>() where TEntity : IObjectState;
            void BeginTransaction();
            bool Commit();
            void Rollback();
        }
    }
    

    UnitOfWork.cs (again from Long Le's framework)

    namespace Repository.Pattern.Ef6
    {
        public class UnitOfWork : IUnitOfWork, IUnitOfWorkAsync
        {
            #region Private Fields
    
            private readonly IDataContextAsync _dataContext;
            private bool _disposed;
            private ObjectContext _objectContext;
            private Dictionary<string, object> _repositories;
            private DbTransaction _transaction;
    
            #endregion Private Fields
    
            #region Constuctor/Dispose
    
            public UnitOfWork(IDataContextAsync dataContext)
            {
                _dataContext = dataContext;
            }
    
            public void Dispose()
            {
                if (_objectContext != null && _objectContext.Connection.State == ConnectionState.Open)
                    _objectContext.Connection.Close();
    
                Dispose(true);
                GC.SuppressFinalize(this);
            }
    
            public virtual void Dispose(bool disposing)
            {
                if (!_disposed && disposing)
                    _dataContext.Dispose();
                _disposed = true;
            }
    
            #endregion Constuctor/Dispose
    
            public int SaveChanges()
            {
                return _dataContext.SaveChanges();
            }
    
            public IRepository<TEntity> Repository<TEntity>() where TEntity : IObjectState
            {
                return RepositoryAsync<TEntity>();
            }
    
            public Task<int> SaveChangesAsync()
            {
                return _dataContext.SaveChangesAsync();
            }
    
            public Task<int> SaveChangesAsync(CancellationToken cancellationToken)
            {
                return _dataContext.SaveChangesAsync(cancellationToken);
            }
    
            public IRepositoryAsync<TEntity> RepositoryAsync<TEntity>() where TEntity : IObjectState
            {
                if (_repositories == null)
                    _repositories = new Dictionary<string, object>();
    
                var type = typeof (TEntity).Name;
    
                if (_repositories.ContainsKey(type))
                    return (IRepositoryAsync<TEntity>) _repositories[type];
    
                var repositoryType = typeof (Repository<>);
                _repositories.Add(type, Activator.CreateInstance(repositoryType.MakeGenericType(typeof (TEntity)), _dataContext, this));
    
                return (IRepositoryAsync<TEntity>) _repositories[type];
            }
    
            #region Unit of Work Transactions
    
            public void BeginTransaction()
            {
                _objectContext = ((IObjectContextAdapter) _dataContext).ObjectContext;
                if (_objectContext.Connection.State != ConnectionState.Open)
                {
                    _objectContext.Connection.Open();
                    _transaction = _objectContext.Connection.BeginTransaction();
                }
            }
    
            public bool Commit()
            {
                _transaction.Commit();
                return true;
            }
    
            public void Rollback()
            {
                _transaction.Rollback();
                ((DataContext)_dataContext).SyncObjectsStatePostCommit();
            }
    
            #endregion
    
            // Uncomment, if rather have IRepositoryAsync<TEntity> IoC vs. Reflection Activation
            //public IRepositoryAsync<TEntity> RepositoryAsync<TEntity>() where TEntity : EntityBase
            //{
            //    return ServiceLocator.Current.GetInstance<IRepositoryAsync<TEntity>>();
            //}
        }
    } 
    

    Updated to include the SetResolver info from UnityMvcActivator.cs

    using System.Linq;
    using System.Web.Mvc;
    using Microsoft.Practices.Unity.Mvc;
    
    [assembly: WebActivatorEx.PreApplicationStartMethod(typeof(CARS.web.App_Start.UnityWebActivator), "Start")]
    
    namespace CARS.web.App_Start
    {
        /// <summary>Provides the bootstrapping for integrating Unity with ASP.NET MVC.</summary>
        public static class UnityWebActivator
        {
            /// <summary>Integrates Unity when the application starts.</summary>
            public static void Start() 
            {
                var container = UnityConfig.GetConfiguredContainer();
    
                FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().First());
                FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(container));
    
                DependencyResolver.SetResolver(new UnityDependencyResolver(container));
    
                // TODO: Uncomment if you want to use PerRequestLifetimeManager
                 Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
            }
        }
    }
    

    I have read/tried the following info/data and nothing has fixed it:

    The type IUserStore`1 does not have an accessible constructor

    How to add MVC 5 authentication to Unity IoC?

    Types not resolving with Unity [MVC 5]

    I have ready where one must write a ControllerFactory for Unity to be able to do this, but that seems quite a bit of work when all the examples I have found simply have the config registered, and the injection apparently happening on the controllers and other classes as need.

    And finally the error:

    The following server error was encountered: 
    An error occurred when trying to create a controller of type           'CARS.web.Controllers.AssetController'. Make sure that the controller has a parameterless     public constructor.Details are: 
    at     System.Web.Mvc.DefaultControllerFactory.DefaultControllerActivator.Create(RequestContext    requestContext, Type controllerType) at    System.Web.Mvc.DefaultControllerFactory.CreateController(RequestContext requestContext,     String controllerName) at System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase     httpContext, IController& controller, IControllerFactory& factory) at     System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback     callback, Object state) at     System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionSte    p.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean&     completedSynchronously)
    

    Thanks