I'm starting to use LightInject in my MVC application, but I'm a bit confused as to how to implement an instance of a DB Context class.
I know I can just inject it via a constructor... but what's the point of LightInject if I have to do this.
Also, the DB Context class in my application already implements an interface (IdentityDbContext) so it somehow doesnt seem right to create another interface for the repository.
The DB Context class does have this in the constructor:
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
So I can quite easily call:
_context.Create()
In a constructor - but my understand is, this would go against SOLID principles.
So how does one use LightInject to create an instance of a database context class?
You can register the context and then pass it with constructor injection:
In you ioc configuration file:
container.Register<Context, Context>(new PerScopeLifetime());
Some service:
private readonly Context _context;
public BookService(Context context)
{
_context = context;
}
If you want to use an interface, then register an interface and pass it everywhere you want.
Related
I use repository method to get all data from DB.
Here is the code of it:
public class ExperienceRepository
{
private readonly ToSeeDatabaseContext _context;
public ExperienceRepository(ToSeeDatabaseContext context)
{
_context = context;
}
public List<Experience> GetAllExperiences()
{
return _context.Experience.ToList();
}
}
I need to call GetAllExperience from controller.
So at first I need to declare repo as private property
I do it like this
private ExperienceRepository _exprepo = new ExperienceRepository();
But it says, it need
Severity Code Description Project File Line Suppression State
Error CS7036 There is no argument given that corresponds to the required formal parameter 'context' of 'ExperienceRepository.ExperienceRepository(ToSeeDatabaseContext)' TooSeeWeb C:\Users\EugeneSukhomlyn\source\Workspaces\TooSee\Too See Web\Too See Web\Too See Web\Controllers\ExperienceController.cs 14 Active
How I can solve it?
Since you are using dependency injection, the preferred way would be to inject the DB context to the repository.
You probably have already code similar to this in the ConfigureServices method of your Startup.cs file (or in the place where you configure your service collection if you are not using ASP.NET Core) to set up the context for dependency injection (if you don't you should add it):
services.AddDbContext<ToSeeDatabaseContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("ToSeeDatabaseContext")));
Since your experience repository already accepts ToSeeDatabaseContext, it is already ready for dependency injection. Now you have to inform the DI framework about ExperienceRepository, so that it can inject it to its consumers. So in ConfigureServices you add:
services.AddTransient<ExperienceRepository, ExperienceRepository>();
Now can use dependency injection whenever you want to need the repository. In your consumer (eg. an ASP.NET page) you can use constructor injection to get a repository:
class MyExperienceConsumer {
private ExperienceRepository _exprepo;
public MyExperienceConsumer(ExperienceRepository exprepo) {
_exprepo = exprepo;
}
}
If your consumer is an ASP.NET page controller, this is all you need to do, since the MVC framework will create the controller for you and use DI to give you the repository. If you need to instantiate the consumer yourself you need to do so with the help a service provider from the DI framework, so that it can do its magic (assuming you have a service collection). When you use ActivatorUtilities, the DI framework will inject the repository into the constructor:
IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
MyExperienceConsumer consumer =
ActivatorUtilities.CreateInstance<MyExperienceConsumer>(serviceProvider);
In any case, you can use the DI framework to do the heavy lifting for you.
Your ExperienceRepository class have one constructor that requires a ToSeeDatabaseContext as parameter.
You are trying to create a instance ExperienceRepository with no parameters. The compiler can't find a constructor which doesn't take any parameters, producing the compiler error.
I've created two projects:
Web Project, that contains all the viewmodels/data/controllers etc. And a Web Api project to allow form capture.
I simply want to capture the data in the web Api and save it to the database where it will become accessible to the front end.
I am experiencing an issue initialzing the DBcontext within the Api controller and need help.
namespace ZebraCRM.API2.Controllers
{
[Route("api/[controller]")]
public class LeadsController : Controller
{
private readonly ApplicationDbContext _context;
public LeadController(ApplicationDbContext context)
{
_context = context;
}
// POST api/values
[HttpPost]
public void Post(Lead formData)
{
formData.DateCreated = DateTime.Now;
_context.Lead.Add(formData);
_context.SaveChanges();
}
}
The above idea was taken from the controller in the main web project, but is obviously not the right approach in this situation.
the debug outputs the following
System.InvalidOperationException: Unable to resolve service for type 'ZebraCRM.Web.Data.ApplicationDbContext' while attempting to activate 'ZebraCRM.API2.Controllers.LeadsController'.
The framework doesn't know how to constructor a LeadController because it doesn't know how to satisfy the ApplicationDbContext context parameter when it calls the constructor. To solve this, you could simply assign the value as part of your constructor, eliminating the parameter.
namespace ZebraCRM.API2.Controllers
{
[Route("api/[controller]")]
public class LeadsController : Controller
{
private readonly ApplicationDbContext _context;
public LeadController()
{
_context = new ApplicationDbContext();
}
// POST api/values
[HttpPost]
public void Post(Lead formData)
{
formData.DateCreated = DateTime.Now;
_context.Lead.Add(formData);
_context.SaveChanges();
}
}
}
Or, if you do want to leave the constructor parameter in place, you'll need a DI container such as AutoFac or Ninject. If you're new to DI, I suggest you watch this video. When you set things up for Dependency Injection, you will basically pass something to the framework that says "When constructing an object that needs an X, here's how you give it an X". This allows you to better follow SOLID principles. An object can demand an IRepository (an interface) and as part of your DI setup you can say "when an object demands an IRepository, I want to pass it a SqlServerRepository (which would implement IRepository)". Then if you later decided to switch to MySQL, you could modify the setup to use a MySqlRepository instead of SqlServerRepository, without needing to modify anything about the controller, since the controller would use the repository via the interface.
I was looking to implement the Repository pattern with unit of work in my asp.net core 2.0 app. There are not many examples of doing this using the dependency injection (DI) principle being used in .net core. I tried to rewrite this example found in the docs. I also tried to add async operations where I could.
Now the idea is that the unit of work passes it's own dbcontext to the GenericRepository for each of the entities that is in use. This then makes sure that you ony use one dbcontext even if you work on two entities.
In my controller I fetch some data like this:
var model = new IndexViewModel
{
Companies = await _unitOfWork.CompanyRepository.GetAsync()
};
In my unit of work the dbcontext is being created using DI. Then it creates new instances of the GenericRepository for each entity while it passes it's dbcontext to the genericrepository's contructor:
private ApplicationDbContext _context;
private GenericRepository<Company> companyRepository;
private GenericRepository<Person> personRepository;
public UnitOfWork(ApplicationDbContext context)
{
_context = context;
}
public GenericRepository<Company> CompanyRepository
{
get
{
if (this.companyRepository == null)
{
this.companyRepository = new GenericRepository<Company>(_context);
}
return companyRepository;
}
}
//repeat for Person
But I fear the dependency injection will automatically create a new dbcontext for each time I use the GenericRepository.
public class GenericRepository<TEntity> where TEntity : class
{
internal ApplicationDbContext _context;
internal DbSet<TEntity> dbSet;
public GenericRepository(ApplicationDbContext context)
{
_context = context;
dbSet = context.Set<TEntity>();
}
//...other methods
}
I fear this will actually create two contexts. one for each(if two requests are made)? So in reality the dbcontext would be instantiated three times, one in unitofwork then one for each repository? Here is a link to the app on github. It is working, but I want to understand how this works. Thanks for any answers!
It all depends on how you register your DbContext and which lifetime you use.
The default overload of .AddDbContext will always register the DbContext with scoped lifetime. That means it will create one instance per request.
If you make it transient, it will create one instance per resolve and singleton one instance per application lifetime.
That should be true for most cases.
However, if you have a service which has a higher lifetime than its dependencies (i.e. a singleton and inject a scoped service), then the above is not true and you have to be careful when you design and do your registration and take this into consideration.
I am building a throwaway application in MVC 6 and experimenting with different architectures for dependencies.
The problem I am facing is how to create a custom 'MyAppContext' object specific to the Application. This would require some information from the HttpContext and some information from the database, and will be a request-scoped repository for application specific attributes. I want to pass the instance of the HttpContext into the constructor of the 'MyAppContext'.
I have successfully created a 'DataService' object with an IDataService interface using DI and this works Ok.
The difference with the 'MyAppContext' class is that it has two parameters in the constructor - the 'DataService' and the Microsoft.AspNet.Http.HttpContext. Here is the MyAppContext class:
public class MyAppContext : IMyAppContext
{
public MyAppContext(IDataService dataService, HttpContext httpContext)
{
//do stuff here with the httpContext
}
}
In the startup code, I register the DataService instance and the MyAppContext instance:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
//adds a singleton instance of the DataService using DI
services.AddSingleton<IDataService, DataService>();
services.AddScoped<IMyAppContext, MyAppContext>();
}
public void Configure(IApplicationBuilder app)
{
app.UseErrorPage();
app.UseRequestServices();
app.UseMvc(routes => /* routes stuff */);
}
I am expecting the HttpContext parameter in the constructor to get resolved by DI.
When running the code, this is the exception I get returned:
InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNet.Http.HttpContext' while attempting to activate 'MyAppContext'
I figure this is because there is no specific instance of HttpContext that this error is occurring, but I don't know how to register the HttpContext instance in DI. I added the line 'app.UseRequestServices();' but this hasn't made any difference. I also tried a variant of:
services.AddScoped<HttpContext, HttpContext>();
But this fails because the second HttpContext is supposed to be an instance - I know it's not correct but haven't been able to work out what is.
So, in summary - how can I pass in the HttpContext object into the constructor of MyAppContext?
Inject IHttpContextAccessor in the constructor
By injecting an HttpContext into your component you are violating the SOLID principles. To be more specifically, you are violating:
The Dependency Inversion Principle (DIP) because you depend on a framework type (the HttpContext).
The Interface Segregation Principle (ISP) because the HttpContext has many methods, while the consumer never uses them all.
Both violations make it much harder to test your code. Although you can instead inject the IHttpContextAccessor as #victor suggests, this is still a violation of both the DIP and ISP, because this is an abstraction that is provided by the framework and you still depend on HttpContext. According to the DIP it is the client who should define the abstraction. This causes your code to be needlessly coupled to the framework.
Instead you should strive to specify narrow role interfaces; interfaces that do one specific thing for you that is specific to the needs of your application. Injecting a big dictionary with string values (as what HttpContext is, is never very specific). From your question it's unclear what kind of data you need from our MyAppContext, but I expect something like information of the currently logged in user. For this you can define a specific IUserContext abstraction, for instance:
public interface IUserContext {
IPrincipal CurrentUser { get; }
}
An adapter that connects the application to the ASP.NET framework can be easily created for this abstraction:
sealed class AspNetUserContextAdapter : IUserContext {
private readonly IHttpContextAccessor accessor;
public AspNetUserContextAdapter(IHttpContextAccessor accessor) {
this.accessor = accessor;
}
public IPrincipal CurrentUser => accessor.HttpContext.User;
}
This adapter does depend on IHttpContextAccessor, but this is okay, since the adapter is an infrastructural component located in the Composition Root. There are serveral ways to register this class, for instance:
services.AddSingleton<IUserContext, AspNetUserContext>();
In the startup class:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.Extensions.DependencyInjection;
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddMvcCore();
}
In the controller:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Core;
private readonly IHttpContextAccessor _httpContextAccessor;
public ServerSentEventController(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
Why would you pass the HttpContext in the constructor?
Why not directly access it wherever you want?
public MyAppContext(IDataService dataService)
{
HttpContext mycontext = HttpContext.Current;
//do stuff here with mycontext
}
I have the following configuration:
builder.Register<EntityContext>().As(
c=> {
var entityCtx = new EntityContext();
//snip snip: some config stuff
return entityCtx;
}).As<MyDbContext>().InstancePerLifetimeScope();
EntityContext obviously inherits from MyDbContext which again inherits from DbContext.
In my repository's constructor I'm usually using them as
...
public MyRepository(MyDbContext context) {...}
This ensures to have one ctx per http request which is what I want. But now I have the need that for a specific repository I want to have a different instance of the EntityContext than is normally being used.
How would you achieve that? The following came to my mind:
Another config where I use Register<EntityContext>()....As<DbContext>()...
Any other ideas?
I just found a proper solution that could work, namely to use the OwnedInstances of AutoFac if I understood correctly. For example:
class MyRepository
{
public MyRepository(Owned<MyDbContext> context, MyDbContext context2)
{
//context and context2 are two different object instances
}
}
This scenario is especially useful if you'd like MyRepository to run in a different transaction, i.e. it needs to have a different DbContext instance than the other repositories.
Once you have different lifetime requirement you must either use another config or manually call container to give you a new instance of the context (or create context instance directly but that is something you don't want to do when using IoC container).