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
Post a Comment