Why is DbContext (ASP.NET) null in MyService? - c#

I use c#, ASP.NET Core, EF.
I have Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
...
}
I have Controller:
public class HomeController : Controller
{
public AppDbContext _db;
public HomeController(AppDbContext db)
{
_db = db;
}
public IActionResult Index()
{
// _db is NOT NULL
var d = _db.Users.FirstOrDefault();
}
}
I have class MyService.cs:
public class MyService
{
public static AppDbContext _db;
public MyService(AppDbContext db)
{
_db = db;
}
public static void GetOtherValue()
{
// _db is NULL
var d = _db.Users.FirstOrDefault();
}
}
HomeController works correctly: _db is not null.
Why variable _db is null in MyService.cs? How to fix it?
UP:
public class OtherController : Controller
{
....
MyService.GetOtherValue();
....
}

You are not injecting your service anywhere.
You just use static method thus constructor is never called. You can inject through the constructor.
First add a service into ASP.Net Core container:
services.AddScoped<MyService, MyService>();
Then inject via constructor in your controller class:
public class HomeController : Controller
{
private readonly MyService _myService;
public HomeController (MyService myService)
{
_myService = myService;
}
}
MyService class will be created every requests ( AddScoped()). Then the your DbContext will be created automatically.
Read more about DI in .NET Core

Related

How to use Dependency inject when using a service layer with Entity Framework Core?

My project is an ASP.NET Core 6 Web API. I am using Entity Framework Core and built-in dependency injection.
Program.cs:
var builder = WebApplication.CreateBuilder(args);
...
builder.Services.AddDbContext<myService>(options => options.UseSqlServer(ConnStr));
builder.Services.AddDbContext<anotherService>(options => options.UseSqlServer(anotherConnStr));
...
myController.cs:
public class myController : ControllerBase
{
private readonly myService _context;
public myController(myService context)
{
_context = context;
}
// ... Routes use either db models directly or methods from myService
}
myService.cs:
I'm using myContext as a base class so I can access the db tables directly in my controller.
Any better way to do this? db = this;
How do I inject this service?
private readonly anotherService anotherdb;
public class myService : myContext
{
private readonly myContext db;
private readonly anotherService anotherdb;
public myService(DbContextOptions<myService> options) : base(options)
{
// inject myContext, works but doesn't feel right.
db = this;
// how do I inject this?
anotherdb = new anotherService();
}
public IQueryable<mytable> Getdata()
{
var q = from s in db.mytable
select s;
return q;
}
// ...
}
myContext.cs:
public partial class myContext : DbContext
{
public myContext()
{
}
public myContext(DbContextOptions<myContext> options)
: base(options)
{
}
protected myContext(DbContextOptions options)
: base(options)
{
}
// ...
}
I have lots of services all with the same design, how can I inject another service into myService.cs?

C# Asp net core How to call database context outside of controllers

I have a question related to the use of database contexts outside the controller, namely, how to call the database context in a regular class?
To communicate with the database, I use: EF Core
I used options like this:
private readonly MSSQLContext _context;
public BookingController(MSSQLContext context)
{
_context = context;
}
Alternative
using (MSSQLContext context=new MSSQLContext())
{
context.get_Users.ToList();
}
Startup.cs
services.AddDbContext<MSSQLContext>(options =>
options.UseSqlServer(connection));
MSSQLContext.cs
public MSSQLContext()
{
}
public MSSQLContext(DbContextOptions<MSSQLContext> options)
: base(options)
{
}
public DbSet<VIVT_Core_Aud.Models.Core.Logger_Model> Logger_Models { get; set; }
and more tables...
Inject the context into whatever class you need to call into and register that class with the DI framework in your startup class.
For instance,
services.AddTransient<YourType>();
class YourType
{
public YourType(YourDbContext context) { ... }
}
You need to inject context using DI(dependency injection). I am showing you an example of repository pattern. You can search for "Repository pattern EF Core C# examples" will give you lots of examples or blogs to follow. Check here and here
Check out below example..
MyRepository.cs
namespace MyApp.Data.Services
{
public class MyRepository: IMyRepository, IDisposable
{
private MyAppContext _context;
private readonly ILogger<MyRepository> _logger;
public MyRepository(MyAppContext context, ILogger<MyRepository> logger)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
_logger = logger;
}
public IEnumerable<MyTable> GetMyTableData()
{
return _context.MyTable.ToList();
}
}
}
IMyRepository.cs
namespace MyApp.Data.Services
{
public interface IMyRepository
{
//Interface implementation
IEnumerable<MyTable> GetMyTableData();
}
}
Startup.cs of MVC project
services.AddDbContext<MyAppContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("AppDbConnString")));
//Scope of repository should be based on your project used, I recommend to check lifetime & scope based your project needs.
services.AddScoped<IMyRepository, MyRepository>();
Mycontroller.cs
using MyApp.Data.Services;
namespace MyApp.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IMyRepository _myRepository;
public HomeController(ILogger<HomeController> logger, IMyRepository myRepository)
{
_logger = logger;
_myRepository = myRepository ??
throw new ArgumentNullException(nameof(myRepository));
}
public IActionResult Index()
{
return View();
}
public IActionResult GetAllData()
{
var result = _myRepository.GetMyTableData();
return Json(result);
}
}
}

InvalidOperationException: Unable to resolve service for type 'Repository.UnitOfWork'

this is my code in .net core 3.1 to implement repository unitofwork and generic.
I write repository,user repository, unitofwork, contex and so.
after run the app there is an error :
InvalidOperationException: Unable to resolve service for type 'Repository.UnitOfWork'
I dont know what should I pass to the context or unitofwork as 'DbContextOptions'
this is my controller:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
UnitOfWork db;
public HomeController(ILogger<HomeController> logger, UnitOfWork db)
{
_logger = logger;
this.db = db;
}
}
this is my context:
public PandanetContext(DbContextOptions<PandanetContext> options) : base(options)
{
}
this is my unit of work:
public class UnitOfWork : IDisposable
{
PandanetContext db;
public UnitOfWork(PandanetContext db)
{
this.db = db;
}
private UserRepository userRepository;
public UserRepository UserRepository
{
get
{
if (userRepository == null)
{
userRepository = new UserRepository(db);
}
return userRepository;
}
}
}
this is my Repository:
public class UserRepository : Repository<UserDomainModel>
{
PandanetContext db;
public UserRepository(PandanetContext context) : base(context)
{
db = context;
}
}
this is actionResult :
public IActionResult Index()
{
UserDomainModel user = new UserDomainModel();
user.Name = "تست";
db.UserRepository.create(user);
db.UserRepository.Save();
return View();
}
in startup.cs:
services.AddDbContext<PandanetContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
You need to register the UnitOfWork:
In your startup.cs you can add the UnitOfWork to the services provider:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<UnitOfWork>();
//... etc.
}

Repository pattern with multiple databases

I am using repository pattern on EF Core and Autofac in a windows service.
I have a service that needs to connect with the some dozen databases which have the same schema (same dbcontext) but only different data.
How can I achieve this in my service using Autofac? Belo
public class ReportRepository : IReportRepository
{
private readonly ReportDbContext dbContext;
public ReportRepository(ReportDbContext dbContext)
{
this.dbContext = dbContext
}
public SomeModel GetData()
{
return dbContext.SalesData;
}
}
public class ReportService : IReportService
{
private readonly IReportRepository reportRepositoryEUServer;
public ReportService(IReportRepository reportRepositoryEUServer)
{
this.reportRepositoryEUServer = reportRepositoryEUServer
}
public SomeModelDto GenerateReport()
{
var euData = reportRepositoryEUServer.GetData();
// I need to call other servers (e.g LATAM) here and get the data and aggregate them with euData
}
}
Create base context including all settings, dbsets etc:
public abstract class BaseContext : DbContext
{
public BaseContext(DbContextOptions options)
: base(options)
{ }
public DbSet<object> FirstSet { get; set; }
...
}
inherit from BaseContext for both DBs
public class LATAMContext : BaseContext
{
public LATAMContext(DbContextOptions<LATAMContext> options) : base(options)
{
}
}
public class EUContext : BaseContext
{
public EUContext(DbContextOptions<EUContext> options) : base(options)
{
}
}
and register both in Startup.cs
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddDbContext<LATAMContext>(options => options.UseSqlServer(Configuration.GetConnectionString("LATAMConnectionString")));
services.AddDbContext<EUContext>(options => options.UseSqlServer(Configuration.GetConnectionString("EUConnectionString")));
// Autofac
var builder = new ContainerBuilder();
// needed only if you plan to inject ICollection<BaseContext>
builder.RegisterType<LATAMContext>().As<BaseContext>();
builder.RegisterType<EUContext>().As<BaseContext>();
builder.Populate(services);
return new AutofacServiceProvider(builder.Build());
}
add connection strings in appsettings.json
"ConnectionStrings": {
"LATAMConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true",
"EUConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
}
and now you can inject both contexts
public class ReportRepository : IReportRepository
{
private readonly LATAMContext latamDbContext;
private readonly EUContext euDbContext;
public ReportRepository(LATAMContext latamDbContext, EUContext euDbContext)
{
this.latamDbContext = latamDbContext;
this.euDbContext = euDbContext;
}
}
or if you plan to inject collection of contexts
public class ReportRepository : IReportRepository
{
private readonly ICollection<BaseContext> dbContexts;
public ReportRepository(ICollection<BaseContext> dbContexts)
{
this.dbContexts = dbContexts;
}
}
to access specific context
var _euContext = dbContexts.FirstOrDefault(x => x is EUContext) as EUContext;
var _latamContext = dbContexts.FirstOrDefault(x => x is LATAMContext) as LATAMContext;

Get Db context inside data access layer

I have some problems with EF-Core that I'm trying to figure out.
I use the startup code in the MVC Core application to initalize the db context.
This is my DB context:
public class AccountsDBContext : DbContext
{
public AccountsDBContext(DbContextOptions<AccountsDBContext> options)
:base(options)
{
}
// ...
}
And startup code:
public void ConfigureServices(IServiceCollection services)
{
// Inject the account db
services.AddDbContext<AccountsDBContext>(options =>
options.UseMySQL(Configuration.GetConnectionString("AccountsStore")));
// ...
In all the exampes I see the DB Context is a delivered via the constructor to the controller (I assume by dependency injection) and from there on to other entities\ layers.
[Route("api/[controller]")]
public class AccountsController : Controller
{
private AccountsDBContext _db;
public AccountsController(AccountsDBContext context)
{
this._db = context;
}
However, I'm not very fond of the idea that the db context will be a member at the controller.
I really prefer to get a hold of the db context in the data access layer instead of getting it passed into the repositories classes.
Is there a way to get the context inside the data access layer? (There is no IServiceCollection, IApplicationBuilder, IServiceScopeFactory there as far as I know)
I Understand what you are trying to do. I have done exactly that. The key is to Create a static class in your DAL that uses the IServiceCollection. then in here you add your context here's mine and it works a treat My front end doesn't even know about entity framework, nethier does my business layer:
public static IServiceCollection RegisterRepositoryServices(this IServiceCollection services)
{
services.AddIdentity<ApplicationUser, IdentityRole<int>>(
config => { config.User.RequireUniqueEmail = true;
config.Cookies.ApplicationCookie.LoginPath = "/Account/Login";
config.Cookies.ApplicationCookie.AuthenticationScheme = "Cookie";
config.Cookies.ApplicationCookie.AutomaticAuthenticate = false;
config.Cookies.ApplicationCookie.Events = new CookieAuthenticationEvents()
{
OnRedirectToLogin = async ctx =>
{
if (ctx.Request.Path.StartsWithSegments("/visualjobs") && ctx.Response.StatusCode == 200)
{
ctx.Response.StatusCode = 401;
}
else
{
ctx.Response.Redirect(ctx.RedirectUri);
}
await Task.Yield();
}
};
}).AddEntityFrameworkStores<VisualJobsDbContext, int>()
.AddDefaultTokenProviders();
services.AddEntityFramework().AddDbContext<VisualJobsDbContext>();
services.AddScoped<IRecruiterRepository, RecruiterRepository>();
services.AddSingleton<IAccountRepository, AccountRepository>();
return services;
}
then in my service layer I have another static class. My service layer has a reference to the repository layer and I register the repository services here (bootstrapping the repository into the service layer), like so and then I do the same again in the UI:
Service layer code:
public static class ServiceCollectionExtensions
{
public static IServiceCollection RegisterServices(this IServiceCollection services)
{
services.RegisterRepositoryServices();
services.AddScoped<IRecruiterService, RecruiterService>();
services.AddSingleton<IAccountService, AccountService>();
return services;
}
}
The Magic in the Repository Layer:
public partial class VisualJobsDbContext : IdentityDbContext<ApplicationUser, IdentityRole<int>, int>
{
private IConfigurationRoot _config;
public VisualJobsDbContext() { }
public VisualJobsDbContext(IConfigurationRoot config, DbContextOptions<VisualJobsDbContext> options) : base(options)
{
_config = config;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlServer(#_config["ConnectionStrings:VisualJobsContextConnection"]);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{....
Inject your repository/DAL implementation into the controller and have the DbContext injected into the repo constructor. The DI container will hook it all up as long as the appropriate classes are registered
How about this?
DALAccount.cs
public class DALAccount
{
private AccountsDBContext _db;
public DALAccount(AccountsDBContext db)
{
_db = db;
}
public IQueryable<User> Get()
=> _db.User.AsQueryable();
}
Your Api
public class AccountsController : Controller
{
private AccountsDBContext _db;
public AccountsController(AccountsDBContext context)
{
this._db = context;
}
public IActionResult Index()
{
DALAccount dal = new DALAccount(_db);
var list = dal.Get();
}
}

Categories

Resources