I used to have a UserFactory (before vNext) that used HttpContext.Current but since that is now gone I am having trouble recreating it.
I want it to be a static class that sets and gets the current user to access user data throughout the application.
I know I must use the DI system but not sure how.
Code so far:
public class CurrentContext : IHttpContextAccessor
{
private IHttpContextAccessor ctx;
public HttpContext HttpContext
{
get
{
return ctx.HttpContext;
}
set
{
ctx.HttpContext = value;
}
}
}
services.AddTransient<IHttpContextAccessor, CurrentContext>();
public class UserFactory
{
private IHttpContextAccessor _context;
public UserFactory(IHttpContextAccessor Context)
{
_context = Context;
}
public void Add(string s) => _context.HttpContext.Session.SetString(s, s);
public string Get(string s) => _context.HttpContext.Session.GetString(s);
}
How can I get a UserFactory instance anywhere in my app with the current context?
I suggest you make the UserFactory class non-static and register it as scoped:
services.AddScoped<UserFactory>();
This will create one instance per web request. You can inject this into every other class and let the UserFactory take a dependency on IHttpContextAccessor to get the current HttpContext.
This adheres to the dependency inversion philosophy Microsoft is trying to implement in ASP.NET 5. Static classes do not really fit into this and should be avoided as much as possible.
Example
UserFactory class:
public class UserFactory
{
private readonly IHttpContextAccessor _httpContextAccessor;
public UserFactory(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
// other code...
}
ConfigureServices() in Startup class:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddScoped<UserFactory>();
// ...
}
Now you can inject an instance of UserFactory in a controller for example:
public class SomeController : Controller
{
private readonly UserFactory _userFactory;
public SomeController(UserFactory userFactory)
{
_userFactory = userFactory;
}
// ...
}
Now when UserFactory is begin resolved, IHttpContextFactory inside UserFactory will also be resolved.
Related
I'd like to access IWebHostEnvironment.WebRootPath anywhere in the asp.net core mvc application. For instance, some random class deep in the class hierarchy. Is there a static class or some other method to do so?
I am aware that I can inject IWebHostEnvironment or that I can cache the value on the startup of the application. My question is strictly about accessing it without these methods.
I am aware that I can inject IWebHostEnvironment or that I can cache the value on the startup of the application. My question is strictly about accessing it without these methods.
No, you cannot. There's no static built in here with access to this information. You can create your own though.
You can achieve this y doing the following
In your Shared project or common project which is reference by the Web project add the below interface
public interface IApplicationContext
{
public string BaseUrl { get; }
}
Then, in the web project add below code
public sealed class ApplicationContext : IApplicationContext
{
private readonly IWebHostEnvironment _webHostEnvironment;
private readonly IHttpContextAccessor _httpContextAccessor;
public ApplicationContext(IWebHostEnvironment webHostEnvironment, IHttpContextAccessor httpContextAccessor)
{
_webHostEnvironment = webHostEnvironment;
_httpContextAccessor = httpContextAccessor;
}
public string BaseUrl
{
get
{
var baseUrl = _webHostEnvironment.IsDevelopment() ? AppConstants.BaseUrl.FELocalHostBaseUrl :
_httpContextAccessor.HttpContext?.Request.BaseUrl();
return baseUrl!;
}
}
}
Then, in you need to configure the dependency injection in your Startup.cs or any where that you configure DI as below
services.AddHttpContextAccessor();
services.AddScoped<IApplicationContext, ApplicationContext>();
Then you can inject the IApplicationContext in any service class constructor and access the baseUrl like below
public sealed class SecurityService
{
private readonly IApplicationContext _applicationContext;
public SecurityService(IApplicationContext applicationContext)
{
_applicationContext = applicationContext;
}
public async Task<ResponseResult> SendResetPasswordEmail(ForgotPasswordModel forgotPasswordModel, CancellationToken cancellationToken)
{
var baseUrl = _applicationContext.BaseUrl;
return new ResponseResult();
}
}
When I attempt requests to a .net core 3.1 WebAPI from Postman I am getting error
System.InvalidOperationException: Unable to resolve service for type 'PaymentsAPI.Repository.PaymentService' while attempting to activate 'PaymentsAPI.Controllers.PaymentController'
'
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddCors(c =>
{
c.AddPolicy("AllowOrigin", options => options.AllowAnyOrigin());
});
services.AddDbContext<ApplicationDbContext>(o => o.UseSqlServer(Configuration.GetConnectionString("SqlSvrConn")));
services.AddTransient<IAsyncPaymentsService<PaymentDetail>, PaymentService>();
}
IAsyncPaymentsService.cs
public interface IAsyncPaymentsService<TEntity>
{
Task<IEnumerable<TEntity>> GetAllAsync();
}
PaymentService.cs
public class PaymentService : IAsyncPaymentsService<PaymentDetail>
{
private readonly ApplicationDbContext _dbContext;
public async Task<IEnumerable<PaymentDetail>> GetAllAsync()
{
return await _dbContext.PaymentDetails.ToListAsync();
}
}
PaymentController.cs
[ApiController]
[Route("[controller]")]
public class PaymentController : ControllerBase
{
private readonly ApplicationDbContext _context;
private readonly PaymentService _service;
public PaymentController(ApplicationDbContext context, PaymentService service)
{
_context = context;
_service = service;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<PaymentDetail>>> GetAsync()
{
var items = (await _service.GetAllAsync());
return Ok(items);
}
}
I have tried rearranging the order of services in the container but the error still persists. What am I missing ?
The quick fix would be to change the controller constructor to depend on the abstraction instead of the implementation since the abstraction is what was registered with the container.
//...
private readonly ApplicationDbContext _context;
private readonly IAsyncPaymentsService<PaymentDetail> _service;
public PaymentController(ApplicationDbContext context, IAsyncPaymentsService<PaymentDetail> service)
{
_context = context;
_service = service;
}
//...
However, the generic abstraction could derived to a closed type if so desired
public interface IPaymentService : IAsyncPaymentsService<PaymentDetail> {
}
applied to the implementation
public class PaymentService : IPaymentService {
//...omitted for brevity
}
registered with the container
services.AddTransient<IPaymentService, PaymentService>();
and refactored in the controller
//...
private readonly ApplicationDbContext _context;
private readonly IPaymentService _service;
public PaymentController(ApplicationDbContext context, IPaymentService service)
{
_context = context;
_service = service;
}
//...
The only thing you should have to change to make this work is to accept the interface into your controller instead of the concrete service.
public PaymentController(ApplicationDbContext context, IAsyncPaymentsService<PaymentDetail> service)
{...}
This is recommended over taking the concrete type for various reasons such as testing. If you truly need the concrete type, you'd have to instead change your registration to
services.AddTransient<PaymentService>();
and leave your controller's constructor as is.
The IHttpContextAccessor gives me:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
My HomeController:
public class HomeController : BaseController
{
private static IHttpContextAccessor httpContextAccessor;
private static seiren_devContext context;
public HomeController() : base(httpContextAccessor,context)
{
}
}
My BaseController:
public class BaseController : Controller
{
protected readonly seiren_devContext _context;
protected readonly IHttpContextAccessor _httpContextAccessor;
protected ISession _session => _httpContextAccessor.HttpContext.Session;
public BaseController(IHttpContextAccessor httpContextAccessor, seiren_devContext context)
{
_context = context;
_httpContextAccessor = httpContextAccessor;
}
}
How can I make the HomeController Class inherit from BaseController in a proper way?
Nothing is setting the static (note: static is very bad in this context) variables in HomeController so they will always be null. Instead, make the HomeController also take the two values as constructor parameters and let the dependency injection system take care of the rest.
public class HomeController : BaseController
{
public HomeController(IHttpContextAccessor httpContextAccessor, seiren_devContext context)
: base(httpContextAccessor,context)
{
}
}
You may also need this line in ConfigureServices in your Startup.cs:
services.AddHttpContextAccessor();
You need to add in ConfigureServices
services.AddHttpContextAccessor();
I try get data from my database with repository Pattern
i have 3 project
Bmu.Mode 'this is for model to create database'
Bmu.Repo 'it have 2 folder for repository include contract/InewsRepository.cs' and 'Repository/NewsRepository' for implement Interface
Bmu.Api for invoke data from Repo project
news class in Model Project
namespace bmu.model
{
public class News
{
public int Id { get; set; }
public string SubTitle { get; set; }
public string Title { get; set; }
public string Summery { get; set; }
}
}
context class in model project
namespace bmu.model
{
public class BmuContext : DbContext
{
public BmuContext(DbContextOptions<BmuContext> options): base(options)
{
}
public DbSet<News> News { get; set; }
}
}
My interface in Repo project
namespace bmu.repo.Contracts
{
public interface INewsRepository
{
Task<IEnumerable<News>> GetAllAsync();
Task<IEnumerable<News>> GetAllActiveAsync();
}
}
implement interface in bmu.repo
namespace bmu.repo.IRepository
{
public class NewsRepository : INewsRepository
{
private readonly BmuContext _context;
private readonly MemoryCache _memoryCache;
public NewsRepository(BmuContext context, MemoryCache memoryCache)
{
_context = context;
_memoryCache = memoryCache;
}
public async Task<IEnumerable<News>> GetAllAsync()
{
return await _context.News.ToListAsync();
}
public async Task<IEnumerable<News>> GetAllActiveAsync()
{
return await _context.News.Where(x => x.Active).ToListAsync();
}
}
}
Also add
services.AddControllers();
services.AddSingleton<INewsRepository, NewsRepository>();
in startup of Api project
and this is my controller
namespace bmu.api.Controllers
{
[ApiController]
[Route("[controller]")]
public class NewsController : ControllerBase
{
private readonly ILogger<NewsController> _logger;
private readonly INewsRepository _newsRepository;
public NewsController(ILogger<NewsController> logger,INewsRepository newsRepository)
{
_logger = logger;
_newsRepository = newsRepository;
}
[HttpGet]
public async Task<IEnumerable<News>> Get()
{
return await _newsRepository.GetAllActiveAsync();
}
}
}
but when run project i got this error
AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: bmu.repo.Contracts.INewsRepository Lifetime: Singleton ImplementationType: bmu.repo.IRepository.NewsRepository': Unable to resolve service for type 'bmu.model.BmuContext' while attempting to activate 'bmu.repo.IRepository.NewsRepository'.)
also because of multi project add DbContext with this
UPDATE:
namespace bmu.model
{
public class BmuContextFactory : IDesignTimeDbContextFactory<BmuContext>
{
public BmuContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<BmuContext>();
optionsBuilder.UseSqlite("Data Source=bmu.db");
return new BmuContext(optionsBuilder.Options);
}
}
}
Is there any solution for this error ?
Firstly,you need to change:
services.AddSingleton<INewsRepository, NewsRepository>();
To:
services.AddTransient<INewsRepository, NewsRepository>();
Secondly,you need to inject IMemoryCache instead of MemoryCache in NewsRepository.
Here is a simple demo like below:
1.Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddSession();
services.AddTransient<INewsRepository, NewsRepository>();
services.AddDbContext<BmuContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("Connectionstring")));
services.AddMemoryCache();
}
2.appsettings.json:
"ConnectionStrings": {
"Connectionstring": "Server=(localdb)\\mssqllocaldb;Database=Bmu;Trusted_Connection=True;MultipleActiveResultSets=true"
}
3.NewsRepository:
public class NewsRepository : INewsRepository
{
private readonly BmuContext _context;
private readonly IMemoryCache _memoryCache;
public NewsRepository(BmuContext context, IMemoryCache memoryCache)
{
_context = context;
}
//...
}
My Error was that I was injecting the service class instead of the interface
It was
//This is wrong
Private readonly DataSerive _dataService;
public void EmployeeHandler(DataSerive dataService)
{
_dataService = dataService;
}
But it should be
//This is correct
Private readonly IDataSerive _dataService;
public void EmployeeHandler(IDataSerive dataService)
{
_dataService = dataService;
}
Here DataService is the class that handles operation
and IDataService is the interface
There is a lifetime type mismatch in your API. EntityFramework DbContext is a scoped service, and you cannot have a singleton instance of the NewsRepository, as it depends on an instance that is generated for each request.
You either have to use NewsRepository as a scoped service, or restructure your dependency resolution, like shown in this SO answer: Use DbContext in ASP .Net Singleton Injected Class
Like Sotiris Koukios-Panopoulos -san comment
I see you are only setting up the options for design time, not in your Startup.cs. I expect a:
services.AddDbContext<BmuContext>(options => options.UseSqlite("your connection string"));
instead.
In my case, I forgot to set this in my Startup.cs
services.AddDbContext<myDbContext>(o => o.UseSqlServer(myConnectionString));
and I forgot to mention this, because I'm using interface an service
services.AddScoped<IMyTruckService, MyTruckService>();
I was adding singleton service that was injecting DbContext class.
services.AddSingleton<WeatherForecastService>();
I changed above to below (Added a transient service scope) and it worked.
services.AddTransient<FoodItemService>();
This error can be caused by circular dependency.
Because probably, you inject service1 in service2 and also service2 in service1.
You should change it and break circular dependency.
I was having two dbcontext, and forgotten to mention this in startup.cs file
services.AddDbContext<Abc>(option => option.UseSqlServer(Configuration.GetConnectionString("ConStr")));
It was because of
private readonly IMemoryCache _memoryCache;
when i remove it every think work fine
Using Asp.Net Core we can make use of Dependency Injection in controllers/repositories.
However, I wish do do some logging in my Entity Class.
class Person
{
private ILogger<Person> _logger;
private List<Pets> pets;
public Person(ILogger<Person> logger)
{
_logger = logger;
}
public bool HasCat()
{
_logger.LogTrace("Checking to see if person has a cat.");
// logic to determine cat ownership
hasCat = true;
return hasCat;
}
}
When the Person class is instantiated by EntityFramework it does not attempt to inject any dependencies.
Can I force this? Am i going about it in completely the wrong way?
Ultimatley I just want to be able to use logging consistently throughout the application.
Thanks,
It is possible but I don't recommend it because I agree with commenters that logging belongs in your services and controllers.
EF Core 2.1 allows injecting the DbContext into a private constructor that EF will invoke. See the official docs.
First you need to expose a LoggerFactory property in your DbContext class.
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options, ILoggerFactory loggerFactory = null)
{
LoggerFactory = loggerFactory;
}
public ILoggerFactory LoggerFactory { get; }
}
Then you can inject the DbContext into a private constructor in your entity class.
public class Person
{
private readonly ILogger _logger;
public Person() { } // normal public constructor
private Person(MyDbContext db) // private constructor that EF will invoke
{
_logger = db.LoggerFactory?.CreateLogger<Person>();
}
public bool HasCat()
{
_logger?.LogTrace("Check has cat");
return true;
}
}