Null reference exception from object - c#

I have an application in asp.net core 2.1. I have registered/injected HttpContextAccessor in startup.cs file as like below.
services.AddHttpContextAccessor();
Also i have created an object for the interface IHttpContextAccessor and initiated that object in the constructor of Dependency injection class "GetScopedServicesTools"
public readonly IHttpContextAccessor HttpContextAccessor;
public GetScopedServicesTools(IHttpContextAccessor _httpContextAccessor)
{
HttpContextAccessor = _httpContextAccessor;
}
I have created an object for the DI class "GetScopedServicesTools" but am not sure what value need to be passed to the constructor of that class.
private GetScopedServicesTools getScopedServices;
Could you please help me what value need to be pass to the constructor of the class "GetScopedServicesTools".

If you want to use that class with and with the DI container, you should also register GetScopedServicesTools with the service provider.
In your Startup.cs you can do services.AddScoped<GetScopedServicesTools>(); and then simply inject it in your controllers or wherever you like.
Note:
I would like to point however that the DI mechanism is already providing out-of-the-box what you're trying to achieve with your GetScopedServicesTools class. You can simply inject the IHttpContextAccessor wherever you like since you already registered it with the service provider with services.AddHttpContextAccessor();. What you're doing with your extra class is an anti-pattern and should be avoided.

Related

Passing parameters to a service that already has dependency injection

I have a service with the following constructor
public TokenService(UserManager<IdentityUser> userManager, ApplicationDbContext dbContext)
{
_userManager = userManager;
_dbContext = dbContext;
}
In Startup.cs I have registered the service :
services.AddScoped<TokenService>();
I would like to add a string parameter to the constructor and pass the string to the service where I register the service in Startup.cs
I know you can pass parameters by creating a new instance during registration but I don't know how to pass the UserManger and ApplicationDbContext objects when doing it manually.
If I do this :
services.AddScoped<>(_ => new TokenService(null , null, "secret");
it doesn't work with the dependency injection of the other services.
As a workaround, I have added the string with a method in Program.cs but would like to remove this.
Normally when you want to provide parameters to something that also needs to get dependencies injected you end up creating a factory. However in this case since the parameters are known during registration you can easily fix this registering a factory method directly:
services.AddSingleton(serviceProvider => new TokenService(serviceProvider.GetRequiredService<UserManager<IdentityUser>>(), serviceProvider.GetRequiredService<ApplicationDbContext>(), "hello"));
In you comments you state that this can't be a singleton, that doesn't matter with the factory method, just as easy to register a transient for example:
services.AddTransient(serviceProvider => new TokenService(serviceProvider.GetRequiredService<UserManager<IdentityUser>>() , serviceProvider.GetRequiredService<ApplicationDbContext>(), "hello"));
Hopes this helps

HttpContext.RequestServices.GetService<T>() vs services.AddScope<T>()?

In the following code (from https://github.com/JasonGT/NorthwindTraders/blob/master/Src/WebUI/Controllers/BaseController.cs), it's a base control inherited by all controllers.
[ApiController]
[Route("api/[controller]/[action]")]
public abstract class BaseController : ControllerBase
{
private IMediator _mediator;
protected IMediator Mediator => _mediator ??= HttpContext.RequestServices.GetService<IMediator>();
}
The sub-class controllers then just use the property of Mediator.
How it differs from just adding services.AddScope<Mediator>(); in Startup.cs and then inject mediator.
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// services.AddSingleton<Mediator>();
services.AddScope<Mediator>();
The difference is that you don't need to inject IMediator to the constructor of BaseController and to all sub classes of BaseController.
So it's saves some boilerplate, but also makes the dependency less explicit.
Side note, Microsoft recommends to prefer the injection over RequestServices
The services available within an ASP.NET Core request from HttpContext are exposed through the HttpContext.RequestServices collection.
Request Services represent the services configured and requested as part of the app. When the objects specify dependencies, these are satisfied by the types found in RequestServices, not ApplicationServices.
Generally, the app shouldn't use these properties directly. Instead, request the types that classes require via class constructors and allow the framework inject the dependencies. This yields classes that are easier to test.
Note
Prefer requesting dependencies as constructor parameters to accessing the RequestServices collection.
See Microsoft docs

How to Implement DbContextOptionsBuilder in .NET Core

I am new to ASP.Net Core and I am trying to implement ASP.NET Core DI.
I configured like below in ConfigureServices Method in Startup.cs
services.AddScoped<DbContext, AutomationDbContext>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IUserService, UserService>();
In UserService Constructor, I am trying to use DI. I think below is NOT the right way to implement this.
public UserService(IHttpContextAccessor httpContextAccessor, AutomationDbContext automationDbContext, IConfiguration configuration)
{
this.configuration = configuration;
this.optionsBuilder = new DbContextOptionsBuilder<AutomationDbContext>();
var connectionString = this.configuration.GetConnectionString("Automation");
this.optionsBuilder.UseSqlServer(connectionString);
this.automationDbContext = new AutomationDbContext(this.optionsBuilder.Options);
this.httpContext = httpContextAccessor.HttpContext;
}
I don't like building optionsbuilder in constructor and get connectionstring.
What would be the better place to build these optionsBuilder and pass in constructor.
You need to use services.AddDbContext<TContext> instead:
services.AddDbContext<AutomationDbContext>(o =>
o.UseSqlServer(Configuration.GetConnectionString("Automation")));
Then, just inject your context:
public UserService(IHttpContextAccessor httpContextAccessor, AutomationDbContext automationDbContext)
As for IHttpContextAccessor, you should simply use:
services.AddHttpContextAccessor();
However, I would encourage you to strongly consider whether you actually need this in your service or not. If you need something like the current user's id, that should be passed into the method that needs it, not retrieved from within your service.
UPDATE
Since it was brought up, let me elucidate the reasons why adding your context in the way you currently are is incorrect, since it will shed a little light on how DI works in general.
First, you're binding DbContext directly to AutomationDbContext, which means you can then only use that one context. Maybe you don't need more than one context... now. That could change later. Second, when you register a service in that way, you can only inject the abstract type, i.e. DbContext here. The service registration literally means "when you see DbContext, inject an instance of AutomationDbContext". If you try to inject AutomationDbContext directly, as you're currently doing in your controller, that will actually throw an exception because that type is not actually registered as service: DbContext is. Third, AddScoped provides no real ability to configure the context, which is of course the part your were missing. There's ways to work around this such as using the factory overload of AddScoped or defining OnConfiguring on your context, but both of those are substandard to just using the right method in the first place: AddDbContext<TContext>
For what it's worth, there's also somewhat of a fourth reason, in that you can opt to use AddDbContextPool<TContext> instead of AddDbContext<TContext>, for connection pooling. There's no other way to set that up, so if you did want/need connection pooling, you'll never get there with AddScoped.

.Net Core Use Session in Class

I am using Session in .Net core, However i am able to set and get the Session data in Controller like
HttpContext.Session.SetString("User", "True");
var user = HttpContext.Session.GetString("User");
But when i am trying to use the same code in a concrete class i am not able to do so. It does not show GetString or SetString after HttpContext.Session.
It does not work after
HttpContext.Session
Please help
Thanks
To access session in non-controller class -
First, register the following service in Startup.ConfigureServices
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Now, register a class (example - SessionManager) where you want to access the Session in Startup.ConfigureServices.
services.AddScoped<SessionManager>();
Now, in SessionManager class, add the following code.
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ISession _session;
public SessionManager(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
_session = _httpContextAccessor.HttpContext.Session;
}
The above code is receiving IHttpContextAccessor object through dependency injection and then, it is storing Sessions in a local variable.
That's because HttpContext is a member of Controller, and outside a controller, it's a type name. See Access the current HttpContext in ASP.NET Core how to inject the IHttpContextAccessor into a class and access the session from there.
However, it's generally inadvisable to use the session in a class library. You'd better pass the particular values to your library call. So instead of accessing the settings in the library method, you do:
// controller code
var user = HttpContext.Session.GetString("User");
var libraryResult = _fooLibrary.Bar(user);
HttpContext.Session.SetString("UserResult", libraryResult);

Get per-request dependency from an Autofac func factory

I'm using ASP.NET Core and Autofac. Almost everything is registered as per lifetime scope ("per request"). So my database context DbContext is the same instance throughout a request.
However I have a singleton which also depends on DbContext. To avoid a captive dependency, it is injected as Func<Owned<DbContext>>, which means a new DbContext instance each time.
The problem is I need the same instance, as everywhere else during the request, not a new one.
I want to avoid a captive dependency bug, but I also want the same instance. Is that possible via tagging or a custom registration?
From the comments the least "architectural" painful approach may be by creating your own Scoped<T> class which will resolve the DbContext from current HttpContext
// Use an interface, so we don't have infrastructure dependencies in our domain
public interface IScoped<T> where T : class
{
T Instance { get; }
}
// Register as singleton too.
public sealed class Scoped<T> : IScoped<T> where T : class
{
private readonly IHttpContextAccessor contextAccessor;
private HttpContext HttpContext { get; } => contextAccessor.HttpContext;
public T Instance { get; } => HttpContext.RequestServices.GetService<T>();
public Scoped(IHttpContextAccessor contextAccessor)
{
this.contextAccessor = contextAccessor ?? throw new ArgumentNullException(nameof(contextAccessor));
}
}
Register it as
// Microsoft.Extensions.DependencyInjection
services.AddSingleton(typeof(IScoped<>), typeof(Scoped<>);
// Autofac
containerBuilder.RegisterType(typeof(Scoped<>))
.As(typeof(IScoped<>));
Then inject this into your validator service.
public class CustomerValidator: AbstractValidator<Customer>
{
private readonly IScoped<AppDbContext> scopedContext;
protected AppDbContext DbContext { get } => scopedContext.Instance;
public CustomValidator(IScoped<AppDbContext> scopedContext)
{
this.scopedContext = scopedContext ?? throw new ArgumentNullException(nameof(scopedContext));
// Access DbContext via this.DbContext
}
}
This way you can inject any scoped service w/o further registrations.
Additional notes
Autofac is considered a "conformer" (see docs) DI and integrates well with ASP.NET Core and Microsoft.Extensions.DependencyInjection.
From the documentation
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add services to the collection.
services.AddMvc();
// Create the container builder.
var builder = new ContainerBuilder();
// Register dependencies, populate the services from
// the collection, and build the container. If you want
// to dispose of the container at the end of the app,
// be sure to keep a reference to it as a property or field.
builder.RegisterType<MyType>().As<IMyType>();
builder.Populate(services);
this.ApplicationContainer = builder.Build();
// Create the IServiceProvider based on the container.
return new AutofacServiceProvider(this.ApplicationContainer);
}
There a few subtle differences to the default usage of Startup class and Microsoft.Extensions.DependencyInjection container.
ConfigureServices isn't void anymore, it returns IServiceProvider. This will tell ASP.NET Core to use the returned provider instead of DefaultServiceProvider from Microsoft.Extensions.DependencyInjection.
We return the Autofac container adapter: new AutofacServiceProvider(this.ApplicationContainer) which is the root container.
This is important to make ASP.NET Core use the container everywhere in ASP.NET Core, even inside middlewares which resolve per request dependencies via HttpContext.RequestedServices.
For that reasons you can't use .InstancePerRequest() lifetime in Autofac, because Autofac isn't in control of creating scopes and only ASP.NET Core can do it. So there is no easy way to make ASP.NET Core use Autofac's own Request lifetime.
Instead ASP.NET Core will create a new scope (using IServiceScopeFactory.CreateScope()) and use a scoped container of Autofac to resolve per-request dependencies.

Categories

Resources