Autofac Wcf - Inject service depending on data within SOAP Request - c#

I have a WCF Service with the following operation contract:
[OperationContract]
Response SearchEntities(Query query);
This operation takes a request that contains a specified Entity like so:
[DataContract]
public class Query
{
[DataMember]
public string SearchTerm { get; set; }
[DataMember]
public string Entity { get; set; }
[DataMember]
public bool ExactMatch { get; set; }
}
Based on the value contained within the Entity property, one the following properties is populated within this response:
[DataContract]
public class Response
{
[DataMember]
public List<Asset> Assets { get; set; }
[DataMember]
public List<Stage> Stages { get; set; }
[DataMember]
public List<Sector> Sectors { get; set; }
}
Terrible design, I know! However. I am using Autofac.Wcf as my service factory to inject dependencies. Normally I would use a common Interface and Generics to determine a service to use based on the Entity value like so:
public interface IEntitySearch<T>
{
Response Search(Query query);
}
The above interface would have several implementations for each of the Lists within the response. Using a design pattern such as a service location I could determine which service to use (all of which inherit from IEntitySearch<T>, something like:
public IEntitySearch ResolveSearcher(Query query)
{
switch(query.Entity)
{
case "Assets":
return _container.Resolve<AssetSearch>();
case "Stages":
return _container.Resolve<StageSearch>();
default:
throw new NotSupportedException();
}
}
While this works, a more elegant solution (I believe) would be to customize the Autofac container per request for this particular operation, depending on the data contained within the request.
IE: Before the WCF pipe line sends the request to the service implementation, is it possible to examine the request data and customize how the container resolves dependencies. That way I can avoid exposing dependency resolution within my service layer.
Is this possible?
If another DI library other than Autofac has a solution for this, I will happily change our DI framework.
Thanks.

I haven't personally tried this but I think a direction you can go down is to combine:
Using OperationContext.Current to get the current request message data.
Specifying a custom IServiceImplementationDataProvider for Autofac that tells Autofac which WCF interface to host for that request.
Using a lambda registration for your service implementation to switch the backing service based on OperationContext.Current.
You can see two examples of the IServiceImplementationDataProvider by looking at the DefaultServiceImplementationProvider - the one that works in Autofac WCF hosting by default; andMultitenantServiceImplementationDataProvider, which is more about generating a proxy to enable multitenant WCF hosting.
While neither of these use OperationContext.Current to determine the actual backing service, you can build on the ideas:
Look at the Autofac.Multitenant.Wcf implementation. You may be able to use it as-is. The point of the instance data provider there is that WCF grabs on to the concrete type of the service being hosted and if you try to swap types out from under it, you get errors. The multitenant support fools WCF by creating a proxy type and your implementation type can be swapped out under the proxy. Note the MultitenantServiceImplementationDataProvider doesn't actually tie anything to a tenant or tenant ID; it's only about that proxy.
In your .svc file specify a service interface rather than any individual concrete implementation since you'll be swapping out the implementation.
Use a lambda registration to figure out your implementation.
Make sure your service is InstanceContextMode.PerCall to ensure things get swapped out on a per request basis.
The registration might look something like this:
builder.Register(ctx => {
var context = OperationContext.Current;
var type = DetermineTypeFromContext(context);
return ctx.Resolve(type);
}).As<IMyServiceInterface>();
The Autofac WCF and Autofac Multitenant section on WCF may also help.

In my opinion you're trying to move your problem just to another place. Why would making decision based on request at low-level WCF is better than switch in SearchEntities method? It's much worse ;-)
I would consider to use IEntitySearch factory/provider e.q.IEntitySearchProvider (it's not so much better but always).
public interface IEntitySearch
{
bool IsMatchQuery(Query query);
Response Search(Query query);
}
// without service locator
public class EntitySearchProvider : IEntitySearchProvider
{
private readonly IEnumerable<IEntitySearch> _searchers;
public EntitySearchProvider(IEnumerable<IEntitySearch> searchers)
{
_searchers = searchers;
}
public IEntitySearch GetSearcher(Query query)
{
// last registered
return _searchers.LastOrDefault(i=>i.IsMatchQuery(query))
?? throw new NotSupportedException();
}
}
or
public interface IEntitySearchProvider
{
IEntitySearch GetSearcher(Query query);
}
public class EntitySearchProvider : IEntitySearchProvider
{
private readonly IComponentContext _container;
public EntitySearchProvider(IComponentContext container)
{
_container = container;
}
public IEntitySearch GetSearcher(Query query)
{
switch(query.Entity)
{
case "Assets":
return _container.Resolve<AssetSearch>();
case "Stages":
return _container.Resolve<StageSearch>();
default:
throw new NotSupportedException();
}
}
}
with
public class WcfService
{
private readonly IEntitySearchProvider _provider;
public WcfService(IEntitySearchProvider provider)
{
_provider = provider;
}
public Response SearchEntities(Query query)
{
var searcher = _provider.GetSearcher(query);
return searcher.Search(query);
}
}

Related

How to inject dependencies from IHostedService before creating scope

I have a multi tenant system with background job. The tenancy details are stored in database and based on the tenant adding request in service bus, I want to resolve the dependencies based on tenant.
For this I would have to add dependencies to service collection before creating scope. When trying to inject IServiceCollection, it gives me error.
I am looking for the best way to inject dependencies from HostedService
public async Task MessageHandler(object sender, Message message)
{
// Inject dependencies
services.AddScoped<IMyService,Myservice>(); // No way to get services here
using (var scope = serviceProvider.CreateScope())
{
var ... = scope.ServiceProvider.GetService<...>();
//...
}
}
I had a similar need a while back. I created my own service bus handler.
You could try something like the below, where you inject a service (here as an example I'm using IMessageService) to the ServiceeBusHandler that itself has a dbcontext injected.
Then where ever you implement IServiceBusHandler you can specify for which tenant (and their queues) you want the connection built.
public class ServiceBusHandler : IServiceBusHandler
{
private readonly ServiceBusSender _serviceBusSender;
private readonly IMessageService _messageService;
public ServiceBusHandler(
ServiceBusSender serviceBusSender,
IMessageService messageService)
{
_serviceBusSender = serviceBusSender;
_messageService = messageService;
}
public async Task PublishMessageAsync<T>(T message)
{
var jsonString = JsonConvert.SerializeObject(message);
var serviceBusMessage = new ServiceBusMessage(jsonString);
await _serviceBusSender.SendMessageAsync(serviceBusMessage);
}
internal static IServiceBusHandler Create(ServiceBusSender sender)
{
return new ServiceBusHandler(sender);
}
}
public class ServiceBusHandlerFactory : IServiceBusHandlerFactory
{
private readonly IAzureClientFactory<ServiceBusClient> _serviceBusClientFactory;
public ServiceBusHandlerFactory(
IAzureClientFactory<ServiceBusClient> serviceBusClientFactory)
{
_serviceBusClientFactory = serviceBusClientFactory;
}
public IServiceBusHandler GetClient(string tenantId)
{
var tenantDetails = _messageService.GetTenantDetails(tenantId); // Call to your DB to get details about the Tenant
var client = GetServiceBusClient(tenantDetails.QueueName);
var sender = client.CreateSender(tenantDetails.QueueName);
return ServiceBusHandler.Create(sender);
}
protected virtual ServiceBusClient GetServiceBusClient(string queueName)
{
var client = _serviceBusClientFactory.CreateClient(queueName);
return client;
}
}
What you are trying to achieve is to change the set of registrations after the Container was built. MS.DI does not support this, and while historically, more mature DI Containers tended to support this behavior, most modern DI Containers stopped supporting this, because there are too many negative consequences in allowing this. Autofac, for instance, obsoleted its Update method in 2016 and described the issues with updating the Container in details. Ninject has gone through a similar process, although development stopped before the final release that removed the possibility to update the Container. The Simple Injector DI Container never supported updating, and its documentation has some clear texts that describe what the issue is.
You might find a DI Container that supports this, but I would urge you to abbondon this path, because of the negative consequences that it can (and probably will) cause, as the previous links described.
Instead, you will have to find a different way to get tenant-specific behavior, with one single set of registrations. The trick here, typically lies in creating a Proxy implementation of your IMyService that can forward the call to the correct tenant implementation.
This might look something like this:
public class ProxyMyService : IMyService
{
public IMyService Service { get; set; }
// IMyService methods
public void SomeMethod() => this.Service.SomeMethod();
}
This proxy class can be registered at startup, together with other IMyService implementations, as follows:
services.AddScoped<IMyService, ProxyMyService>();
services.AddTransient<MyServiceTenant1>();
services.AddTransient<DefaultMyServiceTenant>();
With this, your hosted service can become the following:
private ProxyMyService service;
public MyHostedService(IMyService service)
{
this.service = (ProxyMyService)service;
}
public async Task MessageHandler(object sender, Message message)
{
using (var scope = serviceProvider.CreateScope())
{
var p = scope.ServiceProvider;
var proxy = (ProxyMyService)p.GetRequiredService<IMyService>();
proxy.Service = IsTentant1
? p.GetRequiredService<MyServiceTenant1>()
: p.GetRequiredService<DefaultMyServiceTenant>();
var ... = p.GetRequiredService<...>();
//...
}
}
A more evolved solution would entail a Proxy implementation that allows to switch between tenant-specific implementations internally. That would likely mean moving part of the logic that's currently inside MessageHandler into the ProxyMyService.
Do notice that the solutions I suggested do not require an abstract factory. Abstract factories are typically not needed.

How To Get Configurable Cache Duration on Service Methods With ServiceStack?

I was using CacheResponseAttribute on one of the Get methods in the service like [CacheResponse(Duration = 60)]. But I want this cache duration to come from a config file so I can set it to be different depending on the environment the service is currently running on (dev, prod, etc)
I know we can't use something that's not constant as a parameter in the attribute constructor. So I was planning to do something like
public class MyCacheResponseAttribute : CacheResponseAttribute
{
public IConfiguration Configuration { get; set; }
public CacheWidgetResponseAttribute()
{
int.TryParse(Configuration["cache_duration_in_secs"], out var cacheDuration);
Duration = cacheDuration;
}
}
and use this as the decorator on the Get method. However, the dependency injection doesn't seem to work for the attributes since I'm getting the Configuration as null.
My return type is string, I've tried ToOptimizedResultUsingCache but I couldn't get it to return string properly.
What options do I have? Is it possible to make the IoC work on Attributes somehow? I guess as a last resort I could have a ICacheClient in the service and use it but that would be my last resort since it's gonna be more custom made.
Request Filter Attributes does have their properties autowired from the IOC but that can only happen after an objects constructor is executed, not before.
So you could read from your injected IOC properties before the attribute is executed, e.g:
public class MyCacheResponseAttribute : CacheResponseAttribute
{
public IConfiguration Configuration { get; set; }
public override Task ExecuteAsync(IRequest req, IResponse res, object requestDto)
{
if (Duration == default
&& int.TryParse(Configuration["cache_duration_in_secs"], out var duration))
Duration = duration;
return base.ExecuteAsync(req, res, requestDto);
}
}
Or resolve the IOC dependencies via the singleton, e.g:
public class MyCacheResponseAttribute : CacheResponseAttribute
{
public MyCacheResponseAttribute()
{
var config = HostContext.Resolve<IConfiguration>();
if (int.TryParse(config["cache_duration_in_secs"], out var duration))
Duration = duration;
}
}

accessing multi endpoints web services (ASMX) best practices in c#

I have a clean architecture project that provide micro services, one of which is to access Agresso ERP web services.
https://***************/service.svc
it provide many services
https://**/service.svc?FooService/Foo
https://**/service.svc?BooService/Boo
each of which has it's own service reference(connected service), and each of which has many methods.
each call to any of the end point you need to pass credentials with it.
var fooSoapClient = new FooSoapClient();
var credentials = new WSCredentials
{
Username = "fakeuser",
Password = "fakepassword",
Client = "fakeclient",
};
var result = fooSoapClient.GetFoosAsync(Foo filter,true,
credentials );
(P.S) credential class exist in all entities
namespace Foo1NS
{
public partial class WSCredentials : object
{
public string Username {get;set;}
public string Client {get;set;}
public string Password {get;set;}
}
}
namespace Foo2NS
{
public partial class WSCredentials : object
{
public string Username {get;set;}
public string Client {get;set;}
public string Password {get;set;}
}
}
i can access all end points with no problem.
I have the following Questions:
Is there a generic solution i can follow for not to Fall in DRY?
is there a design pattern that best target this issue?
Here is what I've done in the past, it fits in well into Dependency Injection/containers if you use that as well. The key thing here is to define an single interface that all services will implement. Your code that uses this should only be using the interface.
Each class should implement an interface you define, e.g. IWebServiceOperations
public interface IWebServiceOperations
{
WebServiceOperationResult GetFooAsync(WebServiceOperationRequest request);
}
I'll leave you to figure out the classes WebServiceOperationResult/Request, they just hold your request/response variables, including credentials.
Then each webservice you need to implement is done in a separate class. You also dictate in the constructor what type of implementation this is (FooSoap1 vs FooSoap2) e.g.
public class FooSoapClient : BaseClient, IWebServiceOperations
{
public FooSoapClient() : base(Clients.FooSoap1)
public GetFooAsync(...)
{
...
}
}
public class BaseClient
{
private readonly eFooServiceType _serviceType;
public eFooServiceType ServiceType {
get{
return _serviceType;
}
}
protected BaseClient(eFooServiceType service)
{
_serviceType = service;
}
}
Now you should have a bunch of class references. Either your DI container can resolve these for you, based on the service type you want, or you could add them to a Dictionary, so if you wanted to operate against FooSoap1, you'd do...
var fooSoapClient1 = myServices[Clients.FooSoap1];
await fooSoapClient1.GetFooAsync(...)

How to inject a property into a class that I don't control the creation of

How do I inject the IServiceManager property into this class using autofac? This is a custom resource provider factory class that gets called when you make a call to HttpContext.GetGlobalResourceObject("", "MyResource") to get a resource string.
public class SqlResourceProviderFactory : ResourceProviderFactory
{
// needs autofac property injection
public IServiceManager ServiceManager { get; set; }
public override IResourceProvider CreateGlobalResourceProvider(string classKey)
{
...
}
public override IResourceProvider CreateLocalResourceProvider(string virtualPath)
{
...
}
public static string GetAppRelativePath(string logicalPath)
{
...
}
}
I've faced this exact same problem - with an ASP.NET ResourceProviderFactory - and the answer is, unfortunately, that you have to use service location.
There's no "hook" into the pipeline anywhere that you can inject anything or change the built-in ASP.NET behavior. Thus, if you need something put into a property, it has to be set in the constructor.
public class SqlResourceProviderFactory : ResourceProviderFactory
{
public IServiceManager ServiceManager { get; set; }
public SqlResourceProviderFactory()
{
this.ServiceManager =
DependencyResolver.Current.GetService<IServiceManager>();
}
}
Yeah, it's really ugly.
Something very important to consider here, especially with respect to Autofac, is the lifetime scope for which your IServiceManager is registered.
The ResourceProviderFactory is created once and cached. Same with the global/local resource providers that come out of the Create* methods. we had a heck of a time with this because it means there's not necessarily an HttpContext at the time the factories get created, and even if there is, if any of the downstream dependencies are registered InstancePerHttpRequest then they'll be disposed of and you're hosed.
Anything used by your ResourceProviderFactory or generated resource providers - all the way down the stack - should be registered either SingleInstance or InstancePerDependency.
If possible, instead of using DependencyResolver.Current (which, for Autofac, requires an active HttpContext), reference the application container directly. That means you need to store a reference to it somewhere else (a global static variable?) and use that.
This is what a more complete solution might involve:
// Create some "holder" for the app container.
public static class ApplicationContainerProvider
{
public static ILifetimeScope Container { get; set; }
}
// In Global.asax, build your container and set it in both
// the DependencyResolver AND in the holder class.
var builder = new ContainerBuilder();
builder.RegisterType<Something>().As<ISomething>();
var container = builder.Build();
var resolver = new AutofacDependencyResolver(container);
DependencyResolver.SetResolver(resolver);
ApplicationContainerProvider.Container = container;
// In your service location, reference the container instead of
// DependencyResolver.
public class SqlResourceProviderFactory : ResourceProviderFactory
{
public IServiceManager ServiceManager { get; set; }
public SqlResourceProviderFactory()
{
this.ServiceManager =
ApplicationContainerProvider.Container.Resolve<IServiceManager>();
}
}
Note that since you're resolving that out of the root container, it'll stick around for the lifetime of the application. Even if you register it as InstancePerDependency, because of the internal caching .NET does, it'll only get created once.
If you don't like creating your own static holder class like that, you can abstract it away by using the CommonServiceLocator and the Autofac.Extras.CommonServiceLocator packages.

IoC / Dependency Injection - How to handle contextual dependencies (using Structuremap)

After introducing messaging in my application it seems I've found a bit of a smell.
In my multi tenant application, the file system is abstracted and scoped for each tenant. So if a service needs to create files, then we inject an instance of IFileSystem which will be scoped to the tenants directory/container.
This is achieved by configuring structuremap to construct the IFileSystem implementation by getting of a contextual object that has the current users site.
Now we need to use the filesystem when there is no context and no current user (on a background thread). Here's a simple example:
public class SiteContext
{
public string SiteId { get { return "Site123"; } }
}
public class FileSystemSettings
{
public string BaseDirectory { get; set; }
}
public interface IFileSystem { }
public class DefaultFileSystem : IFileSystem
{
public DefaultFileSystem(FileSystemSettings settings)
{
}
}
public interface ISomeService { }
public class SomeService : ISomeService
{
public SomeService(IFileSystem fileSystem)
{
}
}
public class TestMessageHandler : IMessageHandler<TestMessage>
{
public TestMessageHandler(ISomeService someService)
{
// oO we don't have access to site context here :(
}
}
I suppose I could change my FileSystem implementation to expose the FileSystemSettings as a property so it can be set afterwards.
However, even doing this would still require me to construct my ISomeService object manually, which is a pain as some of my services have a number of dependencies = lots of calls to ObjectFactory.GetInstance...
Ideas?
You could use nested containers and configure the nested container to have a dummy implementation of your context.
The code would approximately be:
using (var container = ObjectFactory.Container.GetNestedContainer())
{
container.Configure(config => {
config.For<ISiteContext>().Use<DummyContext>();
});
return container.GetInstance<TestMessageHandler>();
}
This should set a custom (dummy) implementation of ISiteContext without overwriting the global container (ObjectFactory.Container). Of course, I can't give you an appropriate implementation of DummyContext without more information. But this should get you started.

Categories

Resources