Unity Bootstrapper (Unity.Mvc), Unity 3, MVC 5, EF6 Receiving Error: Parameterless Public Constructor on Controller
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));
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, 2022Comments
-
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