I am testing Account Controller in asp.net mvc project. I tested all methods and I looked the code coverage results, I noticed the Initialize method is not coveraged.
How can I test this method?
public class AccountController : Controller
{
public IFormsAuthenticationService FormsService { get; set; }
public IMembershipService MembershipService { get; set; }
protected override void Initialize(RequestContext requestContext)
{
if (FormsService == null) { FormsService = new FormsAuthenticationService(); }
if (MembershipService == null) { MembershipService = new AccountMembershipService(); }
base.Initialize(requestContext);
}
I tried this test method but error occured.
[TestMethod]
public void Constructor_ReturnsController()
{
RequestContext requestContext = new RequestContext(new MockHttpContext(), new RouteData());
AccountController controller = new AccountController
{
FormsService = null,
MembershipService = null,
Url = new UrlHelper(requestContext),
};
IController cont = controller;
cont.Execute(requestContext);
}
Error Message:
Test method MVC3Project.Tests.Controllers.AccountControllerTests+AccountControllerTest.Constructor_ReturnsController threw exception:
System.NotImplementedException: The method or operation is not implemented.
You will need to invoke it explicitly in your unit test:
[TestMethod]
public void AccountController_Initialize_Method_Should_WireUp_Dependencies()
{
// arrange
AccountController controller = new AccountController();
// act
controller.Initialize(null);
// assert
Assert.IsNotNull(controller.FormsService);
Assert.IsNotNull(controller.MembershipService);
}
Note that using the Initialize method to setup your dependencies is bad practice. I would recommend you using dependency injection:
public class AccountController : Controller
{
public IFormsAuthenticationService FormsService { get; private set; }
public IMembershipService MembershipService { get; private set; }
public AccountController(IFormsAuthenticationService formsService, IMembershipService membershipService)
{
FormsService = formsService;
MembershipService = membershipService;
}
... actions
}
Now you could setup your favorite dependency injection framework to inject those dependencies into your controller. For example with Ninject that's pretty easy. Install the Ninject.MVC3 NuGet and then inside the generated ~/App_Start/NinjectWebCommon.cs file configure the kernel:
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IFormsAuthenticationService>().To<FormsAuthenticationService>();
kernel.Bind<IMembershipService>().To<AccountMembershipService>();
}
and finally you could mock those dependencies into your unit test and then define expectations on them.
Related
I'm trying to create a general base class that I can use in my whole project. I've written some code but still getting a NULL instance on my DbConnectionFactory.
I've create a ASP.Net web api project and added the AppHost file. I'm using Funq together with Simple Injector to Injector my custom services into the Api Controllers.
AppHost.cs
public class AppHost : AppHostBase
{
public AppHost() : base("Erp", typeof(AppHostService).Assembly)
{
}
public override void Configure(Container container)
{
// init
var simpleInjectorContainer = new SimpleInjector.Container();
var erpConnection = ConnectionStrings.ErpLocal;
var isLocal = HelperTools.IsLocalPath();
// check
if (isLocal)
{
erpConnection = ConnectionStrings.ErpOnline;
}
// mvc
ControllerBuilder.Current.SetControllerFactory(new FunqControllerFactory(container));
// register funq services
container.Register<IErpDbConnectionFactory>(c => new ErpDbConnectionFactory(erpConnectionString));
container.RegisterAutoWiredAs<CategoryService, ICategoryService>();
container.RegisterAutoWiredAs<ManufacturerService, IManufacturerService >();
container.RegisterAutoWiredAs<ProductService, IProductService>();
container.RegisterAutoWiredAs<ProductAttributeService, IProductAttributeService>();
container.RegisterAutoWiredAs<SpecificationAttributeService, ISpecificationAttributeService>();
//...
// simple injector services
SimpleInjectorInitializer.Initialize(simpleInjectorContainer, isLocal);
// register SimpleInjector IoC container, so ServiceStack can use it
container.Adapter = new SimpleInjectorIocAdapter(simpleInjectorContainer);
}
}
Base Class I'm trying to use
public abstract class ApiOrmLiteController : ApiController
{
IDbConnection _erpDb;
public virtual IErpDbConnectionFactory ErpDbConnectionFactory { get; set; }
public virtual IDbConnection ErpDb => _erpDb ?? (_erpDb = ErpDbConnectionFactory.OpenDbConnection());
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
_erpDb?.Dispose();
}
}
Web Api Controller
public class ShippingController : ApiOrmLiteController
{
#region Fields
private readonly IOrderService _orderService;
private readonly IAddressService _addressService;
private readonly ICustomerService _customerService;
private readonly IPdfService _pdfService;
private readonly IMessageService _messageService;
private readonly ITranslationService _translationService;
#endregion Fields
#region Ctor
public ShippingController(IOrderService orderService, IAddressService addressService, ICustomerService customerService, IPdfService pdfService, IMessageService messageService, ITranslationService translationService)
{
_orderService = orderService;
_addressService = addressService;
_customerService = customerService;
_pdfService = pdfService;
_messageService = messageService;
_translationService = translationService;
}
#endregion Ctor
[HttpGet]
[System.Web.Http.Route("Test")]
public void Test()
{
var products = ErpDb.Select<Category>();
}
}
You may need to use constructor injection for Web API or MVC controllers, alternatively you can access dependencies in ServiceStack's IOC via HostContext.TryResolve<T>, e.g:
public virtual IDbConnection ErpDb => _erpDb ??
(_erpDb = HostContext.TryResolve<IErpDbConnectionFactory>().OpenDbConnection());
I have a multi tier application using Entity Framework, MVC and Unity.
The basic setup is like this:
EF Data Access Layer
public class MyDataProvider : DbContext, IMyDataProvider
{
public MyDataProvider(SqlConnection existingConnection, bool contextOwnsConnection)
: base(existingConnection,contextOwnsConnection)
{
((IObjectContextAdapter)this).ObjectContext.CommandTimeout = 60;
Configuration.LazyLoadingEnabled = true;
Configuration.ValidateOnSaveEnabled = true;
Configuration.ProxyCreationEnabled = true;
Configuration.AutoDetectChangesEnabled = true;
}
public new IDbSet<TModel> Set<TModel>() where TModel : class
{
return base.Set<TModel>();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new EmployeeMapping());
base.OnModelCreating(modelBuilder);
}
public abstract class ServiceBase<TModel> : IDisposable, IService<TModel> where TModel : class, IModel
{
[Dependency]
public IMyDataProvider MyDataProvider { get; set; }
...
}
All services inherit from this class
I then inject specific services into the Business Logic Layer like so:
public class GetEmployees
{
[Dependency("EmployeeService")]
public IEmployeeService EmployeeService { get; set;
public IQueryable<Employee> GetAllEmployees()
{
return EmployeeService.GetTable();
}
...
}
In MVC I use a controller factory
public class MyControllerFactory : DefaultControllerFactory
{
private IUnityContainer _container;
public MyControllerFactory(IUnityContainer container)
{
_container = container;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType != null)
{
return _container.Resolve(controllerType) as IController;
}
else
{
return base.GetControllerInstance(requestContext, controllerType);
}
}
Global.asax
private static IUnityContainer InitContainer()
{
IUnityContainer unityContainer = new UnityContainer();
Bootstrapper bootstrapper = new Bootstrapper(unityContainer);
return unityContainer;
}
I pass the instance of UnityContainer into the Bootstrapper class. The Bootstrapper class self registers all assemblies.
In the MVC Controllers, I inject the Business Logic like so:
public class EmployeeController
{
[Dependency("GetEmployees")]
public IBusinessLogic GetEmployees_Operations { get; set; }
public ActionResult EmployeeMain()
{
var employees = GetEmployees_Operations.GetAllEmployees();
...
}
}
This all works great up to a point. Every so often I will get an exception thrown from MyDataProvider class: "EntityConnection can only be constructed with a closed DbConnection". This seems to happen during high use of the MVC site. The exception is simple enough to understand, but how should I go about fixing it?
I found that changing how I instantiate the business logic class from a field on the controller to inside the ActionResult method, I don't recieve the exception.
For example:
public class EmployeeController
{
//[Dependency("GetEmployees")]
//public IBusinessLogic GetEmployees_Operations { get; set; }
public ActionResult EmployeeMain()
{
IBusinessLogic GetEmployees_Operations = _ioc_Bootstrapper.Resolve(typeof(IBusinessLogic), "GetEmployees") as IBusinessLogic;
var employees = GetEmployees_Operations.GetAllEmployees();
...
}
}
Have I completely missed the boat on this and implemented Unity incorrectly?
Bootstrapper code
private void RegisterDAL(String assembly)
{
var currentAssembly = Assembly.LoadFrom(assembly);
var assemblyTypes = currentAssembly.GetTypes();
foreach (var assemblyType in assemblyTypes)
{
...
if (assemblyType.FullName.EndsWith("Provider"))
{
foreach (var requiredInterface in assemblyType.GetInterfaces())
{
if (requiredInterface.FullName.EndsWith("DataProvider"))
{
var typeFrom = assemblyType.GetInterface(requiredInterface.Name);
var typeTo = assemblyType;
var injector = GetInjectorConstructor(assemblyType.Module.Name);
RegisterType(typeFrom, typeTo, false, injector);
}
}
continue;
}
...
}
private InjectionConstructor GetInjectorConstructor(String moduleName)
{
...
connString = String.Concat("Data Source=MySqlServer, ";Initial Catalog=", catalogName, ";Application Name=", applicationName, ";Integrated Security=True; );
var conn = new SqlConnection(connString);
return new InjectionConstructor(conn, true);
}
I cannot figure this one out. I have a N-Tier ASP.MVC application and I am writing my first Unit Test and it seems to fail on my AutoMapper configuration. I have used AutoMapper a million times and never had any problems using it.
I'm sure I am missing something simple, but I have been staring at this for 24 hours now.
Class Library: APP.DOMAIN
public class User : IEntity<int>
{
public int Id { get; set; }
[StringLength(20), Required]
public string UserName { get; set; }
}
Class Library: APP.SERVICE
References App.Domain
public class UserViewModel
{
public int Id { get; set; }
public string UserName { get; set; }
}
I have my AutoMapper bootstrapper in the service layer.
public static class AutoMapperBootstrapper
{
public static void RegisterMappings()
{
Mapper.CreateMap<User, UserViewModel>();
}
}
UserService.cs
public class UserService : IUserService
{
private readonly IUserRepository _userRepository;
public UserService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public List<UserViewModel> GetUsers()
{
var users = _userRepository.GetAll();
if (users == null)
{
throw new Exception("No users found.");
}
return Mapper.Map<List<UserViewModel>>(users); // FAILS ON AUTOMAPPER
}
}
ASP.MVC Layer: APP.WEB
References App.Service
private void Application_Start(object sender, EventArgs e)
{
// Register AutoMapper
AutoMapperBootstrapper.RegisterMappings();
Mapper.AssertConfigurationIsValid();
// Code that runs on application startup
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
Unit Test Layer:
public class TestUserRepository :IUserRepository
{
public IEnumerable<User> GetAll()
{
var users = new List<User>()
{
new User { Id = 1, UserName = "Mary"},
new User { Id = 2, UserName = "Joe"}
};
return users;
}
}
public class UserServiceTest
{
private IUserService _userService;
private readonly IUserRepository _userRepository;
public UserServiceTest()
{
_userRepository = new TestUserRepository();
}
[Fact]
public void GetUsers_Should_Return_Correct_Number_Of_Users()
{
// Arrange
_userService = new UserService(_userRepository);
// Act
var result = _userService.GetUsers(); // FAILS ON AUTOMAPPER
// Assert
Assert.True(result.Any(u => u.UserName == "Mary"));
}
}
Failing Test Message:
*** Failures ***
Exception
AutoMapper.AutoMapperMappingException: AutoMapper.AutoMapperMappingException : Missing type map configuration or unsupported mapping.
Mapping types:
User -> UserViewModel
App.Data.Model.User -> App.Service.ViewModels.UserViewModel
Destination path:
List`1[0]
Source value:
App.Data.Model.User
at App.Service.Services.UserService.GetUsers() in D:\Repositories\App\App.Service\Services\UserService.cs:line 36
at App.Tests.Service.Tests.UserServiceTest.GetUsers_Should_Return_Correct_Number_Of_Users() in D:\Repositories\App\App.Tests\Service.Tests\UserServiceTest.cs:line 34
A little late to the party but have you tried setting the mapping before running the test?
public class UserServiceTest
{
public UserServiceTest()
{
// register the mappings before running the test
AutoMapperBootstrapper.RegisterMappings();
}
...
}
What we would need to do is Inject Custom Mapper Mock as given below. Add all those custom profiles that you have used for that particular class that you are unit testing and inject ConfigureMapper() in the Constructor of that class which is expecting IMapper Object
public IMapper ConfigureMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<CustomProfile>();
cfg.AddProfile<UserCustomProfile>();
cfg.AddProfile<UserWorkProfile>();
});
return config.CreateMapper();
}
Hope this solves the issue.
I'm not sure what the problem is, it's been a while since I've last used AutoMapper, but I'm quite sure that the following will work:
return users.Select(Mapper.Map<UserViewModel>);
I have a problem with this line:
var authorDTO = mapper.Map<AuthorCreationDTO>(AuthorinsideDB);
So I change the version of Autormapper
from:
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="7.0.0" />
to
Version="6.0.0"
and it worked.
I'm having a difficult time wrapping my head around Mef and how imports and exports work. My project structure is as follows.
Projects:
MefMVPApp (Main MVC 4 app)
MefMVCFramework.Common(Interfaces shared between the projects)
MefMVCDemo.Plugins.OrderStatus (pluggable area.)
MefMVCDemo.Plugins.Data (Repository for OrderStatus)
OrderStatus.Models(domain models shared between the projects)
The goal of the main Mvc App will be to host plug-able areas via mef.
The OrderStatus Area has a controller called OrderStatusController and is decorated with the Export Attribute and a ImportingConstructor.
[Export(typeof(IController))]
[ExportMetadata("controllerName", "OrderStatus")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class OrderStatusController : Controller
{
private readonly IRepository<OrderStatusApp.OrderStatusResponse>_repository ;
[ImportingConstructor]
public OrderStatusController(IRepository<OrderStatusApp.OrderStatusResponse> oRepository)
{
_repository = oRepository;
}
public ActionResult Index()
{
var model = _repository.GetAll();
return View();
}
}
IRepository is a class in the MefMVCFramework.Common assembly and will be used for generic CRUD operations.
public interface IRepository<T> where T : class
{
IEnumerable<T> GetAll();
T GetById(int id);
void Add(T entity);
int SaveOrUpdate(T entity);
bool Delete(T entity);
bool Delete(int id);
}
The MefMVCDemo.Plugins.Data assembly contains a Class called OrderManagementRepository that inherents for the generic repository and is marked with an Export Attribute.
[Export(typeof(IRepository<OrderStatusApp.OrderStatusResponse>))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class OrderManagementRepository : IRepository<OrderStatusApp.OrderStatusResponse>
{
private readonly JsonServiceClient _client;
public OrderManagementRepository()
{
_client = new JsonServiceClient("http://localhost:52266");
}
public IEnumerable<OrderStatusApp.OrderStatusResponse> GetAll()
{
throw new NotImplementedException("Can not get all");
}
public OrderStatusApp.OrderStatusResponse GetById(int id)
{
throw new NotImplementedException();
}
public void Add(OrderStatusApp.OrderStatusResponse entity)
{
throw new NotImplementedException();
}
public int SaveOrUpdate(OrderStatusApp.OrderStatusResponse entity)
{
throw new NotImplementedException();
}
public bool Delete(OrderStatusApp.OrderStatusResponse entity)
{
throw new NotImplementedException();
}
public bool Delete(int id)
{
throw new NotImplementedException();
}
}
Using Mefx tool I am able to see my parts and there are no rejection.
mefx /dir:C:\
Source.PreBranch.Keep\Prototypes\Projects\MefDemoApp\mefMVC4App\bin /parts
MefMVCDemo.Plugins.Data.OrderManagementRepository
mefMVCDemo.Plugins.OrderStatus.Controllers.OrderStatusController
MefMVCDemo.Plugins.OrderStatus.Verbs.OrderStatusVerb
I can see my import.
mefx /dir:C:\
Source.PreBranch.Keep\Prototypes\Projects\MefDemoApp\mefMVC4App\bin /imports
MefMVCFramework.Common.IRepository(OrderStatus.Models.OrderStatusApp+OrderStatus
Response)
MefMVCFramework.Common.IRepository(OrderStatus.Models.OrderStatusApp+OrderStatus
Response)
Now when browse my main mvc site with the /orderstatus uri I get the following error:
No parameterless constructor defined for this object.
Adding a default constructor to the OrderStatusController that takes no overloads doesn't seem to work.
I guess the question is what am I doing wrong? Why does my interface in the constructor all way end up being null and why is there an mvc error about the "No parameterless constructor defined for this object".
The default controller factory in MVC tries to create the controller using a parameterless constructor. If you want to change this behavior, then you need to create your own custom controller factory.
Here is an example of a ControllerFactory using imports/exports on controllers
I'm using MEF for importing some parts to my app but my controllers are not imported/exported, so I created the following controller factory
public class ControllerFactory : IControllerFactory
{
private readonly CompositionContainer _container;
private IControllerFactory _innerFactory;
/// <summary>
/// Constructor used to create the factory
/// </summary>
/// <param name="container">MEF Container that will be used for importing</param>
public ControllerFactory(CompositionContainer container)
{
_container = container;
_innerFactory = new DefaultControllerFactory();
}
/// <summary>
/// Method used for create the controller based on the provided name. It calls the
/// constructor of the controller passing the MEF container
/// </summary>
/// <param name="requestContext">Context of the request</param>
/// <param name="controllerName">Name of the controller provided in the route</param>
/// <returns>The controller instance</returns>
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
Type controllerType = FindControllerByName(controllerName);
var args = new object[] { this._container };
var controller = (IController)Activator.CreateInstance(controllerType, args);
return controller;
}
/// <summary>
/// This methods looks into the current Assembly for the Controller type
/// </summary>
/// <param name="name">The controller name provided in the route</param>
/// <returns>The controller type</returns>
private static Type FindControllerByName(string name){
var a = Assembly.GetAssembly(typeof(ControllerFactory));
var types = a.GetTypes();
Type type = types.Where(t => t.Name == String.Format("{0}Controller", name)).FirstOrDefault();
return type;
}
public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior(System.Web.Routing.RequestContext requestContext, string controllerName)
{
return System.Web.SessionState.SessionStateBehavior.Default;
}
public void ReleaseController(IController controller)
{
var disposableController = controller as IDisposable;
if (disposableController != null)
{
disposableController.Dispose();
}
}
}
Thank you pollirrata for pointing me in the right direction.
I had to change a few things to get this to work.
1.) I added an interface called INameMetadata to my MefMVCFramework.Common project.
public interface INameMetadata
{
string Name { get; }
}
2.) Modified My ExportMetadata Tag on my controller export to be Name, OrderStatus.
[Export(typeof(IController))]
[ExportMetadata("Name", "OrderStatus")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class OrderStatusController : Controller
{
private IRepository<OrderStatusApp.OrderStatusResponse> _repository;
[ImportingConstructor]
public OrderStatusController(IRepository<OrderStatusApp.OrderStatusResponse> oRepository)
{
_repository = oRepository;
}
public ActionResult Index()
{
var model = _repository.GetById(47985);
return View(model);
}
}
3.) Created the MefControllerFactory (based off what pollirrata posted but modified to look for the Metadata)
public class MefControllerFactory : IControllerFactory
{
private string _pluginPath;
private readonly DirectoryCatalog _catalog;
private readonly CompositionContainer _container;
private DefaultControllerFactory _defaultControllerFactory;
public MefControllerFactory(string pluginPath)
{
_pluginPath = pluginPath;
_catalog = new DirectoryCatalog(pluginPath);
_container = new CompositionContainer(_catalog);
_defaultControllerFactory = new DefaultControllerFactory();
}
public MefControllerFactory(CompositionContainer compositionContainer)
{
_container = compositionContainer;
_defaultControllerFactory = new DefaultControllerFactory();
}
#region IControllerFactory Members
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
//IController controller = null;
var controller = _container.GetExports<IController,INameMetadata>()
.Where(e=>e.Metadata.Name.Equals(controllerName))
.Select(e=>e.Value).FirstOrDefault();
if (controller == null)
{
throw new HttpException(404, "Not found");
}
return controller;
}
public void ReleaseController(IController controller)
{
var disposable = controller as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
#endregion
public SessionStateBehavior GetControllerSessionBehavior(System.Web.Routing.RequestContext requestContext, string controllerName)
{
return SessionStateBehavior.Default;
}
}
4.) I created a Class called MefConfig in Main MVC app and moved it to the App_Start Dir.
public static class MefConfig
{
public static void RegisterMef()
{
//var builder = new RegistrationBuilder();
//builder.ForTypesDerivedFrom<IRepository<OrderStatusApp.OrderStatusResponse>>().Export<IRepository<IRepository<OrderStatusApp.OrderStatusResponse>>>();
var directoryCatalog = new DirectoryCatalog(HostingEnvironment.MapPath("~/bin"), "*.dll");
var container = new CompositionContainer(directoryCatalog, true);
ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(container));
//Working
//ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(HostingEnvironment.MapPath("~/bin")));
// Install MEF dependency resolver for MVC
var resolver = new MefDependencyResolver(container);
DependencyResolver.SetResolver(resolver);
// Install MEF dependency resolver for Web API
GlobalConfiguration.Configuration.DependencyResolver = resolver;
var d = container.GetExportedValues<IRepository<OrderStatusApp.OrderStatusResponse>>();
//Mefx.
try
{
//var ci = new CompositionInfo(aggregateCatalog, container);
var ci = new CompositionInfo(directoryCatalog, container);
var partDef = ci.GetPartDefinitionInfo(typeof(IRepository<OrderStatusApp.OrderStatusResponse>));
//var possibleCauses = partDef.FindPossibleRootCauses();
var stringWriter = new StringWriter();
CompositionInfoTextFormatter.Write(ci, stringWriter);
var compStatString = stringWriter.ToString();
}
catch
{
}
MvcApplication.ActionVerbs = container.GetExports<IActionVerb, IActionVerbMetadata>();
}
}
5.) Load the Mefconfig from the global.asax.
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
//Register Mef
MefConfig.RegisterMef();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
I am using asp.net mvc 3, ninject 2.0 and the ninject mvc 3 plugin.
I am wondering how do I get service layers into my filter(in this case an authorization filter?).
I like to do constructor inject so is this possible or do I have to property inject?
Thanks
Edit
I have this for property inject but my property is always null
[Inject]
public IAccountService AccountServiceHelper { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
// check if context is set
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
// check if user is authenticated
if (httpContext.User.Identity.IsAuthenticated == true)
{
// stuff here
return true;
}
return false;
}
/// <summary>
/// Application_Start
/// </summary>
protected void Application_Start()
{
// Hook our DI stuff when application starts
IKernel kernel = SetupDependencyInjection();
RegisterMaps.Register();
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
public IKernel SetupDependencyInjection()
{
IKernel kernel = CreateKernel();
// Tell ASP.NET MVC 3 to use our Ninject DI Container
DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
return kernel;
}
protected IKernel CreateKernel()
{
var modules = new INinjectModule[]
{
new NhibernateModule(),
new ServiceModule(),
new RepoModule()
};
return new StandardKernel(modules);
}
public class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<IAccountService>().To<AccountService>();
}
}
Edit
I upgraded to ninject 2.2 and get finally got it work.
Edit 2
I am going to try and do the constructor way for my authorize filter but I am unsure how to pass in the Roles. I am guessing I have to do it through ninject?
Edit 3
This is what I have so far
public class MyAuthorizeAttribute : AuthorizeAttribute
{
private readonly IAccountService accountService;
public MyAuthorizeAttribute(IAccountService accountService)
{
this.accountService = accountService;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return base.AuthorizeCore(httpContext);
}
}
this.BindFilter<MyAuthorizeAttribute>(FilterScope.Controller, 0)
.WhenControllerHas<MyAuthorizeAttribute>();
[MyAuthorize]
public class MyController : BaseController
{
}
It tells me it want's a no parameter constructor. So I must be missing something.
The problem with filters is that they are attributes. And if you define a constructor of an attribute that expects some dependency you will never gonna be able to apply it to any method: because all values that you pass to attributes must be known at compile time.
So basically you have two possibilities:
Use Ninject to apply the filter globally instead of decorating your controllers/actions with it:
public interface IFoo { }
public class Foo : IFoo { }
public class MyFooFilter : AuthorizeAttribute
{
public MyFooFilter(IFoo foo)
{
}
}
and then configure the kernel:
kernel.Bind<IFoo>().To<Foo>();
kernel.BindFilter<MyFooFilter>(FilterScope.Action, 0).When(
(controllerContext, actionDescriptor) =>
string.Equals(
controllerContext.RouteData.GetRequiredString("controller"),
"home",
StringComparison.OrdinalIgnoreCase
)
);
Use property injection:
public interface IFoo { }
public class Foo : IFoo { }
public class MyFooFilter : AuthorizeAttribute
{
[Inject]
public IFoo Foo { get; set; }
}
and then configure the kernel:
kernel.Bind<IFoo>().To<Foo>();
and decorate some controller/action with your custom filter:
[MyFooFilter]
public ActionResult Index()
{
return View();
}
See this question /answer here:
Setup filter attribute for dependency injection to accept params in constructor
and here
Dependency Injection with Ninject and Filter attribute for asp.net mvc