Access DBContext using EF7 and VNext - c#

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.

Related

How can I get a new ApiAuthorizationDbContext in my Controller

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

Dependency inject a class that has a dependency injection form DbContext

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> ();
}

Using ApplicationDbContext with DI from appsettings.json

I am trying to abstract any connection information away from my ApplicationDbContext class so that I can take advantage of different databases for development, staging, production. I start by registering a service from Startup.cs
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
My ApplicationDbContext class:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
}
When running this application I get the following error:
InvalidOperationException: Could not create an instance of type 'SquadApps.Data.ApplicationDbContext'. Model bound complex types must not be abstract or value types and must have a parameterless constructor.
So naturally I tried adding a parameterless constructor
public ApplicationDbContext() { }
Now getting another error:
InvalidOperationException: No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.
If I go back to having a connection string stored in the ApplicationDbContext class like so:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("........");
}
Then everything works fine but obviously this is not ideal and probably a bad practice. I think there is something i'm missing about the DI process and any suggestions or advice would be appreciated.
The solution turned out to be how I was trying to call the DI. I had incorrectly assumed DI would be able to be called per each IActionResult inside my controllers but in fact it must occur within the constructor of the controller. This makes the DI available to all IActionResult methods within the controller.
Example of the working DI call:
public class HomeController : Controller
{
private readonly ApplicationDbContext _ctx;
private readonly CompanySettings _companySettings;
public HomeController(ApplicationDbContext ctx, IOptions<CompanySettings> settings)
{
_ctx = ctx;
_companySettings = settings.Value;
}
public IActionResult Index()
{
var model = new HomeViewModel();
// _ctx and _companySettings can be used here
return View(model);
}
}

IServiceProvider in ASP.NET Core

I starting to learn changes in ASP.NET 5(vNext)
and cannot find how to get IServiceProvider, for example in "Model"'s method
public class Entity
{
public void DoSomething()
{
var dbContext = ServiceContainer.GetService<DataContext>(); //Where is ServiceContainer or something like that ?
}
}
I know, we configuring services at startup, but where all service collection staying or IServiceProvider?
You have to bring in Microsoft.Extensions.DependencyInjection namespace to gain access to the generic
GetService<T>();
extension method that should be used on
IServiceProvider
Also note that you can directly inject services into controllers in ASP.NET 5. See below example.
public interface ISomeService
{
string ServiceValue { get; set; }
}
public class ServiceImplementation : ISomeService
{
public ServiceImplementation()
{
ServiceValue = "Injected from Startup";
}
public string ServiceValue { get; set; }
}
Startup.cs
public void ConfigureService(IServiceCollection services)
{
...
services.AddSingleton<ISomeService, ServiceImplementation>();
}
HomeController
using Microsoft.Extensions.DependencyInjection;
...
public IServiceProvider Provider { get; set; }
public ISomeService InjectedService { get; set; }
public HomeController(IServiceProvider provider, ISomeService injectedService)
{
Provider = provider;
InjectedService = Provider.GetService<ISomeService>();
}
Either approach can be used to get access to the service. Additional service extensions for Startup.cs
AddInstance<IService>(new Service())
A single instance is given all the time. You are responsible for initial object creation.
AddSingleton<IService, Service>()
A single instance is created and it acts like a singleton.
AddTransient<IService, Service>()
A new instance is created every time it is injected.
AddScoped<IService, Service>()
A single instance is created inside of the current HTTP Request scope. It is equivalent to Singleton in the current scope context.
Updated 18 October 2018
See: aspnet GitHub - ServiceCollectionServiceExtensions.cs
I don't think it is a good idea for an entity (or a model) to have access to any service.
Controllers, on the other hand, do have access to any registered service in their constructors, and you don't have to worry about it.
public class NotifyController : Controller
{
private static IEmailSender emailSender = null;
protected static ISessionService session = null;
protected static IMyContext dbContext = null;
protected static IHostingEnvironment hostingEnvironment = null;
public NotifyController(
IEmailSender mailSenderService,
IMyContext context,
IHostingEnvironment env,
ISessionService sessionContext)
{
emailSender = mailSenderService;
dbContext = context;
hostingEnvironment = env;
session = sessionContext;
}
}
use GetRequiredService instead of GetService, like the example on ASP.NET Core tutorials ( https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/working-with-sql )
documentation on the method:
https://learn.microsoft.com/en-us/aspnet/core/api/microsoft.extensions.dependencyinjection.serviceproviderserviceextensions#Microsoft_Extensions_DependencyInjection_ServiceProviderServiceExtensions_GetRequiredService__1_System_IServiceProvider_
using Microsoft.Extensions.DependencyInjection;
using (var context = new ApplicationDbContext(serviceProvicer.GetRequiredService<DbContextOptions<ApplicationDbContext>>()))
Do not use GetService()
The difference between GetService and GetRequiredService is related with exception.
GetService() returns null if a service does not exist.
GetRequiredService() will throw exception.
public static class ServiceProviderServiceExtensions
{
public static T GetService<T>(this IServiceProvider provider)
{
return (T)provider.GetService(typeof(T));
}
public static T GetRequiredService<T>(this IServiceProvider provider)
{
return (T)provider.GetRequiredService(typeof(T));
}
}
Generally you want to have the DI do its thing and inject that for you:
public class Entity
{
private readonly IDataContext dbContext;
// The DI will auto inject this for you
public class Entity(IDataContext dbContext)
{
this.dbContext = dbContext;
}
public void DoSomething()
{
// dbContext is already populated for you
var something = dbContext.Somethings.First();
}
}
However, Entity would have to be automatically instantiated for you... like a Controller or a ViewComponent. If you need to manually instantiate this from a place where this dbContext is not available to you, then you can do this:
using Microsoft.Extensions.PlatformAbstractions;
public class Entity
{
private readonly IDataContext dbContext;
public class Entity()
{
this.dbContext = (IDataContext)CallContextServiceLocator.Locator.ServiceProvider
.GetService(typeof(IDataContext));
}
public void DoSomething()
{
var something = dbContext.Somethings.First();
}
}
But just to emphasize, this is considered an anti-pattern and should be avoided unless absolutely necessary. And... at the risk of making some pattern people really upset... if all else fails, you can add a static IContainer in a helper class or something and assign it in your StartUp class in the ConfigureServices method: MyHelper.DIContainer = builder.Build(); And this is a really ugly way to do it, but sometimes you just need to get it working.
I think the OP is getting confused. Entities should be as “thin” as possible. They should try not to contain logic, and or external references other than navigation properties. Look up some common patterns like repository pattern which helps to abstract your logic away from the entities themselves
Instead of getting your service inline, try injecting it into the constructor.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient(typeof(DataContext));
}
}
public class Entity
{
private DataContext _context;
public Entity(DataContext context)
{
_context = context;
}
public void DoSomething()
{
// use _context here
}
}
I also suggest reading up on what AddTransient means, as it will have a significant impact on how your application shares instances of DbContext. This is a pattern called Dependency Injection. It takes a while to get used to, but you will never want to go back once you do.

RepositoryFactory with Ninject

I'm working on web application (web form). I want to be able to change EntityFrameworkRepositoryFactory to NHibernateRepositoryFactory in the future.
IRepositoryFactory
public interface IRepositoryFactory
{
IProductRepository GetProductRepository();
}
ProductRepository
public class ProductRepository : IProductRepository
{
ExDbContext _db;
public ProductRepository(ExDbContext dbContext)
{
_db = dbContext;
}
public IList<Product> ListProductsByCategoryId(int categoryId)
{
List<Product> productsByCategoryId = _db.Products.Where(x => x.ProductCategoryId == categoryId).ToList();
return productsByCategoryId;
}
}
And there is EntityFrameworkRepositoryFactory.
class EntityFrameworkRepositoryFactory:IRepositoryFactory
{
ExDbContext _db;
public EntityFrameworkRepositoryFactory(ExDbContext dbContext)
{
_db = dbContext;
//
// TODO: Add constructor logic here
//
}
public IProductRepository GetProductRepository()
{
return new ProductRepository(_db);
}
}
How can i make easy for changing this in future ? I want use ninject for access EntityFrameworkRepositoryFactory but I'm stuck. Is there any example for this ?
Thanks.
We will add Ninject to your web application, fix your repository classes and add some Ninject modules to configure dependency injection:
Install Ninject. You can do this easily using the Package Manager Console: Install-Package Ninject.Web -dependencyVersion Highest
Remove your RepositoryFactory. Delete IRepositoryFactory and EntityFrameworkRepositoryFactory. You don't need them. Ninject will create a Repository and provide the dependencies as soon as your application asks for them. You need factories only to have better control of an object's lifetime.
Fix the repository. Let's make things more conventional and use an IEnumerable<Product> to return a read-only collection of products as result of our query. We also use Get as a prefix, as most repository patterns do:
public interface IProductRepository
{
IEnumerable<Product> GetProductsByCategoryId(int categoryId);
}
class EfProductRepository : IProductRepository
{
private readonly ExDbContext db;
public EfProductRepository(ExDbContext dbContext)
{
this.db = dbContext;
}
public IEnumerable<Product> GetProductsByCategoryId(int categoryId)
{
var productsByCategoryId = this.db
.Products
.Where(x => x.ProductCategoryId == categoryId)
.ToArray();
return productsByCategoryId;
}
}
Create a Ninject module. We need to bind our repository implementation to its interface. The Entity Framework DbContext uses the "Unit of Work" pattern, so we also need to make sure that our entity context instances are going to be disposed as soon as a request ends. We could do this using a context factory and the using directive, but we can also use the "Request Scope" of Ninject as it's easier:
public class EfRepositoryModule : NinjectModule
{
public override void Load()
{
this.Bind<IProductRepository>().To<EfProductRepository>();
this.Bind<ExDbContext>().ToSelf().InRequestScope();
}
}
At first, we bind IProductRepository to our concrete implementation. Thereby, whenever a component needs a product repository, Ninject will create an instance of EfProductRepository and use that.
Then we tell Ninject to bind ExDbContext to itself and use the request scope. All dependencies on ExDbContext will be served by one single instance of this class during a request, and this instance is going to be disposed when the request ends.
Load the module. In App_Start/NinjectWebCommon.cs update the following method:
private static void RegisterServices(IKernel kernel)
{
kernel.Load<EfRepositoryModule>();
}
Add dependencies to your pages. In every page where you need to show products, add the following property:
[Inject]
public IProductRepository ProductRepository { get; set; }
We need to use property injection or method injection here, because Web Pages doesn't support constructor injection (which should usually be favored). The Inject attribute tells Ninject that we have a dependency here that we want to be injected.
Add a module for NHibernate later on.
public class NHibernateRepositoryModule : NinjectModule
{
public override void Load()
{
this.Bind<IProductRepository>().To<NHibernateProductRepository>();
// Bind whatever else you need when working with NHibernate
}
}
// NinjectWebCommon
private static void RegisterServices(IKernel kernel)
{
kernel.Load<EfRepositoryModule>();
}

Categories

Resources