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.
Related
So iv just been thrown into some new code at work and its in C#. Now I'm not very familiar with C# and there are some things I really don't understand and the main one is Injecting into methods.
Now there is a WebAPI and it has controller that uses a class named "LocalFileStorage" which is a dependency from another project, the constructor for that class looks like this.
public class LocalFileStorageHandler : IStorageHandler
{
*Bunch of private variables
public LocalFileStorageHandler(DbContext dbContext, IConfiguration configuration, ILogger<LocalFileStorageHandler> logger)
{ code here}
Now in the controller class every method that uses the LocalFileStorage gets it injected as a parameter. Here is a example:
public async Task<IActionResult> ApiMapper([FromBody] dynamic request, [FromServices] IStorageHandler storageHandler)
Also in the project startup.cs we can find this line:
services.AddScoped<IStorageHandler, LocalFileStorageHandler>();
Now my understanding is that for each separate request made the Addscoped makes sure that the method gets its own instance of LocalFileStorage handler. I also understand that the "[FromServices]" attribute causes this instance to be injected into the method. However the one thing I don't understand and cant find anywhere in the code is where the LocalFileStorage objects get their "In parameters" for their constructor?
As by my understanding each injected instance of LocalFileStorage should also receive the parameters:
DbContext dbContext, IConfiguration configuration, ILogger<LocalFileStorageHandler> logger
What am i missing here ?
Kind regards
The DI container injects the dependencies for you. So somewhere, the DbContext, IConfiguration and ILogger has been registered/setup.
Then when you use the FromServices attribute, the DI container will try to resolve the type for you and inject all dependencies (if they are registered, if not, an exception will be thrown)
IConfiguration and ILogger are usually setup when building the host. DbContext are (usually) registered by using the AddDbContext extension method.
Link to configuration for ILogger: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-6.0#logging-providers
Link to configuration for IConfiguration: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-6.0#default-configuration
Link to Dependency Injection fundamentals in ASP.Net Core : https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-6.0
I recently came across AddPooledDbContextFactory concept as part of my NET 5 self-education piece and am keen to implement it properly. However, I am not sure how to use it with generics that I generally use.
Example of my current setup:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<TestDbContext>(
(s, o) => o.UseNpgsql(Configuration.GetConnectionString("DatabaseConnection"))
.UseLoggerFactory(s.GetRequiredService<ILoggerFactory>()));
// other code //
}
my repository generic:
public class Repository<T> : IRepository<T> where T
{
private readonly TestDbContext _dbContext;
public Repository(TestDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task Create(T entity)
{
await _dbContext.Set<T>().AddAsync(entity);
await _dbContext.SaveChangesAsync();
}
// other methods //
}
this is invoked in following manner as example:
public class WeatherForecastController : ControllerBase
{
private readonly IRepository<Test> testRepo;
public WeatherForecastController(IRepository<Test> testRepo)
{
this.testRepo= testRepo;
}
[HttpGet]
public async Task<IEnumerable<WeatherForecast>> GetAsync()
{
await testRepo.Create(new Test { Name = "Superman" });
// other code
}
}
I would like to convert this to use the new AddPooledDbContextFactory concept but cannot find enough documentation to figure out how to do this.
Atm only thing that comes to mind is using statements at each method but that doesn't make sense.
Any advice on this?
Documentation is not yet complete and is in progress, you track this issue
https://github.com/dotnet/EntityFramework.Docs/issues/2523
You can also a look at the tests for AddPooledDbContextFactory to see how to register DbContext with
https://github.com/dotnet/efcore/search?q=AddPooledDbContextFactory
for example to register DbContext:
services.AddPooledDbContextFactory<TContext>(ob =>
ob.UseSqlServer("ConnectionString").EnableServiceProviderCaching(false), poolSize: 32)
Then in your class, inject an IDbContextFactory<TContext> and use it like this:
using(var context = _factory.CreateDbContext())
{
var orders = await context.Orders.Where(o => o.Id > 3).ToListAsync();
}
According to this post:
Note that the DbContext instances created in this way are not managed
by the application's service provider and therefore must be disposed
by the application
You can also check out this post to see how to use IDbContextFactory:
https://learn.microsoft.com/en-us/aspnet/core/blazor/blazor-server-ef-core?view=aspnetcore-5.0
#Aeseir your code looks good to me. You are following best practices and you don't need to change it.
You are using the Repository Pattern, so your Repository class has all of your query logic which helps create loosely coupled and maintainable code.
In your ConfigureServices, calling: services.AddDbContext<TestDbContext>() registers TestDbContext with Scoped service lifetime. This is the way that DbContext is designed to work, and it will also work well with ASP.NET controllers, since they have a Scoped lifetime as well.
You did not show your code for registering IRepository, but that service lifetime should be Scoped as well. Btw, you can tell BuildServiceProvider() to validate scope registrations:
builder.Services.BuildServiceProvider(validateScopes: true);
Since DbContext is designed to have a Scoped service lifetime, and since your IRepository and Controller services are Scoped as well, every request gets brand new:
Controller
IRepository
DbContext
Those services are used for the request and then Diposed. This is how ASP.NET is intended to work.
Apparently at some point, DbContext pooling has been introduced to improve performance. In this case, EF Core manages a pool of context instances for you and resets them after each request. This can improve performance, although in some situations, the benefit might be small. See MSDN documentation for more details.
I think for use with ASP.NET controllers (i.e. the code you posted above) all you need to do to take advantage of EF Core context pooling is call AddDbContextPool():
builder.Services.AddDbContextPool<ApplicationDbContext>(/* ... */);
However, if you needed to use DbContext in services registered with Singleton lifetime, then the pattern above would not work well. Because when a Scoped service gets used in a Singleton service, the Scoped service is essentially a Singleton. Each request would not get a new DbContext, nor a reset one from the pool. (See QUESTION below.)
In that case, you might want to use the DbContext factory pattern instead:
builder.Services.AddDbContextFactory<ApplicationDbContext>(/* ... */);
Or, if you want to use context pooling with a factory pattern:
builder.Services.AddPooledDbContextFactory<ApplicationDbContext>(/* ... */);
The DbContextFactory can then be used in other services through constructor injection. For example:
private readonly IDbContextFactory<ApplicationDbContext> _contextFactory;
public MyController(IDbContextFactory<ApplicationDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
The injected factory can then be used to construct DbContext instances in the controller code. For example:
public void DoSomething()
{
using (var context = _contextFactory.CreateDbContext())
{
// ...
}
}
Keep in mind that when you call CreateDbContext(), context instances are not managed by the service provider and therefore must be disposed by the application. Hence you need to Dispose of them yourself, such as in the example above which does so with the using statement.
QUESTION
I am doing my best to understand this stuff and explain it, but I might be wrong, so please call out an inaccuracies in my post.
When using AddDbContextPool(), does the DbContext get registered as a Singleton or Scoped?
I found in MSDN documentation that it's effectively registered as a Singleton:
Context pooling works by reusing the same context instance across requests; this means that it's effectively registered as a Singleton, and the same instance is reused across multiple requests (or DI scopes). This means that special care must be taken when the context involves any state that may change between requests.
However, I have found that if AddDbContextPool() is used along with true for validatedScopes:
builder.Services.BuildServiceProvider(validateScopes: true)
When DbContext is consumed from another service which is registered as a Singleton, the following exception is thrown:
System.InvalidOperationException: 'Cannot consume scoped service 'ApplicationDbContext' from singleton 'IRepository'.'
Hence why I stated above that DbContext still gets Scoped service lifetime.
It has been well documented, how to inject dependencies into services.
Question: But is it (already) possible in ASP.NET Core 2.0 to have the system's DI mechanism automatically inject a service into a method or into a property?
Sidenote: In PHP-Symfony this pattern is called setter injection.
Example:
Say I have a common MyBaseController class for all controllers in my project and I want a service (e.g. the UserManager service) to be injected into MyBaseController that can be later accessed in all child controllers. I could use constructor injection to inject the service in the child class and pass it via base(userManager) to the parent. But having to perform this in all child constructors of all controllers is pretty tedious.
So I would like to have a setter in MyBaseController like this:
public abstract class MyBaseController : Controller
{
public UserManager<User> userManager { get; set; }
// system should auto inject UserManager here
public void setUserManager(UserManager<User> userManager) {
this.userManager = userManager;
}
}
...so I don't have to do the following in every child constructor just to pass the dependency to the parent:
public class UsersController : MyBaseController
{
public ChildController(UserManager<User> userManager) : base(userManager) {}
Update: The answer given here is what I want to achieve, however the question was asked for ASP.NET Core 1.0, I'm interested in whether any solutions have been added in ASP.NET Core 2.0.
In general it is good advice to avoid non constructor DI as it is considered a bit of an anti pattern, there is a good discussion about it in this related question.
With the default Microsoft.Extensions.DependencyInjection container in aspnet core, the answer is no, but you could swap to something more powerfull like autofac (which has property injection) if you are sure you really need this feature.
You can perform setter injection with the built-in DI container (Microsoft.Extensions.DependencyInjection) using Quickwire. Unlike Autofac, this is not a new DI container, it just extends the default one.
To make it work:
Add this to ConfigureServices
public void ConfigureServices(IServiceCollection services)
{
// Activate controllers using the dependency injection container
services.AddControllers().AddControllersAsServices();
services.ScanCurrentAssembly();
// ...
// Register your other services
}
By default, ASP.NET Core will activate controllers by instantiating them directly without going through the DI container. Fortunately, this behaviour can easily be overridden to force ASP.NET Core to resolve controllers using dependency injection. This is what services.AddControllers().AddControllersAsServices() does.
ScanCurrentAssembly is necessary to get Quickwire to search for services declared in your assembly and register them (which will include your controllers).
Decorate your child controller with [RegisterService]
[RegisterService(ServiceLifetime.Transient)]
public class ChildController : MyBaseController
{
// ...
}
This will make your ChildController discoverable when ScanCurrentAssembly is called in step 1.
Decorate the setter
public abstract class MyBaseController : Controller
{
[InjectService]
public UserManager<User> UserManager { get; private set; }
}
Now the UserManager property in your child controller will be automatically set from the dependency injection container.
You have two kind of DI
Mandatory, it's injection needed for object initialization then it's injection setted in constructor.
Optional, it's injection needed for action.
If DI is doing well, u can have unit test without injection system, if all injections are in ctor then you'll break every unit test, every time for nothing.
So all injections in ctor break open/close principle.
One more point is DI is for interface implementation or module public part, object under this implementation are initialized manually.
So setter is not bad because there are hidden by interface.
I am building an application which uses dependency injection following the options pattern design. I have a custom ORM class named DataManager. When I create an instance of DataManager I inject the connection string into the class as below.
public class DataManager : CommonDataManager {
private readonly ConnectionStrings _connectionStrings;
public DataManager(IOptions<ConnectionStrings> options) {
this._connectionStrings = options.Value;
}
When creating the DataManager object which parameter should I use? What should the initialisation code look like?
DataManager dm = new DataManager(?);
DI integration means that the DI engine is going to be responsible for object creation. So instead of directly initializing your class, you should rather do the following. Register also your DataManager with DI, so you can later instantiate it from serviceCollection:
In your ConfigureServices method add the following:
services.AddTransient<DataManager>();
Note, that you should decide the lifespan of your instance (I've chosen Transient here, but it's up to you - Singletone, Scoped, ...).
And then when you need to instantiate it, call as follows:
serviceProvider.GetRequiredService<DataManager>();
If, however, you want to go with your approach and want to instantiate the DataManager yourself, you should get the parameter from IoC container as follows:
DataManager dm = new DataManager(sp.GetRequiredService<IOptions<ConnectionStrings>>());
Note: that in both cases I assume that you've already registered the options with the DI in your Startup.ConfigureServices method.
Say I've a MVC Core Controller like this:
public class SomeController
{
public SomeController(IConfiguration appConfig, Func<string> someDelegate)
{
}
}
Also, I'm using AutoFac to resolve injections. Object injections are working flawlessly while adding a delegate injection produces an ASP.NET Core exception which tells that Func<string> can't be injected because there's no component to inject with such type.
When I try to manually resolve SomeController using AutoFac I get the desired behavior.
Is there any way to support this scenario without using AutoFac to resolve controllers?
Controllers are not resolved via DI by default, they are constructed in the DefaultControllerFactory or so.
Update
Microsoft.Extensions.DependencyInjection doesn't support named components, discovery, auto registrations, decorators etc.
It's meant to be simple out of the box IoC and provide the base for DI for basic applications and offer easy way for 3rd party IoC containers (with advanced features such as auto discovery, decorators etc.) to be integrated (basically all they need is process the information in IServiceCollection and return their own implementation of IServiceProvider from Configure method).
Tag helpers, controllers and view components are different in this aspect as they have their own activators (the default one use activation utilities, which at some point further down the pipeline use the service provider). For that reasons AddControllersAsServices exists, because it replaces DefaultControllerActivator (which uses ActivationUtilities, see DefaultControllerActivator.cs) with ServiceBasedActivator (which uses IServiceProvider, see ServiceBasedControllerActivator).
Also see this related answer for details on how to resolve controllers, tag helpers and view components via DI.
var builder = services
.AddMvc()
.AddControllersAsServices() // this one for your case
.AddViewComponentsAsServices()
.AddTagHelpersAsServices();
I was just run into this issue myself so I thought I would share for future reference as I had one case where I wanted to resolve a delegate but including an additional library seemed like overkill.
Given the following defintions:
public interface ISomething { /*...*/ };
public interface ISomeService { /*...*/ }
public class SomeService : ISomeService { /*...*/ }
public class Something
{
public Something(ISomeService service, string key) { /*...*/ }
}
// I prefer using a delegate for readability but you
// don't have to use one
public delegate ISomething CreateSomething(string key);
The delegate can be registered like this:
var builder = services
.AddSingleton<ISomeService, SomeService>()
.AddTrasient<CreateSomething>(provider => key => new Something(provider.GetRequiredService<ISomeService>(), key));