1

I'm building a Web API that interfaces SharePoint (an external web service). To get at connection to the external service I need to create instances of ClientContext with some kind of token.

For this I have created an interface called IClientContextFactory:

interface IClientContextFactory
{
    Create ClientContext(string siteUrl)
}

I have created an implementation HttpClientContextFactory that creates objects using the claims from the current HttpRequest that is injected into the factory. Based on the claims I know what tenant the ClientContext applies to.

This works well from the web api but what about my supporting console applications that doesn't have a user context? I have to send the tenant id with every message to these which in turn means that the IClientContextFactory interface doesn't work. For this I created IAppClientContextFactory:

interface IAppClientContextFactory
{
   Create(Guid tenantId, string siteUrl);
}

This feel awkward because now any implementation that depends on IClientContextFactory is useless in the console applications since I cant implement it without a user context.

I could add Create(Guid tenantId, string siteUrl) to IClientContextFactory but this would violate Liskov's since Create(string siteUrl) would throw a NotImplementedException in the console applications.

Is there a better way to do this? Am I taking the wrong approach to this? Am I even doing it correctly and shouldn't worry at all? It is difficult for new developers in particular to have to figure out what dependencies must be registered in the IoC container for their particularly application.

4
  • What is the purpose of the interfaces? Do you have multiple implementations of IClientContextFactory and IAppClientContextFactory? Could IClientContextFactory simply include another method overload to cater for the console applications? Commented Sep 2, 2016 at 15:13
  • Can't the interface have both Create(string siteUrl); and Create(Guid tenantId, string siteUrl); ? Commented Sep 2, 2016 at 15:19
  • @TulainsCórdova Now that you've said it might be possible using a factory (or AutoFac's Owned<T> in my case) and let the Console App implementation take the tenantId as a constructor parameter. I guess that implementation would return the same non-user context from both methods then. Is that so bad? Commented Sep 2, 2016 at 16:08
  • 1
    "console applications that doesn't have a user context" - Just get them one. Anything else sounds terrible.
    – maaartinus
    Commented Aug 6, 2017 at 17:58

2 Answers 2

2

As I see, your problem is, that you try introduce web service's implementation details (Context) as abstraction.

"Leak abstraction" the name for this problem.

Consumers of web service doesn't need to know about how you send message. Consumers need only provide required data(arguments) and get result as return value.

public interface IWebService
{
    ResultType GetData(string credentials);
}

Then for web application and for console application you can implement them in different way.

Or even use same implementation, but way how you get required arguments will be different in web and console application

0

The best approach would be to have a pluggable identity provider which will vary based on the type of application that you are going to consume.

For example, Create an interface IUserContext which will have properties like user ID, tenant id etc. The web application with use something like WebUserContextProvider:IUserContext. Similarly, the services or jobs will have like JobUserContextProvider: IUserContext These implementation will be set via a DI like unity or autofac

HTH

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.