How can I inject this:
private readonly CarModelsController _carModelsController;
public AdminController(CarModelsController carModelsController)
{
_carModelsController = carModelsController;
}
When the CarModelsController looks like this:
[ApiController]
public class CarModelsController : ControllerBase
{
private readonly ApplicationDbContext _context;
public CarModelsController(ApplicationDbContext context)
{
_context = context;
}
I need to have the DbContext when I inject it? Should it be done in another way? What's the correct way to go here? I've never learned this.
I would advise you review the choice of injecting controllers into each other.
Create a service abstraction and class that holds the Db related actions
public interface IDataService {
//...expose desired members
}
public class DataService: IDataService {
private readonly ApplicationDbContext context;
public DataService(ApplicationDbContext context) {
this.context = context;
}
//...other members implemented
}
and inject that into your controllers.
public class AdminController: ControllerBase {
private readonly IDataService service;
public AdminController(IDataService service) {
this.service = service
}
//...
}
[ApiController]
public class CarModelsController : ControllerBase
private readonly IDataService service;
public CarModelsController(IDataService service) {
this.service = service
}
//...
}
All that is left is to register all dependencies with the DI container at startup in the composition root.
Assuming default .Net Core DI
services.AddDbContext<ApplicationDbContext>(...);
services.AddScoped<IDataService, DataService>();
Reference Dependency injection in ASP.NET Core
You nedd to inject your dependencies into the startup class ConfigureServices method.
public void ConfigureServices (IServiceCollection services) {
services.AddScoped<DbContext, Your_Project_DbContext> ();
services.AddScoped<Your_Interface, Your_Concrete_Class> ();
}
Related
The ApiAuthorizationDbContext is my default DBContext.
Now how do I get this context in a controller?
I can create the normal DBContext with new DbContext() but with the ApiAuthorizationDbContext I have to give options where I don't know how to get them.
My ApiAuthorizationDbContext:
public class ApplicationDbContext : ApiAuthorizationDbContext<ApplicationUser>
{
public DbSet<Tenant> Tenants { get; set; }
public DbSet<SiteSettings> SiteSettings { get; set; }
public ApplicationDbContext(
DbContextOptions options,
IOptions<OperationalStoreOptions> operationalStoreOptions) : base(options, operationalStoreOptions)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
AppSettings.DbOptions(optionsBuilder);
}
}
}
My Controller:
public class TenantHelper
{
private readonly ApplicationDbContext _context;
public TenantHelper(ApplicationDbContext context)
{
_context = context;
}
public static List<Tenant> GetAllTenants()
{
List<Tenant> tenants = new List<Tenant>();
tenants = _context.Tenants.ToList();
return tenants;
}
}
Dependency injection in your application allows you to use any object as a set of functionalities that can be reused by multiple objects, to do add your database context as an dependency injection you should add it to the Startup.cs file that the .NetCore Web Applications default template creates.
using Microsoft.Extensions.DependencyInjection;
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>();
}
Now your context can be injected in any object constructor of your application and you can save it as a property to be accessed at any point of that object scope
public class MyController
{
private readonly ApplicationDbContext _dbContext;
public MyController(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
private void MethodA()
{
//accessing dbcontext
_dbContext.MyTable.ToList();
}
}
Edit: Apparently OP meant that he wants his TenantHelper to accessible on all his application, still is a problem that dependency injection solves but just a quick rework needs to be done.
public void ConfigureServices(IServiceCollection services)
{
//This adds your object as a reusable set of functions that is initialized for every different request
services.AddScoped<TenantHelper>();
}
Now do the same process to inject your TenantHelper in your other code as you did on the ApplicationDbContext
Create library with you ApiAuthorizationDbContext and put reference from this lib in your project with controller
I need to inject dependency in Startup.cs
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddTransient<IAppService, AppService>();
//how to inject for the rest
}
}
to achieve the line below:
new AppService(new CacheRepository(new ConfigRepository()))
instead of below or others
new AppService(new ConfigRepository())
Decorator pattern with multiple implementation below:
public class ConfigRepository : IRepository
{
public async Task<IEnumerable<Data>> ReadDataAsync()
{
//...
}
}
public class CacheRepository : IRepository
{
private readonly IRepository _pository;
public CacheConfigRepository(IRepository repository)
{
_pository = repository;
}
public async Task<IEnumerable<Data>> ReadDataAsync()
{
//...
}
}
Environment:
.Net Core 2.2, Azure Functions
Update
Answer:
Thanks #Timo for providing the link below
https://github.com/khellang/Scrutor
How to overwrite a scoped service with a decorated implementation?
For decorating a service with a decorated cached service you can use the Scuter.
Example:
public class MyService : IMyService
{
private readonly IRepository _repository;
public MyService(IRepository repository)
{
_repository = repository;
}
public async Task<IEnumerable<Data>> ReadDataAsync()
{
//...
}
}
The decorated cache service:
public class MyCacheService : IMyService
{
private readonly IMyService _myService;
private readonly ICacheRepository _cacheRepository;
public MyCacheService(IMyService myService, ICacheRepository cacheRepository)
{
_myService = myService;
_cacheRepository = cacheRepository;
}
public async Task<IEnumerable<Data>> ReadDataAsync()
{
var cachedKey = "SomeKey";
(isCached,value) = await _cacheRepository.ReadDataAsync();
if (isCached)
retrun value;
var result = await _myService.ReadDataAsync();
return result;
}
}
Startup.cs:
services.AddSingelton<IMyService, MyService>();
services.Decorate<IMyService, MyCacheService>();
Note that in this example I've added a different interface ICacheRepository.
Contrary to popular belief, the decorator pattern is fairly easy to implement using the built-in container.
By using the extension methods in the linked answer, registering decorators becomes as simple as this:
public void ConfigureServices(IServiceCollection services)
{
// First add the regular implementation
services.AddTransient<IRepository, ConfigRepository>();
// Wouldn't it be nice if we could do this...
services.AddDecorator<IRepository>(
(serviceProvider, wrappedRepository) => new CacheConfigRepository(wrappedRepository));
// ...or even this?
services.AddDecorator<IRepository, CacheConfigRepository>();
}
IHttpContextAccessor is used in a utility class inside a constructor, how do i create an instance of that class in the controller.
// Utility class
public class DBRepository
{
private readonly IHttpContextAccessor _contextAccessor;
public DBRepository(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
}
I have done this and I'm stuck in my controller.
public class HomeController : Controller
{
using (DBRepository DBRepo=new DBRepository())
{
}
}
How to a pass on a IHttpContextAccessor parameter to the utility class
You can create an interface for your class and have the Dependency Injection framework inject it for you.
public interface IDbRepository
{
// Add methods signature here
public List<Post> GetPosts();
}
public class DBRepository : IDbRepository
{
private readonly IHttpContextAccessor _contextAccessor;
public DBRepository(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
//implement those methods
public List<Post> GetPosts()
{
// to do : return a list of Posts
}
}
Now map this interface to your concrete implementation in the ConfigureServices method of Startup class
services.AddTransient<IDbRepository , DBRepository>();
Now you can use constructor injection in your controller
public class HomeController : Controller
{
private readonly IDbRepository repository;
public HomeController (IDbRepository repository)
{
this.repository = repository;
}
public ActionResult Index()
{
var posts = this.repository.GetPosts();
//to do : Return something
}
}
Now sure, why you want to access HttpContext in a data access method/class. HttpContext is more of a web layer stuff. Instead of directly passing the HttpContext thing, you can perhaps pass only the needed value to the data access method. This way you can keep the data access layer not rely too much on Httpcontext , which is web stuff
Registering DbContext in ASP.NET MVC Application as InstancePerRequest. (IoC Autofac)
builder.RegisterType<ADbContext>().As<IADbContext>().InstancePerRequest();
Using inside BService
public class BService : IBService
{
readonly IADbContext _dbContext;
public BService(IADbContext dbContext)
{
_dbContext = dbContext;
}
}
Trying to register IBService as Singleton.
builder.RegisterType<BService>().As<IBService>().SingleInstance();
Obviously, this gives me an error
No scope with a tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested.
Simplest solution is to register IBService as InstancePerRequest, but there is no reason having PerRequest IBService rather than error message mentioned above.
How can i use PerRequest DbContext inside Singleton service ?
First attempt, you can inject IContainer into BService. But this will look like Service locator pattern, which is not good. Otherwise, you can define factory interface
public interface IFactory<T>
{
T GetInstance();
}
Then implement it and register
public class SimpleFactory<T> : IFactory<T>
{
private IContainer _container;
public SimpleFactory(IContainer container)
{
_container = container;
}
public T GetInstance()
{
return _container.Resolve<T>();
}
}
public class DbContextFactory : SimpleFactory<IADbContext>
{
public DbContextFactory(IContainer container):base(container)
{
}
}
Finally, use this factory in your singletone
public class BService : IBService
{
IADbContext _dbContext => _dbContextFactory.GetInstance();
IFactory<IADbContext> _dbContextFactory
public BService(IFactory<IADbContext> dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
}
Each time, when you want to acess to context inside singletone, it will pass this request to IoC container, which able to return context per request.
In my MVC 6 project I have my ApplicationDBContext class
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
protected override void OnModelCreating(ModelBuilder builder)
{
}
}
This is added to my services in the Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
//Other configurations removed for brevity
}
Now when I create a new Controller, it asks me if I want to use the Entity Framework, and I can choose my data context. When that controller is created the context is passed in the constructor using what I assume is dependency injection.
public class CompanyController : Controller
{
private ApplicationDbContext _context;
public CompanyController(ApplicationDbContext context)
{
_context = context;
}
}
Now, I don't want to do all database interactions in the controllers, but rather in my other classes. What I can't figure out, is how to get the ApplicationDbContext from my other classes. Passing it from the controller obviously won't work because classes could be called from other places than the controller.
If I just try new ApplicationDbContext(); I get the following error:
No database providers are configured. Configure a database provider by overriding OnConfiguring in your DbContext class or in the AddDbContext method when setting up services.
I feel like this should be something simple, but I am completely lost here.
ASP.NET Core is based on dependency injection, since your context has been added in your dependendy container, it's automatically injected by the framework when your controller is instanciated.
Edit based on comments :
You can setup your classes to support DI, let's suppose you have two class. One that depend on your context, and then second that depend both on your context and your first class :
public class MyClass
{
private ApplicationDbContext _context;
public MyClass(ApplicationDbContext context)
{
_context = context;
}
}
public class AnotherClass
{
private ApplicationDbContext _context;
private MyClass _myClass;
public AnotherClass(ApplicationDbContext context, MyClass myClass)
{
_context = context;
_myClass = myClass;
}
}
Add your classes as a transient dependency in the sevice collections at startup, and let the service provider resolve their dependencies for you :
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
services.AddTransient<MyClass>();
services.AddTransient<AnotherClass>();
//Other configurations removed for brevity
}
Change your controller to accept MyClass as injected dependency :
public class CompanyController : Controller
{
private ApplicationDbContext _context;
private MyClass _myClass;
public CompanyController(ApplicationDbContext context, MyClass myClass)
{
_context = context;
_myClass = myClass;
}
}
You can also have another controller that take AnotherClass as injected dependecy :
public class AnotherController : Controller
{
private AnotherClass _anotherClass;
public AnotherController(AnotherClass anotherClass)
{
_anotherClass = anotherClass;
// _anotherClass will have both ApplicationDbContext and MyClass injected by the service provider
}
}
You should read the docs of dependency injection of ASP.NET Core, it could help to understand basics of DI. Another article from K. Scott Allen that explain some bad practice when you deal with DI.
You can create a service class that receives the DbContext in the same way as the controller.
public class SomeService
{
private ApplicationDbContext MyDbContext { get; set; }
public SomeService(ApplicationDbContext dbContext)
{
MyDbContext = dbContext;
}
public void MethodName()
{
// You can now do MyDbContext.SomeDomainModel
}
}
Then register the service in Startup.cs, in your ConfigureServices method.
public void ConfigureServices(IServiceCollection services) {
// <snipped>
services.AddTransient<SomeService>();
}
And now, in your CompanyController, you can add another parameter in the constructor for the SomeService, just as you have for the ApplicationDbContext.
public class CompanyController : Controller
{
private ApplicationDbContext _context;
private SomeService _someService;
public CompanyController(ApplicationDbContext context, SomeService someService)
{
_context = context;
_someService = someService;
}
}
All that said, I don't think there's anything wrong with doing your logic in your controller actions to build your ViewModel, accessing the DbContext. The DbContext is what's separating your business logic (in the controller) from the DAL. Some may disagree with me, but you don't need to add additional services to further separate them. Most of the code in your action methods are unique to that action and not going to be reused by other actions. IMO, those are the pieces of code to put into services. Things like sending emails and such.