I am trying to use Ninject in ASP.NET MVC project. Here is my plan to use entity framework for my project-
//Web.config
<connectionStrings>
<add name="MyTestDbEntities" connectionString="...." />
</connectionStrings>
//Base controller
public abstract class BaseController : Controller
{
protected readonly MyTestDbEntities Db;
public BaseController() { }
public BaseController(MyTestDbEntities context)
{
this.Db = context;
}
}
public class HomeController : BaseController
{
public ActionResult Index()
{
Db.Students.Add(new Student() { StudentName="test"});
Db.SaveChanges();
return View();
}
}
I would like to use Ninject as follows-
kernel.Bind<MyTestDbEntities>().To<BaseController>().InRequestScope();
But it says-
The type 'NinjectTest.BaseController' cannot be used as type parameter
'TImplementation' in the generic type or method
'IBindingToSyntax<MyTestDbEntities>.To<TImplementation>()'.
There is no implicit reference conversion from 'NinjectTest.BaseController'
to 'NinjectTest.Models.MyTestDbEntities'.
Would you please suggest me how can I configure Ninject to work in the project?
what typically occurs is you bind an interface to a concrete Type that implements it, i.e.:
kernel.Bind<IMyService>().To<MyServiceImpl>();
you do not need to create a binding to be able to inject the service into every consumer (i.e., BaseController). you can use the binding anywhere by asking for it in the constructor (contructor injection), or decorating a property with [Inject] (property injection, or setter injection)
in your example, you would need to create a binding for your DbContext:
kernel.Bind<MyTestDbEntities>().ToSelf().InRequestScope();
and then it will be injected into your Controller constructors, but all Controllers that derive from BaseController will need to have that contructor that asks for the DbContext as a parameter
public HomeController(MyTestDbEntities db) : base(db) { }
however, be aware that you are creating a dependency on a concrete implementation (the DbContext), which sort of defeats the purpose of Dependency Injection.
Related
I have a C# solution with two projects, ProductStore.Web and ProductStore.Data, both targeting .NET Core 2.0.
I have my HomeController and CustomerRepository as follows (I've set it up in the HomeController for speed, customer creation will be in the customer controller, but not yet scaffold-ed it out):
namespace ProductStore.Web.Controllers
{
public class HomeController : Controller
{
private readonly DatabaseContext _context;
public HomeController(DatabaseContext context)
{
_context = context;
}
public IActionResult Index()
{
ICustomerRepository<Customer> cr = new CustomerRepository(_context);
Customer customer = new Customer
{
// customer details
};
//_context.Customers.Add(customer);
int result = cr.Create(customer).Result;
return View();
}
}
}
namespace ProductStore.Data
{
public class CustomerRepository : ICustomerRepository<Customer>
{
DatabaseContext _context;
public CustomerRepository(DatabaseContext context)
{
_context = context;
}
}
}
Dependency Injection resolves _context automatically inside the controller. I am then passing the context as a parameter for CustomerRepository which resides in ProductStore.Data.
My question is two fold:
Is this best practice (passing the context from controller to CustomerRepository)
If not best practice, can I access context via IServiceCollection services in a similar way to how the DatabaseContext is inserted into services in my application StartUp.cs class...
I feel like I shouldn't have to pass the context over, CustomerRepository should be responsible for acquiring the context.
FYI, relatively new to MVC and brand new to Entity Framework and Dependency Injection
Thanks
You don't need to pass context to controller to be able to use the context registered in services inside repository. The way I prefer to do that, is the following. Inject context into repository and then inject repository into controller. Using the Microsoft Dependency Injection Extension in for .Net Core it will look like this
// Service registrations in Startup class
public void ConfigureServices(IServiceCollection services)
{
// Also other service registrations
services.AddMvc();
services.AddScoped<DatabaseContext, DatabaseContext>();
services.AddScoped<ICustomerRepository<Customer>, CustomerRepository>();
}
// Controller
namespace ProductStore.Web.Controllers
{
public class HomeController : Controller
{
private readonly ICustomerRepository _customerRepository;
public HomeController(ICustomerRepository customerRepository)
{
_customerRepository = customerRepository;
}
public IActionResult Index()
{
Customer customer = new Customer
{
// customer details
};
//_context.Customers.Add(customer);
int result = _customerRepository.Create(customer).Result;
return View();
}
}
}
//Repository
namespace ProductStore.Data
{
public class CustomerRepository : ICustomerRepository<Customer>
{
DatabaseContext _context;
public CustomerRepository(DatabaseContext context)
{
_context = context;
}
}
}
After this when DependencyResolver tries to resolve ICustomerRepository to inject into the HomeController he sees, that the registered implementation of ICustomerRepository (in our case CustomerRepository) has one constructor which needs DatabaseContext as a parameter and DependencyResolver trying to to get registered service for DatabaseContext and inject it into CustomerRepository
If you define your repository in your ConfigureServices method, you won't need to inject the DbContext into controller, just the repository:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped(typeof(ICustomerRepository<>), typeof(CustomerRepository<>));
}
Then you can just simply inject the repository into controller:
public class HomeController : Controller
{
private readonly ICustomerRepository _customerRepository;
public HomeController(ICustomerRepository customerRepository)
{
_customerRepository = customerRepository;
}
...
}
The dependency injector takes care of injecting DbContext into your repository.
1. Is this best practice (passing the context from controller to CustomerRepository)
I think you're looking for something like a "Unit of Work" pattern.
Microsoft has written a tutorial about creating one here.
I would also inject the repository in your controller instead of your
context.
2. If not best practice, can I access context via IServiceCollection services in a similar way to how the DatabaseContext is inserted into services in my application StartUp.cs class...
If I understand you correctly, than yes, you can. Also add the
CustomerRepository to the services in your StartUp.cs so you can use
it in your controller.
Mabye this tutorial from Microsoft will also help you.
I have a controller and I want to use Dependency Injection with constructor,this is my code
private readonly IHomeService _iHomeService;
public HomeController(IHomeService iHomeService)
{
_iHomeService = iHomeService;
}
public HomeController()
{
}
When I remove Constructor without any parameter(Second Constructor),I see this error :
No parameterless constructor defined
and When I use Constructor without any parameter,I see my private field is null(_iHomeService = null) because program use constructor without parameter.
How can I resolve this problem for Dependency Injection?
Well, to do dependency injection youll need to either use a framework or use controller factory .
try ninject
public class HomeController : Controller
{
private readonly IWelcomeMessageService welcomeMessageService;
public HomeController(IWelcomeMessageService welcomeMessageService)
{
this.welcomeMessageService = welcomeMessageService;
}
public void Index()
{
ViewModel.Message = this.welcomeMessageService.TodaysWelcomeMessage;
return View();
}
}
public class WelcomeMessageServiceModule : NinjectModule
{
public override void Load()
{
this.Bind<IWelcomeMessageService>().To<WelcomeMessageService>();
}
}
The framework will take control on the controller instance creation and pass the constractor params
It sounds like you are expecting, automatically, the HomeService class to be instantiated and injected into the Controller.
Using an IoC framework like Ninject or StructureMap will do that for you - once you've set it up.
If you don't want to use an IoC framework, you'll need to manually instantiate the HomeService in your constructor.
ASP.NET uses a ControllerFactory to instantiate your controllers on-demand. This class requires that your controller has a parameterless constructor that it can use to create an instance of it.
You'll need to use a dependency injection framework to create your controllers and inject the required dependencies. ASP.NET has some dependency injection capability, but I understand that it is flawed. I suggest using Castle Windsor to manage your dependency injection. It integrates very well with ASP.NET, and there's a tutorial on integrating it here.
If you go down this route, you'd end up with an installer for your controllers and service:
public class Installer : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly().BasedOn<IController>().LifestyleTransient());
container.Register(Component.For<IHomeService>.ImplementedBy<HomeService>());
}
}
..and a new ControllerFactory to create them:
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IKernel _kernel;
public WindsorControllerFactory(IKernel kernel)
{
_kernel = kernel;
}
public override void ReleaseController(IController controller)
{
_kernel.ReleaseComponent(controller);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
return (IController) _kernel.Resolve(controllerType);
}
}
Finally, you'd create a container and set a new controller factory:
var container = new WindsorContainer().Install(new Installer());
var controllerFactory = new WindsorControllerFactory(_container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
You could also use Ninject or StructureMap.
Suppose we have a controller in MVC,like to this:
public class HomeController : Controller
{
IProductService _productService;
ICategoryService _categoryService;
IUnitOfWork _uow;
public HomeController(IUnitOfWork uow, IProductService productService, ICategoryService categoryService)
{
_productService = productService;
_categoryService = categoryService;
_uow = uow;
}
// ...
}
we use StructureMap for Dependency Injection,and now in Global..asax.cs we have a code like to this:
...
ObjectFactory.Initialize(x =>
{
x.For<IUnitOfWork>().HttpContextScoped().Use(() => new EFCodeFirstContext());
x.ForRequestedType<ICategoryService>().TheDefaultIsConcreteType<EfCategoryService>();
x.ForRequestedType<IProductService>().TheDefaultIsConcreteType<EfProductService>();
});
...
Now my Question is:
for example what time a instance of EfCategoryService be created and assigned to _categoryService?
1- any time we use _categoryService in any method in this controller?
OR
2-Immediately when a request to this controller sended? for example,
www.sitename.com/Home
or
www.sitename.com/Home/News
You should let ASP .NET MVC know that you're using StructureMap for dependency injection.
You can do it by providing an IControllerFactory
Before wiring the routing (in the begining of your program) use this code-
ControllerBuilder.Current.SetControllerFactory(new StractureMapControllerFactory());
Where StructureMapControllerFactory will provide the implementation to use the DI container when instantiating Controllers
I've never done it with StructureMap but I guess someone has already implemented the ContorllerFactory for StructureMap.
I'm using ninject as my IoC and I wrote a role provider as follows:
public class BasicRoleProvider : RoleProvider
{
private IAuthenticationService authenticationService;
public BasicRoleProvider(IAuthenticationService authenticationService)
{
if (authenticationService == null) throw new ArgumentNullException("authenticationService");
this.authenticationService = authenticationService;
}
/* Other methods here */
}
I read that Provider classes get instantiated before ninject gets to inject the instance. How do I go around this? I currently have this ninject code:
Bind<RoleProvider>().To<BasicRoleProvider>().InRequestScope();
From this answer here.
If you mark your dependencies with [Inject] for your properties in your provider class, you can call kernel.Inject(MemberShip.Provider) - this will assign all dependencies to your properties.
I do not understand this.
I believe this aspect of the ASP.NET framework is very much config driven.
For your last comment, what they mean is that instead of relying on constructor injection (which occurs when the component is being created), you can use setter injection instead, e.g:
public class BasicRoleProvider : RoleProvider
{
public BasicRoleProvider() { }
[Inject]
public IMyService { get; set; }
}
It will automatically inject an instance of your registered type into the property. You can then make the call from your application:
public void Application_Start(object sender, EventArgs e)
{
var kernel = // create kernel instance.
kernel.Inject(Roles.Provider);
}
Assuming you have registered your role provider in the config. Registering the provider this way still allows great modularity, as your provider implementation and application are still very much decoupled.
I am using ASP.NET MVC 3 and Autofac for my dependency injection. I am using AutoMapper for my mapping.
I have an IMapper class that I use for all my model view mappings. So any any of my mapping classes can implement this interface. In the controller below the constructor receives an IMapper instance, and in my User controller it might receive a different instance, maybe userMapper. Getting back to the code below, I have a class called NewsMapper and it implements IMapper. How do I setup dependency injection so that this controller must receive an instance of NewsMapper? Please bear in mind that I might have another mapper called UserMapper.
I have the following controller:
public class NewsController
{
private INewsService newsService;
private IMapper newsMapper;
public NewsController(INewsService newsService, IMapper newsMapper)
{
if (newsService == null)
{
throw new ArgumentNullException("newsService");
}
if (newsMapper == null)
{
throw new ArgumentNullException("newsMapper");
}
this.newsService = newsService;
this.newsMapper = newsMapper;
}
}
I have the following configuration in my global.asax.cs:
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<NewsService>().As<INewsService>();
builder.RegisterType<NewsRepository>().As<INewsRepository>();
UPDATED:
My IMapper interface:
public interface IMapper
{
object Map(object source, Type sourceType, Type destinationType);
}
My NewsMapper class:
public class NewsMapper : IMapper
{
static NewsMapper()
{
Mapper.CreateMap<News, NewsViewModel>();
Mapper.CreateMap<NewsViewModel, News>();
}
public object Map(object source, Type sourceType, Type destinationType)
{
return Mapper.Map(source, sourceType, destinationType);
}
}
My controller action method where I do the mappings:
[HttpPost]
public ActionResult Create(NewsViewModel newsViewModel)
{
// Check model state
if (!ModelState.IsValid)
{
return View("Create", newsViewModel);
}
// Do mapping
var news = (News)newsMapper.Map(newsViewModel, typeof(NewsViewModel), typeof(News));
// Add to database via news service
// Redirect to list view
return RedirectToAction("List", "News");
}
The problem here is the broadness of the IMapper contract. It is too general - the NewsController wants to map something like News to NewsViewModel but IMapper just says "maps something to something."
Instead, have a look at creating a generic variant such as IMapper<TFrom,TTo>. Then, you can set up container so that the NewsController receives an IMapper<News,NewsModel> which is unambiguous and should uniquely match the NewsMapper component (however you decide to set that up.)
Edit
For examples/variants on the generic mapper theme see:
http://consultingblogs.emc.com/owainwragg/archive/2010/12/15/automapper-profiles.aspx
http://lucisferre.net/2009/12/31/graphite-update-the-automapfilter-for-model-e280933e-viewmodel-mapping/
Specifying a default Unity type mapping for a generic interface and class pair