asp.net web api2 - Autofac, OWIN, and temporary per-request registrations -


i have question creating temporary request scope when using web api owin pipeline autofac.

we have need disable external dependencies on demand our qa team can test negative test cases. did not want change code in normal application flow, did create custom middleware inspects request qa headers, , when present extends normal container temporary new scope, registers replacement object call, overrides autofac:owinlifetimescope, disposes temporary scope @ end of call.

this has allowed me override normal container behaviour request only, allow other requests continue normal.

here modified sample of middleware. code working expected.

public override async task invoke(iowincontext context) {     var headerkey = configurationmanager.appsettings["qatest.offlinevendors.headerkey"];      if (headerkey != null && context.request.headers.containskey(headerkey))     {         var offlinevendorstring = context.request.headers[headerkey].toupper(); //list of stuff blow          action<containerbuilder> qaregistration = builder =>         {             if (offlinevendorstring.contains("otherapi"))             {                 var otherclient = new mock<iotherclient>();                 otherclient.setup(x => x.getvalue()).throws<apiserviceunavailableexception>();                 builder.register(c => otherclient.object).as<iotherclient>();             }         };          using (             var scope =                 context.getautofaclifetimescope()                     .beginlifetimescope(matchingscopelifetimetags.requestlifetimescopetag, qaregistration))         {             var key = context.environment.keys.first(s => s.startswith("autofac:owinlifetimescope"));             context.set(key, scope);              await this.next.invoke(context).configureawait(false);         }     }     else     {         await this.next.invoke(context).configureawait(false);     } } 

however, lines

var key = context.environment.keys.first(s => s.startswith("autofac:owinlifetimescope")); context.set(key, scope); 

seem hacky , don't them. have searched around, have not found way cleanly override context object, or found better way implement functionality.

i'm looking suggestions better way handle this.

i can see 2 ways of achieving want to.

1. dynamic registration

the first possibility mimic autofac inject current httprequestmessage when integrated asp.net web api.

you can have @ how it's done here. create containerbuilder, registers desired type, , calls update method on lifetime scope's componentregistry.

applied scenario, like:

public override task invoke(iowincontext context) {     var headerkey = configurationmanager.appsettings["qatest.offlinevendors.headerkey"];     if (headerkey != null && context.request.headers.containskey(headerkey))     {         // not sure how use this, assume took out of logic         var offlinevendorstring = context.request.headers[headerkey].toupper(); //list of stuff blow          // autofac's lifetime scope owin context , associated component registry         // getautofaclifetimescope extension method in autofac.integration.owin namespace         var lifetimescope = context.getautofaclifetimescope();         var componentregistry = lifetimescope.componentregistry;          // create new containerbuilder , register mock         var builder = new containerbuilder();         var otherclient = new mock<iotherclient>();         otherclient.setup(x => x.getvalue()).throws<apiserviceunavailableexception>();         builder.register(c => otherclient.object).as<iotherclient>();          // update component registry containerbuilder         builder.update(componentregistry);     }      // no need await here, can return task , it'll     // awaited somewhere call stack     return this.next.invoke(context); } 

warning: though autofac uses dynamic registration after container has been built in example above, update method on containerbuilder marked obsolete following message - spanned across several lines readability:

containers should considered immutable. register of dependencies before building/resolving. if need change contents of container, technically should rebuild container. method may removed in future major release. 

2. conditional registration

there's 2 drawbacks first solution:

  • it uses obsolete method removed
  • it involves conditional registration of owin middleware it's applied in qa environment

another way register iotherclient per-request. since autofac owin integration registers owin context in lifetime scope - can see here, determine each request instance of iotherclient want register.

it like:

var headerkey = configurationmanager.appsettings["qatest.offlinevendors.headerkey"]; if (currentenvironment == env.qa && !string.isnullorempty(headerkey)) {     builder         .register(x =>         {             var context = x.resolve<icomponentcontext>();             var owincontext = context.resolve<iowincontext>();              // not sure how use this, assume took out of logic             var offlinevendorstring = context.request.headers[headerkey].toupper();  //list of stuff blow              var otherclient = new mock<iotherclient>();             otherclient.setup(x => x.getvalue()).throws<apiserviceunavailableexception>();              return otherclient.object;         })         .as<iotherclient>()         .instanceperlifetimescope(); } else {     // register "real" instance of iotherclient } 

registering fake iotherclient instanceperlifetimescope important, means logic executed each request.


3. notes

i think using moq outside of test projects not idea. suggest creating stub implementation of iotherclient throw exception when needed. way can free of dependency has nothing in production code.


Comments

Popular posts from this blog

aws api gateway - SerializationException in posting new Records via Dynamodb Proxy Service in API -

asp.net - Problems sending emails from forum -