Leveraging user context in an IHostedService via DI - .net-core

I have a series of class libraries that are used in asp.net-core middleware, and in an IHostedService.
To fetch the user context, I can inject IHttpContextAccessor to grab the HttpContext user:
public class MyLibrary
{
public MyLibrary(IHttpContextAccessor accessor)
{
// set the accessor - no problem
}
public async Task DoWorkAsync(SomeObject payload)
{
// get the user from the accessor
// do some work
}
}
To be a little more abstract, I have an IUserAccessor with an HttpUserAccessor implementation:
public class HttpUserAccessor: IUserAccessor
{
IHttpContextAccessor _httpaccessor;
public HttpUserAccessor(IHttpContextAccessor accessor)
{
_httpaccessor = accessor;
}
public string GetUser()
{
// return user from _httpaccessor
}
}
and then MyLibrary does not need an IHttpContextAccessor dependency:
public class MyLibrary
{
public MyLibrary(IUserAccessor accessor)
{
// set the accessor - no problem
}
public async Task DoWorkAsync(SomeObject payload)
{
// get the user from the accessor
// do some work
}
}
My IHostedService is popping message from a queue, where the message includes:
a user context, and
a serialized SomeObject to pass to MyLibrary.DoWorkAsync
So, something like:
public class MyHostedService : IHostedService
{
IServiceScopeProvider _serviceScopeFactory;
public MyHostedService(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = servicesScopeFactory;
}
public Task StartAsync(CancellationToken cancellationToken)
{ ... }
public Task StopAsync(CancellationToken cancellationToken)
{ ... }
public async Task ExecuteAsync(CancellationToken stoppingToken)
{
foreach (var message in queue)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
// todo: tell IUserAccessor what message.User is!
var payload = // create a SomeObject from the queue message
var mylibrary = _services.GetRequiredService<MyLibrary>();
await myLibrary.DoWorkAsync(payload);
}
}
}
}
So, my question is, how does MyHostedService store message.User in such a way that a custom IUserAccessor can access it in a thread-safe manner via DI?

how does MyHostedService store message.User in such a way that a custom IUserAccessor can access it in a thread-safe manner via DI?
The thing you're looking for is AsyncLocal<T> - it's like a thread-local variable but scoped to a (possibly asynchronous) code block instead of a thread.
I tend to prefer a "provider" + "accessor" pairing for this: one type that provides the value, and a separate type that reads the value. This is logically the same thing as a React Context in the JS world, though the implementation is quite different.
One tricky thing about AsyncLocal<T> is that you need to overwrite its value on any change. In this case, that's not really a problem (no message processing will want to update the "user"), but in the general case it's important to keep in mind. I prefer storing immutable types in the AsyncLocal<T> to ensure they aren't mutated directly instead of overwriting the value. In this case, your "user" is a string, which is already immutable, so that's perfect.
First, you'll need to define the actual AsyncLocal<T> to hold the user value and define some low-level accessors. I strongly recommend using IDisposable to ensure the AsyncLocal<T> value is unset properly at the end of the scope:
public static class AsyncLocalUser
{
private static AsyncLocal<string> _local = new AsyncLocal<string>();
private static IDisposable Set(string newValue)
{
var oldValue = _local.Value;
_local.Value = newValue;
// I use Nito.Disposables; feel free to replace with another IDisposable implementation.
return Disposable.Create(() => _local.Value = oldValue);
}
private static string Get() => _local.Value;
}
Then you can define a provider:
public static class AsyncLocalUser
{
... // see above
public sealed class Provider
{
public IDisposable SetUser(string value) => Set(value);
}
}
and the accessor is similarly simple:
public static class AsyncLocalUser
{
... // see above
public sealed class Accessor : IUserAccessor
{
public string GetUser() => Get();
}
}
You'll want to set up your DI to point IUserAccessor to AsyncLocalUser.Accessor. You can also optionally add AsyncLocalUser.Provider to your DI, or you can just create it directly.
Usage would go something like this:
foreach (var message in queue)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var userProvider = new AsyncLocalUser.Provider(); // (or get it from DI)
using (userProvider.SetUser(message.User))
{
var payload = // create a SomeObject from the queue message
var mylibrary = _services.GetRequiredService<MyLibrary>();
await myLibrary.DoWorkAsync(payload);
}
}
}

Related

Error in CreateInstance() while dynamically creating object of concrete type in Factory Pattern

I am actually new to design patterns and trying to implement factory pattern with .NET Core.
I tried to see couple of posts related to factory pattern and trying to implement it, I have added the concrete types in the config and reading it as dictionary in my code -
My Factory Interface -
public interface IEmpFactory
{
public BaseEmployee CreateEmployeeType<EmpType>()
where EmpType : BaseEmployee, new();
}
Implementation -
public class EmpFactoryImpl : IEmpFactory
{
public BaseEmployee CreateEmployeeType<EmpType>()
where EmpType: BaseEmployee, new()
{
return new EmpType();
}
}
Below are my services which are using the Factory as dependency -
public interface IEmpService
{
public string GetEmployeeBonus();
}
public class ContractEmpService : IEmpService
{
IEmpFactory _empFactory;
public ContractEmpService(IEmpFactory empFactory) =>
_empFactory = empFactory;
private BaseEmployee CreateMyEmployee() =>
_empFactory.CreateEmployeeType<ContractEmp>();
public string GetEmployeeBonus() =>
return CreateMyEmployee().GetBonus();
}
public class PermEmpService : IEmpService
{
private readonly IEmpFactory _empFactory;
public PermEmpService(IEmpFactory empFactory) =>
_empFactory = empFactory;
private BaseEmployee CreateMyEmployee() =>
_empFactory.CreateEmployeeType<PermEmp>();
public string GetEmployeeBonus() =>
CreateMyEmployee().GetBonus();
}
Added these concrete types in the config -
"ConfigurationProps": {
"EmpServices": {
"PermEmp": "SimpleFactoryWithoutSwitchCase.Service.PermEmpService",
"ContractEmp": "SimpleFactoryWithoutSwitchCase.Service.ContractEmpService"
}
}
Created the class to create a instance of the concrete type based on the type i.e, PermEmp or ContractEmp dynamically -
public class EmployeeTypeRouter : IEmployeeTypeRouter
{
private readonly ConfigurationProps _props;
public EmployeeTypeRouter(ConfigurationProps props)
{
_props = props;
}
public IEmpService GetInstance(string key)
{
string className = _props.EmpServices
.Where(k => k.Key.Equals(key)).FirstOrDefault().Value;
Type t = Type.GetType(className);
return (IEmpService)Activator.CreateInstance(t);
}
}
This is my calling method -
[HttpGet(Name = "GetEmployeeBonus")]
public string Get()
{
string type = "PermEmp";
IEmpService empService = _empRouter.GetInstance(type);
return empService.GetEmployeeBonus();
}
based on the type passed here i want to fetch the concrete type and call the method.
I am getting the error like this on CreateInstance method -
System.MissingMethodException: `Cannot dynamically create an instance of type 'SimpleFactoryWithoutSwitchCase.Service.PermEmpService'. Reason: No parameterless constructor defined.'
Which is very clear, but I don't want to create a parameterless constructor.
Since I am registering the dependencies in .NET Core, do I need to pass it again here? (which does not make sense for me)
Any help is really appreciated or if you feel I am doing something wrong please let me know.
Your EmployeeTypeRouter class tries to replicate the creation process that your DI Container can do more eloquently. So instead of calling Activator.CreateInstance, forward the resolution to the DI Container.
This means the following things:
Register all known IEmpService at startup.
Resolve the expected type from the IServiceProvider from inside the EmployeeTypeRouter.
In other words, change the startup code to the following:
var dictionary = props.EmpServices
.ToDictionary(p => p.Key, p => Type.GetType(p.Value));
foreach (string pair in dictionary)
{
services.AddTransient(pair.Value);
}
services.AddTransient<IEmployeeTypeRouter, EmployeeTypeRouter>();
services.AddTransient<Func<string, IEmpService>>(sp =>
key => (IEmpService)sp.GetRequiredService(dictionary[key]));
And change EmployeeTypeRouter to the following:
public class EmployeeTypeRouter : IEmployeeTypeRouter
{
private readonly Func<string, IEmpService> _factory;
public EmployeeTypeRouter(Func<string, IEmpService> factory)
{
_factory = factory;
}
public IEmpService GetInstance(string key) =>
_factory.Invoke(key);
}
In the previous code snippet, EmployeeTypeRouter makes use of the Func<string, IEmpService> delegate, which functions as factory. Under the covers the delegate calls back into the IServiceProvider.
There are of course several ways to skin a cat. You could also move some of the startup logic into EmployeeTypeRouter, or even remove the IEmployeeTypeRouter altogether and let application code depend directly on Func<string, IEmpService> delegate.

Action filter : how to call service layer and async method

I have a controller with many action method. The requirement for me is to check a value of a field from database and if the field value is "true" all the action methods can execute otherwise these action methods should not execute.
The method is in service layer
public class CustomAttributeFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var myFlag = await _adminDB.GetFlagSettingsAsync();
// how do i call async method from OnActionExecuting filter
if (!myFlag)
{
//Create your result
filterContext.Result = new EmptyResult();
}
else
{
base.OnActionExecuting(filterContext);
}
}
}
Interface implementaion
public interface IAdminDB
{
Task<MySettings> GetMySettingsAsync();
}
public class AdminDB : IAdminDB
{
public async Task<MySettings> GetMySettingsAsync()
{
var dbName = _appSettings.AdminDbName;
var blobName = _appSettings.AdminBlobName;
return await _dbStorage.GetBlobAsync<MySettings>(blobName, dbName);
}
}
public class MySettings
{
public bool MyFlag { get; set; }
}
I get an error message "no suitable method found to override". How do i clear this error and how to inject service properly . Above is what i have tried, the call to async getting failed here.
I don't see where the _adminDB dependency comes from in your code, but I'm guessing that is causing the problem.
If you want to use async filters you have to implement the IAsyncActionFilter interface.
You can retrieve services from the executing context's DI container and use async methods the following way:
public class CustomAttributeFilter : ActionFilterAttribute
{
public override async Task OnActionExecutionAsync(
ActionExecutingContext context, ActionExecutionDelegate next)
{
var adminDb = filterContext.HttpContext.RequestServices.GetService<AdminDb>();
var myFlag = await adminDb.GetFlagSettingsAsync();
//..
await next();
}
}
Depending on your your needs, you can place your custom logic after the next() call as well.
See the documentation for more information.

Is it possile to return .NET object from controller to middleware

I was working on one of the requirements, where I need to modify result data in middleware (not any MVC Filters due to some other services injected through middleware).
In middleware I was getting data in json format and then deserializing that data then updating that data and finally serializing to JSON and sending it back as a response.
I don't want to serialize data in MVC pipeline so I tried to remove output formator but that didn't work for me and throwing error.
services.AddControllers(options =>
{
options.OutputFormatters.Clear();
});
Is there any solution to get the .Net object in the pipeline and modify that object (as we do in MVC filter) and then serialize at last?
I am not sure whether it fits your requirements but you can use HttpContext to store some data in the scope of the request. There is a 'Items' key-value collection.
Beside the other suggestion to use Items of HttpContext, I want to note that you can inject services into Action Filters:
public class ResultFilter : IActionFilter
{
// Inject anything you want
IHostEnvironment env;
public ResultFilter(IHostEnvironment env)
{
this.env = env;
}
public void OnActionExecuted(ActionExecutedContext context)
{
if (context.Result is OkObjectResult result)
{
result.Value = JsonSerializer.Serialize(new
{
Value = result.Value,
Environment = this.env.EnvironmentName,
});
}
}
public void OnActionExecuting(ActionExecutingContext context) { }
}
Register to DI Builder:
services.AddScoped<ResultFilter>();
Apply to action/controller:
[HttpGet, Route("/test"), ServiceFilter(typeof(ResultFilter))]
public IActionResult ReturnOk()
{
return this.Ok(new
{
Value = 1,
});
}
Testing by accessing the URL:
{"Value":{"Value":1},"Environment":"Development"}
Another alternative is to use DI service with Scoped lifetime.
Scoped objects are the same for a given request but differ across each new request.
Service:
public interface IMyRequestDataService
{
object? MyData { get; set; }
}
public class MyRequestDataService : IMyRequestDataService
{
public object? MyData { get; set; }
}
Register to DI:
services.AddScoped<IMyRequestDataService, MyRequestDataService>();
Set data in Controller:
readonly IMyRequestDataService dataService;
public TestController(IMyRequestDataService dataService)
{
this.dataService = dataService;
}
[HttpGet, Route("/test-scoped")]
public IActionResult ReturnObj()
{
this.dataService.MyData = new
{
Value = 1,
};
return this.Ok();
}
Your middleware that consumes it:
class CustomMiddleware
{
readonly RequestDelegate next;
public CustomMiddleware(RequestDelegate next)
{
this.next = next;
}
// Add DI Services here
public async Task InvokeAsync(HttpContext httpContext, IMyRequestDataService dataService, IHostEnvironment env)
{
await this.next(httpContext);
// Data should be here
if (dataService.MyData != null)
{
// Do something with it
await httpContext.Response.WriteAsJsonAsync(new
{
Data = dataService.MyData,
Env = env.EnvironmentName,
});
}
}
}
// Register it:
app.UseMiddleware<CustomMiddleware>();
// Make sure it's before the Controller middleware since we wrap it around the next()
// ...
app.MapControllers();
Test with the URL:
{"data":{"value":1},"env":"Development"}
You can store data in HTTP context items.
In controller action:
Request.HttpContext.Items.Add("SomeKey", data);
In middleware:
object data = httpContext.Items["SomeKey"];

Data Migration into Service Fabric Stateful Service

I have a stateful service that stores a bunch of data about my users that is stored in a reliable dictionary and obviously also retrieves it from there too.
However, I also have a SQL database that used to store this info. On initialization of a new stateful service instance, I need to migrate that info from my SQL database into the new reliable storage mechanism. From that point on, the stateful service is the source of truth. Ideally, I'd like to delay availability of my stateful service until this initialization process is completed.
Are there any suggestions on an approach for how to do this?
Something like does will do the trick:
public interface IStateful1 : IService
{
Task MyMethod();
}
internal sealed class Stateful1 : StatefulService, IStateful1
{
private bool isReady = false;
public Stateful1(StatefulServiceContext context)
: base(context)
{ }
public Task MyMethod()
{
if(!isReady)
throw new NotImplementedException(); // Probably throw or return something more meaningful :-)
return Task.CompletedTask; // Do your thing here
}
protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
{
return new ServiceReplicaListener[0];
}
protected override async Task RunAsync(CancellationToken cancellationToken)
{
await Task.Run(() => {
// Simulation of some work
Thread.Sleep((int)TimeSpan.FromMinutes(5).TotalMilliseconds);
});
isReady = true;
}
}
In this setup the import from the DB into the reliable collection is done in the RunAsync method.
Unfortunately, AFAIK, there is not way to plug in the communication listeners at a later time. That would make things way easier.
If CreateServiceReplicaListeners would be an async operation we could await the initialization task here, but we can't right now. Using .Wait() is not going to work as it will report that the instance is taking to long to get running and will mark the instance as unhealthy.
A complete overview of the lifecycle of a service can be found in the docs
I am not sure if I got you right. But based on your comment I would suggest the following solution for returning the 'Not ready' response during the migration.
public interface IMigrationService
{
bool IsDone();
}
public class MigrationService : IMigrationService
{
private bool migrating = tu;
public bool BeginMigration()
{
this.migrating = true;
}
public bool EndMigration()
{
this.migrating = false;
}
public bool IsDone()
{
return this.migrating;
}
}
// WebHost startup class
public class Startup
{
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// Register a middle-ware that would short circuit responses
// while migration is in progress.
app.Use(
async (context, next) =>
{
var migrationService =
context.RequestServices.GetService<IMigrationService>();
if (!migrationService.IsDone())
{
/* short circuit the response with approriate error message */
}
await next();
});
app.UseMvc();
}
}
public class Stateful : StatefulService
{
private readonly IMigrationService migrationService;
public Stateful(StatefulServiceContext context)
: base(context)
{
this.migrationService = new MigrationService();
}
protected override IEnumerable<ServiceReplicaListener>
CreateServiceReplicaListeners()
{
/*
Create a listener here with WebHostBuilder
Use Startup class with the middle-ware defined and
add configure services -> .ConfigureServices()
with services.AddSingleton<IMigrationService>(this.migrationService)
*/
}
protected override async Task
RunAsync(CancellationToken cancellationToken)
{
this.migrationService.StartMigration();
/* Migration code */
this.migrationService.EndMigration();
}
}
This would allow you to roll-out a new version of the service that would short circuit all requests with appropriate error message while the migration is in progress.
Hope this helps.

How can I access the workflow ID in BeginOnLoad / PersistenceIOParticipant?

I am using a class named:
public class ProcessFlowPersistenceIOParticipant : PersistenceIOParticipant
It inherits from System.Activities.Persistence.PersistenceIOParticipant in the WF4 framework.
I would like to override the BeginOnLoad(..) method.
In this override I would like to intercept the deserialization of the workflow instance and inject the business entity from the entity database into the entity workflow variable.
BeginOnLoad is fired when the workflow instance is loaded from the instance store.
via: workflowApplication.Load(workflowInstanceGuid);
I can see the workflow guid inside the readwritevalues dictionary.
Find the key in readWriteValues.Keys matching namespace
"urn:schemas-microsoft-com:System.Activities/4.0/properties"
Then the guid is inside:
value = readWriteValues[xName];
_workflowInstanceId = ((System.Activities.Runtime.ActivityExecutor)value).WorkflowInstanceId;
It is accessible via a debugger but not in code as ActivityExecutor is an internal class.
So is there a way to determine what workflow instance is being loaded inside BeginOnLoad?
Any tips much appreciated.
Mike
This question is similar to Access workflow id from inside BeginOnSave.
However in the case of BeginOnLoad there is no activity execution context available to get the workflow instance guid.
If you also implement the IWorkflowInstanceExtension interface in the ProcessFlowPersistenceIOParticipant you should be able to get at the workflow ID through the WorkflowInstanceProxy in the SetInstance() function.
public class TaskActivity : NativeActivity
{
protected override void Execute(NativeActivityContext context)
{
context.GetExtension<RelevantDataExtensionIO>().WorkflowInstanceId = context.WorkflowInstanceId;
}
}
public class RelevantDataExtensionIO : PersistenceIOParticipant
{
public const string NamespaceWorkflowInstanceId = "WorkflowInstanceId";
public Guid WorkflowInstanceId { get; set; }
public static XNamespace Namespace
{
get { return XNamespace.Get("http://sample.com/RelevantDataIO"); }
}
public RelevantDataExtensionIO()
: base(false, false)
{
}
protected override void CollectValues(out IDictionary<XName, object> readWriteValues, out IDictionary<XName, object> writeOnlyValues)
{
readWriteValues = new Dictionary<XName, object>();
readWriteValues.Add(Namespace.GetName(NamespaceWorkflowInstanceId), this.WorkflowInstanceId);
writeOnlyValues = null;
}
protected override IAsyncResult BeginOnLoad(IDictionary<XName, object> readWriteValues, TimeSpan timeout, AsyncCallback callback, object state)
{
Guid id = this.WorkflowInstanceId;
return base.BeginOnLoad(readWriteValues, timeout, callback, state);
}
}

Resources