c# asp.net core 3 calling different methods from the controller, depending on the request body - asp.net-core

I have a controller with the following content (simplified version):
[HttpPost]
public Task<OkResult> Post([FromBody] commonRequest)
{
parser.DoWork(commonRequest);
return Ok();
}
The commonRequest object is populated from the incoming JSON request.
The parser.DoWork method should invoke the creation of a new instance of the class, depending on requestBody.
Here's what it looks like:
public class CommonParser : ICommonParser
{
private readonly ILogger<CommonParser> logger;
private IServiceProvider serviceProvider;
public CommonParser(ILogger<CommonParser> _logger, IServiceProvider _serviceProvider)
{
this.logger = _logger;
this.serviceProvider = _serviceProvider;
}
public void DoWork(CommonRequest commonRequest)
{
ICommonParser parser = (ICommonParser)Activator.CreateInstance(Type.GetType(commonRequest.instance)
, serviceProvider);
parser.DoWork(commonRequest);
}
}
I have three classes whose names are passed through commonRequest.instance. All of these classes implement the ICommonParser interface. Inside these classes, I pass a serviceProvider so that they can get the ILogger inside themselves and use it.
Here is an example constructor of this class:
private readonly ILogger<Parser1> logger;
public Parser1(IServiceProvider serviceProvider)
{
this.logger = serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger<Parser1>();
}
As a result, I can send only one message in this way. On the second call, I get a message that serviceProvider.GetRequiredServiceILoggerFactory () has been destroyed.
Please tell me what to do in such cases. I think I'm designing wrong.

From Dependency Injection in ASP.NET Core:
Avoid using the service locator pattern. For example, don't invoke
GetService or GetRequiredService to obtain a service instance when you
can use DI instead.
1) register the logger factory or the logger service, in case of the logger factory
services.AddSingleton<ILoggerFactory, LoggerFactory>();
2) use constructor injection to inject logger factory into the constructor
public Parser1(ILoggerFactory loggerFactory)
{
}
3) you might create a new interface for the parsers (parser1, 2, 3). The parsers implement this interface. Register them as services
public interface IParser
{
void DoWork(CommonRequest commonRequest);
}
services.AddTransient<Parser1>(); // implements IParser
services.AddTransient<Parser2>();
This post gives an answer how to resolve classes implementing the same interface. For getting parser with DI you will actually need IServiceProvider:
_serviceProvider.GetService<Parser1>();

Related

Creating Singleton CacheManager in Asp.Net Core

I am trying to create Singleton CacheManager class that has dependency on IMemoryCache.
public class CacheManager:ICacheManager
{
private readonly IMemoryCache _cache;
public CacheManager(IMemoryCache cache)
{
_cache = cache;
}
public void LoadCache(MyData data)
{
// load cache here at startup from DB
}
}
I also have a Scoped service that retrives data from the database
public class LookupService:ILookupService
{
private readonly MyDatabaseContext _dbContext;
public class LookupService(MyDatabaseContext dbContext)
{
_dbContext = dbContext;
}
public void Dispose()
{
//Dispose DBContext here
}
// some async methods that returns lookup collection
}
Register these services in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// EF
services.AddDbContext<MyDatabaseContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
// domain services
services.AddScoped<ILookupService, LookupService>();
services.AddMemoryCache();
// singleton
services.AddSingleton<CacheManager>(sp=>
{
using(var scope = sp.CreateScope())
{
using (var service = scope.ServiceProvider.GetService<ILookupService>())
{
how do i create cacheManager instance by injecting IMemoryCache and also register callback function
}
}
});
}
ILookupService is registered as Scoped service becuase it has dependency on DBContext which is also (by default) registered with Scoped lifetime. I do not want to change lifetime of these services.
However I want CacheManager to be registered as Singleton, that means I cannot inject ILookupService as dependency into CacheManager.
So here is my possible solution to create & register singleton instance of CacheManager
services.AddSingleton<CacheManager>(sp=>
{
using(var scope = sp.CreateScope())
{
using (var lookupService = scope.ServiceProvider.GetService<ILookupService>())
{
var cache = scope.ServiceProvider.GetService<IMemoryCache>();
var manger = new CacheManager(cache);
manger.LoadCache(lookupService.GetData());
return manger;
}
}
});
Not sure this is the best way to create CacheManager. How do I implement a callback function to re-populate CacheEntry if it becomes null?
I guess I would simply configure services.AddSingleton<CacheManager>();
(CacheManager having a default constructor)
After configuring all of the DI dependencies and having a serviceprovider, get the Cachemanager singleton and initialize it with LoadCache.
(so let DI create "empty" singleton cachemanager, but initialize immediately somewhere in startup of application)
var cachemanager = scope.ServiceProvider.Get<CacheManager>();
var lookupService = scope.ServiceProvider.Get<ILookupService>();
var cache = scope.ServiceProvider.Get<IMemoryCache>();
cachemanager.Cache = cache;
cachemanager.LoadCache(lookupService.GetData());
Looks like the underlying issue is that ILookupService cannot be resolved until runtime and requests start coming in. You need to create CacheManager before this.
DI COMPOSITION
This should be done when the app starts - as in this class of mine. Note the different lifetimes for different types of object but I just focus on creating the objects rather than interactions.
DI RESOLUTION
.Net uses a container per request pattern where scoped objects are stored against the HttpRequest object. So a singleton basically needs to ask for the current ILookupService, which is done by calling:
container.GetService<ILookupService>
So include the DI container as a constructor argument to your CacheManager class and you will be all set up. This is the service locator pattern and is needed to meet your requirement.
An alternative per request resolution mechanism is via the HttpContext object as in this class, where the following code is used:
IAuthorizer authorizer = (IAuthorizer)this.Context.RequestServices.GetService(typeof(IAuthorizer));
SUMMARY
The important thing is to understand the above design pattern, and you can then apply it to any technology.
register Cache service as singleton, try below code
public class CacheService : ICacheService
{
private ObjectCache _memoryCache;
/// <summary>
/// Initializes a new instance of the <see cref="CacheService"/> class.
/// </summary>
public CacheService()
{
this._memoryCache = System.Runtime.Caching.MemoryCache.Default;
}
}

ASP.NET Core Data Access Layer Custom Class AddSingleton

I have my EntityFrameworkCore DBContext in ConfigureServices
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyDBContext>(Options => ... );
...
}
I also have my data access layer factory class which receives DBContext in constructor
public partial class DataAccessFactory
{
public readonly ProductsDataAccess Products;
public readonly CategoriesDataAccess Categories;
public DataAccessFactory(MyDBContext db)
{
Products = new ProductsDataAccess(db);
Categories = new CategoriesDataAccess(db);
}
}
In order to work with Data Access Layer, I have to create new instance of DataAccessFactory per each request.
My question is, does it make sence and is there any way to create one instance of DataAccessFactory and add it as a Singleton?
You can register your service inside ConfigureServices in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSingleton<DataAccessFactory>();
}
and inject it where you needed:
public class MyController
{
public readonly DataAccessFactory Factory;
public MyController(DataAccessFactory factory)
{
Factory = factory;
}
}
It will be created only once per application life. If you instead will want to change it scope for example per request, just change AddSingleton to AddScoped.
Update:
But be careful when mixing different-scoped services. You cannot inject short living object into long living, because it will cause exceptions. In your situation you will need to change DbContext scope to singleton (sic) or consider to change Factory lifetime to Scoped. Here is example how to change DbContext scope.

How to inject HttpHeader value in controller?

I have Web API developed using ASP.NET Core API. Every incoming request has a custom header value inserted. eg x-correlationid. The controller use this value for logging and tracing the request.
Currently I'm reading the value in each controller as below
[Route("api/[controller]")]
public class DocumentController : Controller
{
private ILogger<TransformController> _logger;
private string _correlationid = null;
public DocumentController(ILogger<DocumentController > logger)
{
_logger = logger;
_correlationid = HttpContext.Request.Headers["x-correlationid"];
}
[HttpPost]
public async Task<intTransform([FromBody]RequestWrapper request)
{
_logger.LogInformation("Start task. CorrelationId:{0}", _correlationid);
// do something here
_logger.LogInformation("End task. CorrelationId:{0}", _correlationid);
return result;
}
}
I think this is against DI rules.
Instead of reading the value inside the controller's constructor, I want to inject the value in the controller's constructor.
Or
Can middleware read the x-correlationid and *somehow* make it available to all the controllers so we don't have to inject it in any controller?
What would be a better option here?
Instead of reading the value inside the controller's constructor, I want to inject the value in the controller's constructor.
You can't inject the value itself into the constructor of the api controller, because at the time of construction the HttpContext is going to be null.
One "injection-style" option would be to use the FromHeaderAttribute in your actions:
[HttpPost]
public async Task<int> Transform(
[FromBody]RequestWrapper request,
[FromHeader(Name="x-correlationid")] string correlationId)
{
return result;
}
Can middleware read the x-correlationid and somehow make it available to all the controllers so we don't have to inject it in any controller?
I think a middleware solution would probably be overkill for what you need. Instead, you can create a custom base class that derives from Controller and have all your Api controllers derive from that.
public class MyControllerBase : Controller
{
protected string CorrelationId =>
HttpContext?.Request.Headers["x-correlationid"] ?? string.Empty;
}
[Route("api/[controller]")]
public class DocumentController : MyControllerBase
{
private ILogger<TransformController> _logger;
public DocumentController(ILogger<DocumentController> logger)
{
_logger = logger;
}
[HttpPost]
public async Task<intTransform([FromBody]RequestWrapper request)
{
_logger.LogInformation($"Start task. CorrelationId:{CorrelationId}");
// do something here
_logger.LogInformation($"End task. CorrelationId:{CorrelationId}");
return result;
}
}
This is what I came up with. I think i can also unit test it.
public interface IRequestContext
{
string CorrelationId { get; }
}
public sealed class RequestContextAdapter : IRequestContext
{
private readonly IHttpContextAccessor _accessor;
public RequestContextAdapter(IHttpContextAccessor accessor)
{
this._accessor = accessor;
}
public string CorrelationId
{
get
{
return this._accessor.HttpContext.Request.Headers[Constants.CORRELATIONID_KEY];
}
}
}
then in startup's configureservice method register the adapter
services.AddSingleton<IRequestContext, RequestContextAdapter>();
and inject it in controller
[Route("api/[controller]")]
public class DocumentController : Controller
{
private ILogger<TransformController> _logger;
private IRequestContext _requestContext = null;
public DocumentController(ILogger<DocumentController > logger,IRequestContext requestContext)
{
_logger = logger;
_requestContext = requestContext;
}
[HttpPost]
public async Task<intTransform([FromBody]RequestWrapper request)
{
_logger.LogInformation("Start task. CorrelationId:{0}", _requestContext.CorrelationId);
// do something here
_logger.LogInformation("End task. CorrelationId:{0}", _requestContext.CorrelationId);
return result;
}
}
Depending on your needs one of following is suitable:
If you need your header values at action level, then using FromHeaderAttribute sounds better (lighter and easier).
If you need to use this header value in lower layers like Repository or DAL, which will be instantiated before Controller has been initialized, then consider to use middleware to get header values initialized and available for other components.

How to resolve EF7 current database context in ASP NET 5 out of the controller?

I want to get one context per request in ASP NET 5/EF 7 app for use it in some methods (not in controller).
Unfortunately I did not find the answer in the documentation
ASP.NET vNext template and examples aspnet/MusicStore
You may use some methods for achieving this purpose.
Using .AddDbContext<ApplicationDbContext>(); method for registering ApplicationDbContext in Dependency Injection system (in ConfigureServices() method), leads to the fact that it registered as Scoped dependence(or in another words "per request"). Thereby you only need get it from Dependency Injection system.
Add your dbContext as parameter of constructor method your class (in which you will use dbContext). Then you have to get this class using Dependency Injection system, e.g added it as parameter of controller's constructor.
public class HabitsController : Controller
{
public HabitsController(HabitService habitService)
{
}
}
public class HabitService
{
private GetHabitsContext _dbContext;
public HabitService(GetHabitsContext dbContext)
{
_dbContext = dbContext;
}
}
But if you don't want to use constructor injection for getting context, you can get necessary dependenses using GetService() method (but you need in ServiceProvider instance for that, in example below, i'am getting it through constructor injection too).
using Microsoft.Framework.DependencyInjection; // for beta 6 and below
using Microsoft.Extensions.DependencyInjection; // for beta 7 and above
public class HabitService
{
private IServiceProvider _serviceProvider;
public HabitService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public GetHabit()
{
var dbcontext = _serviceProvider.GetService<ApplicationDbContext>();
}
}
In first method, we can get HabitService through GetService() method too (not through the constructor injection).
using Microsoft.Framework.DependencyInjection; // for beta 6 and below
using Microsoft.Extensions.DependencyInjection; // for beta 7 and above
public class HabitsController : Controller
{
public HabitsController(IServiceProvider serviceProvider)
{
var habitService= serviceProvider.GetService<HabitService>();
}
}
public class HabitService
{
private GetHabitsContext _dbContext;
public HabitService(GetHabitsContext dbContext)
{
_dbContext = dbContext;
}
}
Thanks Tseng for remark:
I should be noted, that it's a pretty bad practice to inject the container into your objects. The container should only be referenced from the composition root and certain type of factories (which are implemented on application level, and not in the domain/business layer)
dbContext in HabitsController and _dbContext in HabitService are different contexts!
I checked, this is the same context.

Entity framework DbContext in wcf per call instance mode

I have a repository like this
public abstract class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected DbContext _dbContext;
public BaseRepository(DbContext dbContext)
{
_dbContext = dbContext;
}
public TEntity GetByKey(object keyValue)
{
// todo
}
}
and a concrete repository like this
public CustomerRepository : BaseRepository<Customer> , ICustomerRepository
{
public CustomerRepository(DbContext context) : base (context) { }
public Customer FindCustomerByKey(string key)
{
_dbContext.Set<Customer>().Find(key);
}
}
I have wcf service like this
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class CustomerSatisfactionService : ICustomerSatisfactionService
{
private ICustomerRepository _customerRepository;
private IHelpDeskRepository _helpdeskRepository;
public AccountService(ICustomerRepository customerRepository,IHelpdeskRepository helpdeskRepository)
{
_customerRepository = customerRepository;
_helpdeskRepository = helpdeskRepository;
}
public void DoSomethingUsingBothRepositories()
{
// start unit of work
// _customerRepository.DoSomething();
// _helpdeskRepository.DoSomething();
// commit unit of work
}
}
and I am using StructureMap for injecting DbContext instances like this
For<DbContext>().Use(() => new MyApplicationContext());
My problem is when a client calls the service, a new CustomerSatisfactionService instance is created, hence new instances of CustomerRepository and HelpdeskRepository are created but with different DbContexts.
I want to implement the unit of work pattern, but in the DoSomethingWithBothRepositories method, the two repositories have different DbContexts.
Is there any way to tell structure map to spin up a DbContext instance on a per call basis?
You must specify lifecycle for your DbContext so that only one instance is created per call. StructureMap doesn't contain build-in lifecycle management for per call WCF but you can find one implementation on this blog.
You need to implement UnitOfWork pattern so that same context is shared amongst entities. Take a look at http://blogs.msdn.com/b/adonet/archive/2009/06/16/using-repository-and-unit-of-work-patterns-with-entity-framework-4-0.aspx for a way to implement it.
I don't know if you need/want StructureMap to control the instantiation of dbcontext, have a look at this reply, UoW and UoW Factory setup dbcontext for the calls that need to be made in the repository.
EF ObjectContext, Service and Repository - Managing context lifetime.