AutoFac WCF proxy with changing ClientCredentials -
i'm writing wcf service , using autofac wcf integration di. have weird situation have proxy service requires credentials. credentials change based on parameters coming in can't set values when i'm setting container , done it.
public class myservice : imyservice { private isomeotherservice _client; public myservice(isomeotherservice client) { _client = client; } public response somecall(somedata data) { // how set clientcredentials here, without casting concrete implementation _client.makeacall(); } }
what's best way set credentials on proxy without having cast known type or channelbase. i'm trying avoid because in unit tests i'm mocking out proxy interface casting 1 of types fail.
any thoughts?
you can it, it's not straightforward, , have change design logic of "decide , set credentials" pulled out of myservice
class.
first, let's define rest of classes in scenario can see come together.
we have isomeotherservice
interface, i've modified can see credentials getting set @ end. have return string instead of being void. i've got implementation of someotherservice
has credential get/set (which clientcredentials
in wcf). looks this:
public interface isomeotherservice { string makeacall(); } public class someotherservice : isomeotherservice { // "credentials" here stand-in wcf "clientcredentials." public string credentials { get; set; } // returns credentials used can validate things // wired up. don't have in "real life." public string makeacall() { return this.credentials; } }
notice credentials
property not exposed interface can see how work without casting interface concrete type.
next have imyservice
interface , associated request/response objects somecall
operation show in question. (in question have somedata
it's same idea, went different naming convention me keep straight input vs. output.)
public class somecallrequest { // number value we'll use determine // set of client credentials. public int number { get; set; } } public class somecallresponse { // response include credentials used, passed // call isomeotherservice. public string credentialsused { get; set; } } public interface imyservice { somecallresponse somecall(somecallrequest request); }
the interesting part there data we're using choose set of credentials number
in request. whatever want be, using number here makes code little simpler.
here's starts getting more complex. first need familiar 2 autofac things:
- implicit relationships - can take reference on
func<t>
instead oft
"factory createst
instances." - using parameters registration delegates - can take inputs , use inform outputs of resolve operation.
we'll make use of both of concepts here.
the implementation of myservice
gets switched take factory take in int
, return instance of isomeotherservice
. when want reference other service, execute function , pass in number determine client credentials.
public class myservice : imyservice { private func<int, isomeotherservice> _clientfactory; public myservice(func<int, isomeotherservice> clientfactory) { this._clientfactory = clientfactory; } public somecallresponse somecall(somecallrequest request) { var client = this._clientfactory(request.number); var response = client.makeacall(); return new somecallresponse { credentialsused = response }; } }
the real key there func<int, isomeotherservice>
dependency. we'll register isomeotherservice
, autofac automatically create factory takes in int
, returns isomeotherservice
us. no real special work required... though registration little complex.
the last piece register lambda isomeotherservice
instead of simpler type/interface mapping. lambda typed int
parameter , we'll use determine/set client credentials.
var builder = new containerbuilder(); builder.register((c, p) => { // in wcf, more going call // channelfactory<t>.createchannel(), ease // here we'll new up: var service = new someotherservice(); // magic: incoming int parameter - // func<int, isomeotherservice> pass // in when called. var data = p.typedas<int>(); // our simple "credentials" tell whether // passed in or odd number. yours // way more complex, looking config, // resolving sort of "credential factory" // current context (the "c" parameter in lambda), // or else want. if(data % 2 == 0) { service.credentials = "even"; } else { service.credentials = "odd"; } return service; }) .as<isomeotherservice>(); // , registration of consuming service here. builder.registertype<myservice>().as<imyservice>(); var container = builder.build();
ok, have registration taking in integer , returning service instance, can use it:
using(var scope = container.beginlifetimescope()) { var myservice = scope.resolve<imyservice>(); var request = new somecallrequest { number = 2 }; var response = myservice.somecall(request); // write "credentials = even" @ console // because passed in number , registration // lambda executed set credentials. console.writeline("credentials = {0}", response.credentialsused); }
boom! credentials got set without having cast base class.
design changes:
- the credential "set" operation got moved out of consuming code. if don't want cast base class in consuming code, won't have choice pull credential "set" operation out. logic right in lambda; or put in separate class gets used inside lambda; or handle onactivated event , little magic there (i didn't show - exercise left reader). "tie together" bit has somewhere in component registration (the lambda, event handler, etc.) because that's point @ still have concrete type.
- the credentials set lifetime of proxy. it's not if have single proxy in consuming code set different credentials before execute each operation. can't tell question if that's how have it, but... if that's case, need different proxy each call. may mean want dispose of proxy after you're done it, so you'll need @ using
owned<t>
(which make factoryfunc<int, owned<t>>
) or run memory leak if services long-lived singletons.
there other ways this, too. create own custom factory; handle onactivated
event mentioned; use autofac.extras.dynamicproxy2
library create dynamic proxy intercepts calls wcf service , sets credentials before allowing call proceed... brainstorm other ways, idea. what posted here how i'd it, , @ least point in direction need go.
Comments
Post a Comment