Configure/register depdency injection scoped service from within the scope - dependency-injection

I have a stateless service in Azure Service Fabric, and I'm using Microsoft.Extensions.DependencyInjection, although the same issue exists for any other DI frameworks. In my Program.cs, I create a ServiceCollection, add all (but one) of my registrations, create the service provider, and pass it to my service's constructor. Any service method with external entry will create a new service scope and call the main business logic class. The issue is that one of the classes I want to have scoped lifetime needs a value that is an input parameter on the request itself. Here's a code snippet of what I would like to achieve.
internal sealed class MyService : StatelessService, IMyService
{
private IServiceProvider _serviceProvider;
private IServiceScopeFactory _scopeFactory;
public MyService(StatelessServiceContext context, IServiceProvider serviceProvider)
: base(context)
{
_serviceProvider = serviceProvider;
_scopeFactory = _serviceProvider.GetRequiredService<IServiceScopeFactory>();
}
public async Task<MyResponse> ProcessAsync(MyRequest request, string correlationId, CancellationToken cancellationToken)
{
using (var scope = _scopeFactory.CreateScope())
{
var requestContext = new RequestContext(correlationId);
//IServiceCollection serviceCollection = ??;
//serviceCollection.AddScoped<RequestContext>(di => requestContext);
var businessLogic = scope.ServiceProvider.GetRequiredService<BusinessLogic>();
return await businessLogic.ProcessAsync(request, cancellationToken);
}
}
}
The cancellation token is already passed around everywhere, including to classes that don't use it directly, just so it can be passed to dependencies that do use it, and I want to avoid doing the same with the request context.
The same issue exists in my MVC APIs. I can create middle-ware which will extract the correlation id from the HTTP headers, so the API controller doesn't need to deal with it like my service fabric service does. One way I can make it work is by giving RequestContext a default constructor, and have a mutable correlation id. However, it's absolutely critical that the correlation id doesn't get changed during a request, so I'd really like the safety of having get-only property on the context class.
My best idea at the moment is to have a scoped RequestContextFactory which has a SetCorrelationId method, and the RequestContext registration simply calls the factory to get an instance. The factory can throw an exception if a new instance is requested before the id is set, to ensure no id-less contexts are created, but it doesn't feel like a good solution.
How can I cleanly register read-only objects with a dependency injection framework, where the value depends on the incoming request?

I only had the idea for a RequestContextFactory as I was writing the original question, and I finally made time to test the idea out. It actually was less code than I expected, and worked well, so this will be my go-to solution now. But, the name factory is wrong. I'm not sure what to call it though.
First, define the context and factory classes. I even added some validation checks into the factory to ensure it worked the way I expect:
public class RequestContext
{
public RequestContext(string correlationId)
{
CorrelationId = correlationId;
}
public string CorrelationId { get; }
}
public class RequestContextFactory
{
private RequestContext _requestContext;
private bool _used = false;
public void SetContext(RequestContext requestContext)
{
if (_requestContext != null || requestContext == null)
{
throw new InvalidOperationException();
}
_requestContext = requestContext;
}
public RequestContext GetContext()
{
if (_used || _requestContext == null)
{
throw new InvalidOperationException();
}
_used = true;
return _requestContext;
}
}
Then, add registrations to your DI container:
services.AddScoped<RequestContextFactory>();
services.AddScoped<RequestContext>(di => di.GetRequiredService<RequestContextFactory>().GetContext());
Finally, the Service Fabric service method looks something like this
public async Task<MyResponse> ProcessAsync(MyRequest request, string correlationId, CancellationToken cancellationToken)
{
using (var scope = _scopeFactory.CreateScope())
{
var requestContext = new RequestContext(correlationId);
var requestContextFactory = scope.ServiceProvider.GetRequiredService<RequestContextFactory>();
requestContextFactory.SetContext(requestContext);
var businessLogic = scope.ServiceProvider.GetRequiredService<BusinessLogic>();
return await businessLogic.ProcessAsync(request, cancellationToken);
}
}
Kestrel middleware could look something like this
public async Task Invoke(HttpContext httpContext)
{
RequestContext requestContext = new RequestContext(Guid.NewGuid().ToString());
var factory = httpContext.RequestServices.GetRequiredService<RequestContextFactory>();
factory.SetContext(requestContext);
httpContext.Response.Headers["X-CorrelationId"] = requestContext.CorrelationId;
await _next(httpContext);
}
Then just do the normal thing and add a RequestContext parameter to the constructor of any class that needs to get the correlation id (or any other info you put in the request context)

Related

Configure Unity container per-request in OWIN middleware

I'm wanting to configure registrations in a Unity container being used by ASP.NET Web API 2 based on properties of a HTTP request. For example, a request to /api/database1/values should result in a Unity container configuration with an IDbContext configured for database1, while a request to /api/database4/values will get an IDbContext configured for database4.
I've gotten so far as using UnityHierarchicalDependencyResolver as the dependency resolver, so types registered with HierarchicalLifetimeManager last only for the lifetime of the request. This works well for getting types resolved per request. But how to get them registered per request using OWIN middleware is beyond me.
In my middleware, a call to System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IUnityContainer)) gets an instance of IUnityContainer, but it's the same container for all requests, including any registrations from previous requests.
By encapsulating UnityHierarchicalDependencyResolver with my own implementation of IDependencyResolver I can see that IDependencyResolver.BeginScope isn't called until much later in the process. So the problem would seem to be that the child container isn't created until Web API wakes up, long after my middleware calls Next(..).
Is there a way I can get the scope of my dependency resolver to start sooner? Is there some other strategy that I'm missing. In case it makes any difference, I'm hosting in IIS, but favouring the OWIN middleware approach.
Update
This isn't an answer, and it's too big for a comment, but after struggling to solve this with Unity I decided to switch to Autofac and it all just fell into place.
The Autofac OWIN packages (Autofac.Mvc5.Owin, Autofac.Owin, Autofac.WebApi2.Owin) make it dead easy to use Autofac within the OWIN pipeline and ensure appropriate lifetime management in ASP.NET MVC and Web API. This was the missing link.
I couldn't find a way to reconfigure the container per-request, but it did at least make it possible to configure a factory per-request (so yes, #Haukinger and #alltej, you were right to push in that direction.
So I register a factory like:
builder.RegisterType<DataDependencyFactory>().InstancePerRequest();
And register the create method of that factory like:
builder
.Register(c => c.Resolve<DataDependencyFactory>().CreateDataDependency())
.As<IDataDependency>()
.InstancePerRequest();
Registering the factory this way is particularly useful, because downstream dependents don't need to be aware of the factory. I like this because my dependents don't need a factory, they need an instance. The container bends to the needs of my dependents, not the other way around :)
Then, in a piece of OWIN middleware, I resolve the factory, and set a property on it according to the properties of the request. Subsequent resolution of IDataDependency in an MVC or Web API controller, or anything else later in the OWIN pipeline, will get an instance configured according to the property on the factory.
Based on your api URL ("/api/database4/values"), I suggest that you create a filter attribute(e.g. DbIdFilter) so that you can reuse the filter attribute to other controller methods that follow similar url path/segment like this below:
[HttpGet]
[DbIdFilter]
[Route("{databaseId}/values")]
public IHttpActionResult GetValues()
{
return Ok();
}
[HttpGet]
[DbIdFilter]
[Route("{databaseId}/products")]
public IHttpActionResult GetProducts()
{
return Ok();
}
First, create the filter attribute:
public class DbIdFilterAttribute : ActionFilterAttribute
{
private readonly string _routeDataId;
private const string defaultRouteName = "databaseId";
public DbIdFilterAttribute():this(defaultRouteName)
{}
public DbIdFilterAttribute(string routeDataId)
{
_routeDataId = routeDataId;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
var routeData = actionContext.Request.GetRouteData();
var dbId = routeData.Values[_routeDataId] as string;
//here we create the db instance at the filter level.
DbInstanceFactory.RegisterDbInstance(dbId);
}
}
Next, create an instance factory that will register/resolve the db instance during runtime:
public class DbInstanceFactory : IDbInstanceFactory
{
public static IDbInstance RegisterDbInstance(string databaseId)
{
var factory = UnityConfig.GetConfiguredContainer().Resolve<IDbInstanceFactory>();
return factory.CreateInstance(databaseId);
}
public IDbInstance CreateInstance(string databaseId)
{
var container = UnityConfig.GetConfiguredContainer();
//container.RegisterType<IDbInstance, DbInstance>();
container.RegisterType<IDbInstance, DbInstance>(new InjectionConstructor(databaseId));
var dbInstance = container.Resolve<IDbInstance>();
return dbInstance;
}
public IDbInstance GetInstance()
{
var container = UnityConfig.GetConfiguredContainer();
var dbInstance = container.Resolve<IDbInstance>();
return dbInstance;
}
}
public interface IDbInstanceFactory
{
IDbInstance CreateInstance(string databaseId);
IDbInstance GetInstance();
}
Register this factory class in UnityConfig.cs (or wherever you currently register the types):
container.RegisterType<IDbInstanceFactory, DbInstanceFactory>
(new ContainerControlledLifetimeManager());
It's registered ContainerControlledLifetimeManager since this factory does not have to be a per request.
So just a basic DbInstance class below(for clarity) that takes a parameter in the constructor (this parameter can be your connection string or a named connection):
public class DbInstance : IDbInstance
{
public string DbId { get; }
public DbInstance(string databaseId)
{
DbId = databaseId;
}
}
public interface IDbInstance
{
string DbId { get; }
}
In controller class, you can use it like this:
....
private IDbInstanceFactory _dbFactory;
public MyController(IDbInstanceFactory dbFactory)
{
_dbFactory = dbFactory;
}
// Alternate, if you want to use property injection instead of constructor injection
//[Dependency]
//public IDbInstanceFactory DbFactory { get; set; }
[HttpGet]
[DbIdFilter]
[Route("{databaseId}/test")]
public IHttpActionResult Test()
{
var db = _dbFactory.GetInstance();
return Ok(db.DbId);
}
...

AddTransient service using info from request

I've got an app in asp.net core using the built-in DI framework. I'd like to add a per-request (i.e. transient) service to the provider, but I'd like to actually make use of the request in its construction.
services.AddTransient<IMyService>(provider => { ... });
That's the closest overload I can find, but the provider object doesn't have anything about the current request. Is there a way to achieve what I'm trying to do, without upgrading to a more robust DI framework?
As posted in the comments, you can inject the IHttpContextAccessor into your services and access it, if the HttpContext is the only thing you need.
public class MyService : IMyService
{
private readonly HttpContext context;
public MyService(IHttpContextAccessor httpContextAccessor)
{
if(IHttpContextAccessor==null)
throw new ArgumentNullException(nameof(httpContextAccessor));
context = httpContextAccessor.HttpContext;
}
}
However, if you need something that's only available in the controller or outside of HttpContext, you can create a factory and pass the parameters to the factory
public class MyServiceFactory : IMyServiceFactory
{
// injecting the HttpContext for request wide service resolution
public MyServiceFactory(IHttpContextAccessor httpContextAccessor) { ... }
public IMyService Create(IDependency1 dep1, IDependency2 dep 2, string someRuntimeConfig)
{
IServiceProvider provider = this.context.RequestServices;
var myService = new MyService(provider.GetService<ISomeRepository>(), dep1, dep2, someRuntimeConfig);
return myService;
}
}
and then inject the IMyServiceFactory to your classes where you'd need IMyService.

How to set argument to Ninject binder regarding on request header

Problem:
I have webapi serviss where almost every user has its own database instance to connect. So i have to set different connection string for each user. To recognize user i will pass specific Token into header. Regarding on this Token, system has to build and set differenct connection string into Data Access layer constructor (Order in this case)
Question:
Is it possible to pass argument to Ninject or any kind of IoC binder regarding on request header?
IOrders _orders;
public HomeController(IOrders order)
{
_orders = order;
}
Here is an Ninject binding, but as you can guess, HttpContext.Current is null.
private static void RegisterServices(IKernel kernel)
{
var some_value = HttpContext.Current.Request.Headers.GetValues("Token");
kernel.Bind<IOrders>()
.To<Orders>()
.WhenInjectedInto<HomeController>()
.WithConstructorArgument("Token", some_value);
}
Maybe there is much elegant way to do this using Controller Factory ?
I would create a service class that does this lookup for you. then inject this service into the Orders implementation.
public interface IRequestContext {
string ConnectionString {get;}
}
public class HttpHeaderRequestContext : IRequestContext {
public string ConnectionString {
get {
var token = HttpContext.Current.Request.Headers.GetValues("Token");
// .. lookup conn string based on token
}
}
}
public class Orders : IOrders {
public Orders(IRequestContext ctx) {
// create new connection w/ ctx.ConnectionString
}
}
using this method, the lookup of headers and connection strings is abstracted away from the implementation. this makes it easier to test and easier swap out with a different method of obtaining a connection string if the need arises.
After implementing Dave approach, i realized that i could solve this connection string injection by feeding HttpContext.Current into Ninject binding like this:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IOrders>()
.To<Orders>()
.WhenInjectedInto<HomeController>()
.WithConstructorArgument("smth", x => {
var token = HttpContext.Current.Request.Headers.Get("Token");
var _db = new SomeDataCxt();
var connStr = _db.DbStringRepository.GetByToken(token);
return connStr;
});
}

Response is not available in this context when creation cookie's

hi i defined one class to create cookie by received parameter's from user. when i want to add cookie to context i receive an exception.
My Class
public static class ManageCookies
{
public static void Create(string name, string value)
{
HttpCookie cookie = new HttpCookie(name);
cookie.Value = value;
cookie.Expires = DateTime.Now.AddYears(1);
HttpContext.Current.Response.Cookies.Add(cookie);
}
}
Occured Exception: Response is not available in this context.
i know it is connected with the context within the current sub is executing.
i whould suggest my function to pass the current HttpResponse as a parametter to that!
public static class ManageCookies
{
public static void Create(string name, string value, HttpResponse response)
{
HttpCookie cookie = new HttpCookie(name);
cookie.Value = value;
cookie.Expires = DateTime.Now.AddYears(1);
response.Cookies.Add(cookie);
}
public static void PrePareForApplicationStart()
{
Create("somecookie", "somevalue", _context);
}
}
this is correct way? why? and are you have another way?
UPDATE: Oppps! using this way still have First Exception! :(( Help Help
ManageCookies manager = new ManageCookies(this.Context);
manager.PrePareForApplicationStart();
i use above code to send HTTPContext object to defined class. it called from Application_Start event.
and ManageCookies class updated as below:
public class ManageCookies
{
private HttpContext _context;
public ManageCookies(HttpContext context)
{
this._context = context;
}
}
i use this variable (_context) to adding cookies!
The code you provided is not a problem the problem is where you call it from. If you call it after the response is flushed or from a thread other than the one assigned to process your request - you can get all sorts of problems
In response to Sadegh clarification:
This is exactly the point. Application Start happens only once during application lifetime. And I guess you want this cookie to be delivered as a part of every response. In other words on ApplicationStart is not the right place to do it. You can do it at any moment during page lifecycle BEFORE the end of the PreRender

What is needed in the HttpContext to allow FormsAuthentication.SignOut() to execute?

I am trying to write a unit test for our log out method. Among other things it FormsAuthentication.SignOut(). However, it throws a System.NullReferenceException.
I've created a mock; HttpContext (using Moq), but it is obviously missing something.
My mock context contains:
A mocked HttpRequestBase on Request
A mocked HttpResponseBase on Response
With a HttpCookieCollection on Request.Cookies and another on Response.Cookies
A mocked IPrincipal on User
I am aware I could go the wrapper route and inject an empty FormsAuth wrapper object in it's place, but I would really like to avoid the 3 additional files just to fix one line of code. That and I am still curious for an answer
So my question is "What is needed in the HttpContext to allow FormsAuthentication.SignOut() to execute."
The NullReferenceException in this case is actually being thrown by the call:
current.Request.Browser["supportsEmptyStringInCookieValue"]
You can test this assertion by calling:
HttpContext.Current.Request.Browser.SupportsEmptyStringInCookieValue
...which will also return the NullReferenceException. Contrary to the accepted answer, if you attempt to call:
CookielessHelperClass.UseCookieless(current, false, CookieMode)
...from the immediate window, this will return without error.
You can fix the exception like this:
HttpContext.Current.Request.Browser = new HttpBrowserCapabilities() { Capabilities = new Dictionary<string, string> { { "supportsEmptyStringInCookieValue", "false" } } };
...and the FormsAuthentication.SignOut() call will now succeed.
You can always wrap FormsAuthentication.SignOut() into another method and stub / mock it.
Create IFormsAuthenticationWrap interface.
public interface IFormsAuthenticationWrap
{
void SignOut();
}
Create wrap class that implements IFormsAuthenticationWrap
public class FormsAuthenticationWrap : IFormsAuthenticationWrap
{
public void SignOut()
{
FormsAuthentication.SignOut();
}
}
Your calling class is going to look something like this:
public class LogOutClass
{
private readonly IFormsAuthenticationWrap _formsAuthentication;
public LogOutClass() : this (new FormsAuthenticationWrap())
{
}
public LogOutClass(IFormsAuthenticationWrap formsAuthentication)
{
_formsAuthentication = formsAuthentication;
}
public void LogOutMethod()
{
// Code before SignOut
_formsAuthentication.SignOut();
// Code after SignOut
}
}
Now let's get to our test. You can stub / mock with Moq but I'm going to show here how you can do it manually.
Create your stub / mock class:
public class FormsAuthenticationStub : IFormsAuthenticationWrap
{
public void SignOut()
{
}
}
And the last write the test:
[TestMethod]
public void TestLogOutMethod()
{
var logOutClass = new LogOutClass(new FormsAuthenticationStub());
logOutClass.LogOutMethod();
}
Here's the code for signout.
public static void SignOut()
{
Initialize();
HttpContext current = HttpContext.Current;
bool flag = current.CookielessHelper.DoesCookieValueExistInOriginal('F');
current.CookielessHelper.SetCookieValue('F', null);
if (!CookielessHelperClass.UseCookieless(current, false, CookieMode) || current.Request.Browser.Cookies)
{
string str = string.Empty;
if (current.Request.Browser["supportsEmptyStringInCookieValue"] == "false")
{
str = "NoCookie";
}
HttpCookie cookie = new HttpCookie(FormsCookieName, str);
cookie.HttpOnly = true;
cookie.Path = _FormsCookiePath;
cookie.Expires = new DateTime(0x7cf, 10, 12);
cookie.Secure = _RequireSSL;
if (_CookieDomain != null)
{
cookie.Domain = _CookieDomain;
}
current.Response.Cookies.RemoveCookie(FormsCookieName);
current.Response.Cookies.Add(cookie);
}
if (flag)
{
current.Response.Redirect(GetLoginPage(null), false);
}
}
Looks like you need a CookielessHelperClass instance. Too bad it's internal and sealed - there's no way to mock it unless you're using TypeMock. +1 for wrapper suggestions :)
The wrapper is the clean way to go.
You mentioned in a comment that "this is going to be quite a big application", that's another argument to use the wrapper not the opposite. In a big application you want to have clear dependencies, and you want tests to be done easily.
You are trading clean dependencies that can be easily injected over obscure dependencies to the internal workings of asp.net in your tests.
On a different note: Use Reflector. I honestly don't know the inner dependencies of this specific part of asp.net, but you can clear any doubts with reflector.
Don't mock HttpContext, use a real one in your tests. This way you don't have to mock all these Http* stuff. You can use Ivonna and test your method directly, without mocking all these dependencies and getting mysterious exceptions.

Resources