My ApplicationDbContext is null for some reason - asp.net-core

I get the error System.NullReferenceException. Object reference not set to an instance of an object. At Club.BLL.MaintenanceBLL.Maintenance.DoMaintenance()
But the context code is the same as on scaffolded pages, so not sure what I need to do. Any help is appreciated.
The code is:
namespace Club.BLL.MaintenanceBLL
{
public class Maintenance
{
private readonly Club.Data.ApplicationDbContext Context;
public Maintenance(ApplicationDbContext context)
IN DEBUG MODE context IS INDICATED
AS OBJECT REFERENCE NOT SET TO AN INSTANCE OF AN OBJECT.
HOW DO I FIX THIS.
{
Context = context;
}
public Maintenance()
{
}
public void DoMaintenance()
{
//Parse Maintenance table and action those items
//where Active=True and ActionDate has passed
//==================================
//Retrieve list of rows in Maintenance table
var maintenances = Context.Maintenance; PROGRAM FAILS ON THIS LINE.
I imagine I am missing something fundamental as I am 'learning by doing'. The lines that call up the DoMaintenance routine are located in root/Pages/Index, which is a scaffolded page. The DoMaintenance routine is called from the following lines in the root/Pages/index.cshtml page:
public void OnGet()
{
Maintenance maintenance = new Maintenance();//Create new instance
maintenance.DoMaintenance();
}
AND startup.cs includes the lines
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity(options =>
options.SignIn.RequireConfirmedAccount = true)
.AddRoles()
.AddEntityFrameworkStores();
ANY HELP WOULD BE APPRECIATED.
Thanks and cheers....Alan

According to your description and codes, I found you have register the dbcontext as a service in your application that means if you want to use it, you should inject the dbcontext into your Maintenance class.
Normally, we will create a service as BLL service in asp.net core.
Then we could register the service in the startup.cs and inject it in the razor page.
I suggest you could try to modify the Maintenance class as below codes shows:
You could create a interface called IMaintenance:
public interface IMaintenance
{
public void DoMaintenance();
}
Then you could let Maintenance inherit IMaintenance as below:
public class Maintenance : IMaintenance
{
private readonly TestDbcontext Context;
public Maintenance(TestDbcontext testDbcontext ) {
Context = testDbcontext;
}
public void DoMaintenance() {
var maintenances = Context.Employees.ToList() ;
}
}
At last, you could register the Maintenance as service in startup.cs ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContext<TestDbcontext>(ServiceLifetime.Transient);
services.AddTransient<IMaintenance, Maintenance>();
}
You could directly use the Maintenance in razor pages like below, the asp.net core dependency injection will inject the dbcontext automatically into Maintenance.
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
private readonly IStringLocalizer<IndexModel> _localizer;
private readonly IMaintenance _maintance;
public CustomerModel Customer { get; set; }
public IndexModel(ILogger<IndexModel> logger, IStringLocalizer<IndexModel> localizer, IMaintenance maintance)
{
_logger = logger;
_localizer = localizer;
_maintance = maintance;
}
public void OnGet()
{
_maintance.DoMaintenance()
}
}
namespace Club.Pages
{
[AllowAnonymous]
public class IndexModel : PageModel
{
private readonly IMaintenance _maintenance;
public IndexModel (IMaintenance<IndexModel> maintenance)
{
_maintenance = maintenance;
}
public void OnGet()
{
_maintenance.DoMaintenance();
}
}
}

You might have missed to inject your context and since there's default ctor, everything works fine without setting Context property.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<Context>(() => ...));
}

Related

How to get AppSetting values in startup by using services in asp.net core?

I want to get value of appsetting inside StartUp and also using services for saving them.
I create a static IServiceCollection method for AddTransient my custom service.
I define a readonly variable for keep the appsetting values. My problem is that, this service creates new instance for readonly variable, for all calling.how can I prevent this?
and I have a question that other extensions like AddOpenIdConnect, how to work with their configs, I mean how to save and use them?
this is startup:
public void ConfigureServices(IServiceCollection services){
...
services.AddMyIntegration(conf =>
{
conf.ConnectionString = Configuration.GetConnectionString("Integration");
conf.AgentApiAddress = Configuration["AgentApiAddress"];
});
}
....
public static class MyExtension
{
public static IServiceCollection AddMyIntegration(this IServiceCollection services, Action<MyConstantsProvider> myConstantsProvider)
{
services.AddTransient((t) =>
{
return new MyService(myConstantsProvider);
});
return services;
}
}
this is my service:
public class MyService
{
public readonly MyConstantsProvider Provider;
public MyService(Action<MyConstantsProvider> configure)
{
Provider = new MyConstantsProvider();
configure(Provider);
}
}
public class MyConstantsProvider
{
public string ConnectionString { get; set; }
public string AgentApiAddress { get; set; }
}
Update my question:
Finally I fixed my issue by add MyConstantsProvider as singletone instead of MyService so this creates new instance of variable at the first time in extension class:
public static class MyExtension
{
public static IServiceCollection AddMyIntegration(this IServiceCollection services, Action<MyConstantsProvider> myConstantsProvider)
{
var provider = new MyConstantsProvider();
myConstantsProvider(provider);
services.AddSingleton(provider);
services.AddTransient<MyService>();
return services;
}
}
this is MyService class:
public class MyService
{
public readonly MyConstantsProvider Provider;
public MyService(MyConstantsProvider provider)
{
Provider = provider;
}
}
I wonder why we make it so complicated ? I just saw we're trying to read appsettings later in the application somewhere, and for this, the framework have default implementation to back us up.
Our app settings might look like
{
"Catalog": {
"ConnectionString": "SomeConnection",
"AgentApiAddress": "http://somewhere.dev"
}
}
Then our class could be
public class MySetting
{
public string ConnectionString { get; set; }
public string AgentApiAddress{ get; set; }
}
Config register it in startup (or somewhere we like in .net 6)
services.Configure<MySetting>(configuration.GetSection("Catalog"));
Retrive it later in the app via DI
public class SomeService
{
private readonly MySetting _setting;
public SomeService(IOptions<MySetting> config)
{
_setting = config.Value;
}
}
For setting that can be change dynamically, take a look at IOptionsMonitor
Or that might be some special case that I miss ?

ASP.NET Core Dependency Injection: NullReferenceException trying to access concrete class member defined in its interface

this is getting weird as I've done it several times with no issues.
I'm using ASP.NET Core 3.1 for the record.
The problem is that I register a concrete type and its relative interface in the ConfigureServices Startup method:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("MyDatabase")
));
ProfileManager.RegisterMappingService(services);
services.AddScoped<IUserWorkerService, UserWorkerService>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddControllers();
I inject that dependency in the controller:
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private readonly IUserWorkerService userWorkerService;
public UsersController(IUserWorkerService userWorkerService)
{
this.userWorkerService = userWorkerService ?? throw new ArgumentNullException(nameof(userWorkerService));
}
[AllowAnonymous]
[HttpPost("authenticate")]
public IActionResult Authenticate(string userName, string password)
{
var user = this.userWorkerService.Authenticate(userName.Trim(), password.Trim());
if (user == null)
return Unauthorized();
return Ok(System.Text.Json.JsonSerializer.Serialize(user));
}
}
This is the interface:
public interface IUserWorkerService
{
public UserDto Authenticate(string userName, string password);
}
And this is the concrete class:
public class UserWorkerService : IUserWorkerService
{
private readonly MyDbContext dbContext;
private readonly IMapper mapper;
public UserWorkerService(MyDbContext dbContext, IMapper mapper)
{
this.dbContext = dbContext;
this.mapper = mapper;
}
public UserDto Authenticate(string userName, string password)
{
*blah blah*
}
}
And when I make the POST request, I land correctly on the ActionResult of the controller but the UserWorkerService instance doesn't contain the member defined in its interface, just the injected members IMapper and MyDbContext.
Therefore, when code reaches Authenticate method invoke in the UsersController, debugger throws a NullReferenceException.
What am I missing here?

inject Database Context into Custom Attribute .NET Core

I'm creating ASP.NET Core 3.1 app, using SPA for front end. So I decided to create custom Authentication & Authorization. So I created custom attributes to give out and verify JWTs.
Lets say it looks like this:
[AttributeUsage(AttributeTargets.Method)]
public class AuthLoginAttribute : Attribute, IAuthorizationFilter
{
public async void OnAuthorization(AuthorizationFilterContext filterContext)
{
//Checking Headers..
using (var EF = new DatabaseContext)
{
user = EF.User.Where(p => (p.Email == username)).FirstOrDefault();
}
filterContext.HttpContext.Response.Headers.Add(
"AccessToken",
AccessToken.CreateAccessToken(user));
}
}
Everything was Okay, but my DatabaseContext, looked like this:
public class DatabaseContext : DbContext
{
public DbSet<User> User { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySQL("ConnectionString");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//....
}
}
I wanted to take Connection string from Appsettings.json and maybe use Dependency injection. I
Changed Startup.cs to look like this:
//...
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContext<DatabaseContext>(
options => options.UseMySQL(Configuration["ConnectionStrings:ConnectionString"]));
services.Add(new ServiceDescriptor(
typeof(HMACSHA256_Algo), new HMACSHA256_Algo(Configuration)));
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
}
//...
Changed Database Context class to this:
public class DatabaseContext : DbContext
{
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) { }
public DbSet<User> User { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
///..
}
}
In Controllers I injected DB context and everything works. It looks like this:
[ApiController]
[Route("API")]
public class APIController : ControllerBase
{
private DatabaseContext EF;
public WeatherForecastController(DatabaseContext ef)
{
EF = ef;
}
[HttpGet]
[Route("/API/GetSomething")]
public async Task<IEnumerable<Something>> GetSomething()
{
using(EF){
//.. this works
}
}
}
But my custom Attribute doesn't work no more. I can't declare new Database context, because it needs DatabaseContextOptions<DatabaseContext> object to declare, so how do I inject DBContext to Attribute as I did to Controller?
This doesn't work:
public class AuthLoginAttribute : Attribute, IAuthorizationFilter
{
private DatabaseContext EF;
public AuthLoginAttribute(DatabaseContext ef)
{
EF = ef;
}
public async void OnAuthorization(AuthorizationFilterContext filterContext)
{
using(EF){
}
}
}
this works with controller, but with attribute complains about there not being constructor with 0 arguments.
What you can do is utilize the RequestServices:
[AttributeUsage(AttributeTargets.Method)]
public class AuthLoginAttribute : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
var dbContext = context.HttpContext
.RequestServices
.GetService(typeof(DatabaseContext)) as DatabaseContext;
// your code
}
}
If you allow me to add two comments to your code:
Try not to use async void because in the event of an exception you will be very confused what is going on.
There is no need to wrap injected DbContext in a using statement like this using(EF) { .. }. You will dispose it early and this will lead to bugs later in the request. The DI container is managing the lifetime for you, trust it.

.NET Core how to DI DbContext to AuthrozationFilter

like as title
I setting db context on Startup.cs
services.AddDbContext<MyContext>(options => options.UseSqlServer(connectionStr));
and I want using it on AuthrozationFilter constructor like this
public class AuthrozationFilter : Attribute, IAuthorizationFilter
{
private readonly MyContext _db;
public AuthrozationFilter(MyContext db)
{
this._db = db;
}
}
but it doesn't work, how to do that ?
You can use service location to resolve components from the built-in IoC container by using RequestServices.GetService:
public class AuthrozationFilter : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
var dbcontext= context.HttpContext.RequestServices.GetRequiredService<MyContext>();
}
}
Or you can use ServiceFilter/TypeFilter :
public class AuthrozationFilter : IAuthorizationFilter
{
private readonly MyContext _db;
public AuthrozationFilter(MyContext db)
{
this._db = db;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
}
}
And add [TypeFilter(typeof(AuthrozationFilter))] on controllers/actions . Please refer to below documents for filters in asp.net core :
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-3.1
https://www.devtrends.co.uk/blog/dependency-injection-in-action-filters-in-asp.net-core
Found another way to do it. Under the covers this is wrapping ServiceFilter
Use the AddService api on Action<MvcOptions>
serviceCollection.AddControllers(c => c.Filters.AddService<AuthrozationFilter>())
Need to register your service with dependency injection
serviceCollection.AddScoped<AuthrozationFilter>();
Then inject via the constructor
public class AuthrozationFilter : IAuthorizationFilter
{
private readonly MyContext _db;
public AuthrozationFilter(MyContext db)
{
this._db = db;
}
}
Adds the filter to all controllers. If you need more targeted, probably use ServiceFilterAttribute directly.

.Net Core How to Access Configuration Anywhere in application

I have read through the documentation on the different ways to setup and access configuration in .Net Core 2.1 and also the options pattern that seems to be recommended (https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-2.1). However, I can't seem to get what I want working:
I have done the following:
AppSettings:
{
"ConnectionStrings": {
"DefaultConnStr": "Server=(localdb)\\MSSQLLocalDB;Database=_CHANGE_ME;Trusted_Connection=True;MultipleActiveResultSets=true;Integrated Security=true",
"AW2012ConnStr": "Server=localhost;Database=AW2012;Trusted_Connection=True;MultipleActiveResultSets=true;Integrated Security=true"
}
}
MyConfig:
public class MyConfig
{
public string AWConnStr { get; }
public string DefaultConnStr { get; }
}
Startup:
public class Startup
{
public IConfiguration _config { get; set; }
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
_config = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
//add config to services for dependency injection
//services.AddTransient<IMyConfig, MyConfig>();
//services.AddScoped<IMyConfig, MyConfig>();
var section = _config.GetSection("ConnectionStrings");
services.Configure<MyConfig>(section);
}
private static void HandleGetData(IApplicationBuilder app)
{
//DataHelper dataHelper = new DataHelper(_dataHelper);
var _dataHelper = app.ApplicationServices.GetService<DataHelper>();
app.Run(async context =>
{
//await context.Response.WriteAsync("<b>Get Data</b>");
//await context.Response.WriteAsync(dataHelper.GetCompetitions(context.Request.QueryString.ToString()));
await context.Response.WriteAsync(_dataHelper.GetCompetitions(context.Request.QueryString.ToString()));
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.Map("/Route1", HandleRoute1);
app.Map("/Route2", HandleRoute2);
app.Map("/GetData", HandleGetData);
app.Run(async (context) =>
{
await context.Response.WriteAsync("Non Mapped Default");
});
}
}
I would like to then access the configuration in any class anywhere in my code. So for example I have the following class where I would like to just read the configuration information:
public interface IDataHelper
{
string GetCompetitions(string val);
}
public class DataHelper : IDataHelper
{
private readonly MyConfig _settings;
public DataHelper(IOptions<MyConfig> options)
{
_settings = options.Value;
}
public string GetCompetitions( string queryStringVals)
{
return _settings.AWConnStr;
}
}
As shown above in my Startup class I then want to access/call something in the HandleGetData function in my startup, so that when I browse to the following route: http://localhost:xxxxx/getdata I get back the response from the Something.GetData function.
Is this correct? The problem I'm having is that when I create an instance of class Something, it is requiring me to pass in the configuration object, but doesn't that defeat the purpose of injecting it. How should I be setting this up to work similar to how DBContext gets the context injected with the configuration options. And what's the difference between services.AddTransient and services.AddScoped? I've seen both as a way to register the service.
I would say that in .Net Core application you shouldn't pass instance of IConfiguration to your controllers or other classes. You should use strongly typed settings injected through IOtions<T> instead. Applying it to your case, modify MyConfig class (also property names should match names in config, so you have to rename either config (DefaultConnection->DefaultConnStr, AW2012ConnStr->AWConnStr or properies vice versa):
public class MyConfig
{
public string AWConnStr { get; set; }
public string DefaultConnStr { get; set; }
}
Register it:
public void ConfigureServices(IServiceCollection services)
{
// in case config properties specified at root level of config file
// services.Configure<MyConfig>(Configuration);
// in case there are in some section (seems to be your case)
var section = Configuration.GetSection("ConnectionStrings");
services.Configure<MyConfig>(section);
}
Inject it to required service:
public class MyService
{
private readonly MyConfig _settings;
public MyService(IOptions<MyConfig> options)
{
_settings = options.Value;
}
}
And what's the difference between services.AddTransient and
services.AddScoped? I've seen both as a way to register the service.
Transient lifetime services are created each time they're requested.
Scoped lifetime services are created once per request.
You have to do the same thing for the Something as you did for MyConfig like:
public interface ISomething
{
string GetSomeData();
}
Then:
public class Something : ISomething
{
public IConfiguration _config { get; set; }
public Something(IConfiguration configuration)
{
_config = configuration;
}
public string GetSomeData()
{
return _config["DefaultConnStr"];
}
}
Then in the ConfigureService method of the Startup class as follows:
services.AddScoped<ISomething,Something>();
Then call the GetSomeData() as follows:
public class CallerClass
{
public ISomething _something { get; set; }
public CallerClass(ISomething something)
{
_something = something;
}
public string CallerMethod()
{
return _something.GetSomeData();
}
}
Then:
And what's the difference between services.AddTransient and services.AddScoped? I've seen both as a way to register the service.
Here is the details about this from microsoft:
Service Lifetime details in ASP.NET Core