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.
Related
I have a Dnn module project for a Web API. In the startup I want to configure dependency injection for this module.
The error I get is: "An error occurred when trying to create a controller of type 'SchoolsController'. Make sure that the controller has a parameterless public constructor."
Inner exception is a bit clearer on the actual problem: "Unable to resolve service for type 'MyProj.Persistence.Context.PortfolioContext' while attempting to activate 'MyProj.Persistence.UnitOfWork.UnitOfWork'."
My guess is that this is because my UnitOfWork class constructor requires a DbContext?
My unit-of-work class looks like this:
public class UnitOfWork: IUnitOfWork, IDisposable
{
private readonly PortfolioContext context;
public UnitOfWork(PortfolioContext context)
{
this.context = context;
Schools = new SchoolRepository(context);
Users = new UserRepository(context);
}
}
My controller looks like this:
public class SchoolsController: BaseController
{
private readonly IUnitOfWork unitOfWork;
public SchoolsController(IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
}
}
My startup class:
public class Startup : IDnnStartup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IUnitOfWork, UnitOfWork>();
}
}
I've tried to add the DbContext before adding the scoped UnitOfWork like this, but it didn't work:
services.AddSingleton<DbContext, PortfolioContext>((ctx) =>
{
return new PortfolioContext();
});
Any ideas how to do this in Dnn? Normally I would add the DBContext by doing something like this:
services.AddDbContext<DataContext>(x => x.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
But for some reason I cannot find the extension method AddDbContext anywhere in this context (Dnn, IDnnStartup, etc.)
EDIT:
Forgot to add that this is for Dnn 9.6.2 which targets .net 4.7.2
Turns out I wasn't far off.
Where I originally tried this:
services.AddSingleton<DbContext, PortfolioContext>((ctx) =>
{
return new PortfolioContext();
});
Should have actually been this:
services.AddSingleton<PortfolioContext>((ctx) =>
{
return new PortfolioContext();
});
I have the following unit of work pattern set up for an MVC 5 application using Entity Framework. The unit of work has all the repos defined as follows so that they are all using the same dbcontext and it has one save method to co-ordinate the transaction using the same context:
public class UnitOfWork : IUnitOfWork
{
private readonly ApplicationDbContext _context;
public IProductRepository ProductRepository { get; private set; }
public ICustomerRepository CustomerRepository { get; private set; }
// Other reposistories
public UnitOfWork(ApplicationDbContext context)
{
_context = context;
ProductRepository = new ProductRepository(_context);
CustomerRepository = new CustomerRepository(_context);
// Other reposistories
}
public void Complete()
{
_context.SaveChanges();
}
}
This is an example of my repo. The reason for using repos is for code re-use so that I'm not duplicating queries inside different controllers.
public class ProductRepository : IProductRepository
{
private readonly ApplicationDbContext _context;
public ProductRepository(ApplicationDbContext context)
{
_context = context;
}
public Product GetProduct(int productId)
{
return _context.Ticket.SingleOrDefault(p => p.Id == productId);
}
public void Add(Product product)
{
_context.Product.Add(product);
}
// Other methods
}
I inject the unit of work class in my controller as follows using Ninject:
public class ProductsController : Controller
{
private readonly IUnitOfWork _unitOfWork;
private readonly IFileUploadService _FileUploadService;
public ProductsController(IUnitOfWork unitOfWork,
IFileUploadService fileUploadService)
{
_unitOfWork = unitOfWork;
_FileUploadService = fileUploadService;
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CreateEditProductViewModel viewModel)
{
var product = new Product
{
// Do stuff
};
_unitOfWork.ProductRepository.Add(product);
// Call file upload service
_fileUploadService.Upload();
_unitOfWork.Complete();
}
}
This unit of work set up works fine if all I'm using are repos that are defined in the unit of work class. But now I want to use a service class to process some additional application logic and then the unit of work is committed in the controller action. If I define the class as follows it will be using a different instance of the context, In which case how would you co-ordinate a transaction where the service layers is ending up with a different context?
public class FileUploadService : IFileUploadService
{
private readonly IUnitOfWork _unitOfWork;
public FileUploadService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public uploadResult Upload()
{
// Some stuff
var uploadedFile = new UploadedFile
{
//some stuff
};
_unitOfWork.UploadedFileRepository.Add(uploadedFile);
}
}
I've done quite a bit of research online and I'm unable to find any resource that provides a practical example to solve this problem. I've read quite a bit of stuff on ditching unit of work and repos and simply using entity frameworks dbset. However as explained above the purpose of using
repos is to consolidate queries. My questions is how do I co-ordinate the unit of work with a service class.
I would like the service to use the same context so that it can access the repositories it needs to work with, and let the controller (client code) commit the operation when it see fits.
* UPDATE *
In my DI Container I resolve all interfaces using the following snippet:
private static IKernel CreateKernel()
{
RegisterServices(kernel);
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();
// default binding for everything except unit of work
kernel.Bind(x => x.FromAssembliesMatching("*")
.SelectAllClasses()
.Excluding<UnitOfWork>()
.BindDefaultInterface());
return kernel;
}
Would adding the line kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope(); ensure that no more than one ApplicationDbContext is created, even if the request ends up hitting multiple controllers or service layers that all require an IUnitOfWork (ApplicationDbContext)?
If you are using MVC, then your unit of work is your web request. If I were you I'd ditch the UOW implementation and just make sure you dbcontext is instantiated in the Application_BeginRequest. Then I'd stuff it into the HttpContext for safe keeping. On Application_EndRequest, I dispose of the DbContext.
I would move the save to your repository.
I'd create a [Transaction] attribute that would maintain a TransactionScope something like this:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class TransactionAttribute : ActionFilterAttribute
{
private TransactionScope Transaction { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Transaction = new TransactionScope( TransactionScopeOption.Required);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Exception == null)
{
Transaction.Complete();
return;
}
Transaction.Dispose();
}
}
You can then just tag your controller methods with [Transaction].
I'm just spitballing here, but I do something similar with NHibernate instead of EF and it works out nicely for me.
The InRequestScope() will create a new instance of the bound type on every new web request, and at the end of that web request, it will Dispose that instance if it is disposable.
I am not sure how are you passing the ApplicationDbContext into your UnitOfWork. I am assuming that you use Ninject for this injection too. Just make sure that you bind your ApplicationDbContext using the InRequestScope()Bind.To().InRequestScope();.
This way, your ApplicationDbContext instance will be created once per request and disposed at the end.
Also, the use of InRequestScope is for types that are disposable, so you can also release resoruces in the Dispose method of your UnitOfWork method too.
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.
This might be a strange case but I want to sometimes reuse the same instance when getting exports with MEF and sometimes create a new.
Basicly I have a WCF service class the is instance per call. Each instance imports a RepositoryFactory which will also be new instance per service class. I return a Repository in the Factory and a repository gets a IDbContext injected.
I want each instance of the Factory to inject the same instance of IDbContext but have seperate instances between Factory instances.
So:
1) Factory1 is created
2) Factory1 creates Repository1-1 that gets IDbContext1 injected
3) Factory1 creates Repository1-2 that gets IDbContext1 injected
4) Factory2 is created
5) Factory2 creates Repository2-1 that gets IDbContext2 injected
6) Factory2 creates Repository2-2 that gets IDbContext2 injected
This should ensures that Repositories created from the same factory share a Unit of Work.
But being new to MEF I'm not sure how I would go about doing that.
EDIT
This is what I got:
public class RepositoryFactory
{
private readonly CompositionContainer _container;
[Import(RequiredCreationPolicy=CreationPolicy.NonShared)]
private readonly IDbContext _context;
public IRepository<T> CreateRepository<T>() where T : class, IEntity
{
//Somehow add the _context instance into the Repository import
return _container.GetExportedValue<EntityRepository<T>>();
}
}
and then
public class EntityRepository<T> : IRepository<T> where T : class, IEntity
{
// Perhaps a contract name might help!!
[Import(RequiredCreationPolicy=CreationPolicy.Shared)]
protected readonly IDbContext _context;
You cannot accomplish this with MEF; no matter what you do the MEF container will not act correctly as a Unit of Work manager for you, it just isn't made for this.
You should attempt to explicitly code a Unit of Work infrastructure for your DAL to consume. Your repositories should explicitly ask the a Unit of Work Manager to provide a current Unit of Work and with it the appropriate context.
Take a look at the code in NCommon https://github.com/riteshrao/ncommon; you can refactor the Unit of Work features to serve your needs.
OK here is a solution I came up with but haven't tried. It's somewhat simple and actually works around MEF but doesn't really break it, at least not in my case.
Add to IRepository class the following method:
void SetContext(IDbContext context);
or better yet
IDbContext context { set; }
and in the factory:
public class RepositoryFactory
{
private readonly CompositionContainer _container;
[Import(RequiredCreationPolicy=CreationPolicy.NonShared)]
private readonly IDbContext _context;
public IRepository<T> CreateRepository<T>() where T : class, IEntity
{
IRepository<T> repo = _container.GetExportedValue<EntityRepository<T>>();
repo.context = _context;
return repo;
}
}
And the rest should be self explanatory:
public class EntityRepository<T> : IRepository<T> where T : class, IEntity
{
protected IDbContext _context;
IDbContext context
{
set { _context = value; }
}
public virtual IQueryable<T> GetQuery()
{
return _context.Set<T>();
}
public virtual T GetById(Guid id)
{
return _context.Set<T>().Find(id);
}
public virtual void SaveOrUpdate(T entity)
{
if (_context.Set<T>().Find(entity.Id) == null)
{
_context.Set<T>().Add(entity);
}
_context.SaveChanges();
}
public virtual void Delete(T entity)
{
_context.Set<T>().Remove(entity);
_context.SaveChanges();
}
}
If you are using it in the same way as I am I can't really see a problem with this implementation. The factory is responsible for creating the class so It can be responsible for setting the context. The CreationPolicy should ensure that each Factory gets it's own instance of DbContext that it then relegates to it's Repositories so they share a context.
In a MVC3-application with Ninject.MVC 2.2.0.3 (after merge), instead of injecting repostories directly into controllers I'm trying to make a service-layer that contain the businesslogic and inject the repostories there. I pass the ninject-DependencyResolver to the service-layer as a dynamic object (since I don't want to reference mvc nor ninject there). Then I call GetService on it to get repositories with the bindings and lifetimes I specify in NinjectHttpApplicationModule. EDIT: In short, it failed.
How can the IoC-container be passed to the service-layer in this case? (Different approaches are also very welcome.)
EDIT: Here is an example to illustrate how I understand the answer and comments.
I should avoid the service locator (anti-)pattern and instead use dependency injection. So lets say I want to create an admin-site for Products and Categories in Northwind. I create models, repositories, services, controllers and views according to the table-definitions. The services call directly to the repositories at this point, no logic there. I have pillars of functionality and the views show raw data. These bindings are configured for NinjectMVC3:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ICategoryRepository>().To<CategoryRepository>();
kernel.Bind<IProductRepository>().To<ProductRepository>();
}
Repository-instances are created by ninject via two layers of constructor injection, in the ProductController:
private readonly ProductsService _productsService;
public ProductController(ProductsService productsService)
{
// Trimmed for this post: nullchecks with throw ArgumentNullException
_productsService = productsService;
}
and ProductsService:
protected readonly IProductRepository _productRepository;
public ProductsService(IProductRepository productRepository)
{
_productRepository = productRepository;
}
I have no need to decouple the services for now but have prepared for mocking the db.
To show a dropdown of categories in Product/Edit I make a ViewModel that holds the categories in addition to the Product:
public class ProductViewModel
{
public Product Product { get; set; }
public IEnumerable<Category> Categories { get; set; }
}
The ProductsService now needs a CategoriesRepository to create it.
private readonly ICategoryRepository _categoryRepository;
// Changed constructor to take the additional repository
public ProductsServiceEx(IProductRepository productRepository,
ICategoryRepository categoryRepository)
{
_productRepository = productRepository;
_categoryRepository = categoryRepository;
}
public ProductViewModel GetProductViewModel(int id)
{
return new ProductViewModel
{
Product = _productRepository.GetById(id),
Categories = _categoryRepository.GetAll().ToArray(),
};
}
I change the GET Edit-action to return View(_productsService.GetProductViewModel(id)); and the Edit-view to show a dropdown:
#model Northwind.BLL.ProductViewModel
...
#Html.DropDownListFor(pvm => pvm.Product.CategoryId, Model.Categories
.Select(c => new SelectListItem{Text = c.Name, Value = c.Id.ToString(), Selected = c.Id == Model.Product.CategoryId}))
One small problem with this, and the reason I went astray with Service Locator, is that none of the other action-methods in ProductController need the categories-repository. I feel it's a waste and not logical to create it unless needed. Am I missing something?
You don't need to pass the object around you can do something like this
// global.aspx
protected void Application_Start()
{
// Hook our DI stuff when application starts
SetupDependencyInjection();
}
public void SetupDependencyInjection()
{
// Tell ASP.NET MVC 3 to use our Ninject DI Container
DependencyResolver.SetResolver(new NinjectDependencyResolver(CreateKernel()));
}
protected IKernel CreateKernel()
{
var modules = new INinjectModule[]
{
new NhibernateModule(),
new ServiceModule(),
new RepoModule()
};
return new StandardKernel(modules);
}
So in this one I setup all the ninject stuff. I make a kernal with 3 files to split up all my binding so it is easy to find.
In my service layer class you just pass in the interfaces you want. This service class is in it's own project folder where I keep all my service layer classes and has no reference to the ninject library.
// service.cs
private readonly IRepo repo;
// constructor
public Service(IRepo repo)
{
this.repo = repo;
}
This is how my ServiceModule looks like(what is created in the global.aspx)
// ServiceModule()
public class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<IRepo>().To<Repo>();
}
}
Seee how I bind the interface to the repo. Now every time it see that interface it will automatically bind the the Repo class to it. So you don't need to pass the object around or anything.
You don't need worry about importing .dll into your service layer. For instance I have my service classes in their own project file and everything you see above(expect the service class of course) is in my webui project(where my views and global.aspx is).
Ninject does not care if the service is in a different project since I guess it is being referenced in the webui project.
Edit
Forgot to give you the NinjectDependecyResolver
public class NinjectDependencyResolver : IDependencyResolver
{
private readonly IResolutionRoot resolutionRoot;
public NinjectDependencyResolver(IResolutionRoot kernel)
{
resolutionRoot = kernel;
}
public object GetService(Type serviceType)
{
return resolutionRoot.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return resolutionRoot.GetAll(serviceType);
}
}