servicestack serverevents triggered by eventhandler/action - c#

Context:
I am using the ServiceStack Framework (4.0.42) for my application. It is selfhosted and runs as a windows service. The main purpose is to give some devices the ability to communicate via web services (RESTful and ServerEvent).
For this I created a common interface for all types of devices which look like this (simplified):
public interface IDevice
{
string GetName();
bool IsConnected(string id);
event EventHandler<EventArgs> RaiseSomeEvent;
}
This interface is implemented in DLL’s one for each type of device.
My problem is that I can’t figure out how to forward the RaiseSomeEvent to notify a subscriber to ServerEvents.
I tried many different implementations, none of them worked. Most, it ended up, that at runtime, when the DeviceAdapter_RaiseSomeEvent is called, the ServerEvents instance is null.
I am running out of ideas now.
Here is the actual (simplified) Version:
public class ServiceInterface : Service
{
public IDevice DeviceAdapter { get; set; }
public IServerEvents ServerEvents { get; set; }
public IAppSettings AppSettings { get; set; }
public ServiceInterface(IDevice deviceAdapter)
{
DeviceAdapter = deviceAdapter;
DeviceAdapter.RaiseSomeEvent += DeviceAdapter_RaiseSomeEvent;
}
public void DeviceAdapter_RaiseSomeEvent(object sender, EventArgs e)
{
ServerEvents.NotifyAll("Something happend!");
}
and here the AppHost Class:
public class AppHost : AppSelfHostBase
{
public IDevice DeviceAdapter;
public AppHost()
: base("grob.MachineConnector.Service", typeof(ServiceInterface).Assembly)
{ }
public override void Configure(Funq.Container container)
{
this.Plugins.Add(new ServerEventsFeature());
switch (UsedAdapter)
{
case enAdapterTyp.DeviceTyp1:
DeviceAdapter = new DeviceTyp1();
break;
case enAdapterTyp.DeviceTyp2:
DeviceAdapter = new DeviceTyp2();
break;
default:
throw new AdapterTypException("Wrong or no Adaptertyp is configured:" + UsedAdapter.ToString());
}
container.Register(new ServiceInterface(DeviceAdapter));
}
Maybe it lies somewhere in the Funq. I am not sure what happening exactly with the DI, IoC, Autowiring stuff. I tried to write my own Plugin for the framework. I have no idea how to get the a valid instance of IServerEvents when a Event rises from my Device.
Maybe I did some general design faults. For OOP and C# I am at beginner level.
Any hints are very welcome.

The Service Dependencies are only available within the lifetime of a request, beyond that the Service and its dependencies are released/disposed. It's not clear from the event that it's only raised during the Request:
DeviceAdapter.RaiseSomeEvent += DeviceAdapter_RaiseSomeEvent;
You should also never register a Service as they're automatically registered and autowired by ServiceStack:
//Don't register Services
//container.Register(new ServiceInterface(DeviceAdapter));
Otherwise IServerEvents is just a normal Singleton dependency that's registered when the ServerEventsFeature Plugin is loaded.
Normally you'd just access it like any other dependency, where you'd just resolve it in the dependency that needs it, i.e:
container.Register<IDevice>(c => new DeviceTyp1 {
ServerEvents = c.Resolve<IServerEvents>()
});
Which will automatically inject the resolved IDevice into the Service dependencies:
public class ServiceInterface : Service
{
public IDevice DeviceAdapter { get; set; }
public object Any(Request request)
{
//DeviceAdapter also has access to IServerEvents
DeviceAdapter.Exec(request);
}
}
But if this event is not in reaction to a Service request then the event shouldn't be tied to a Service, i.e. you could just have the handler in the AppHost:
public override void Configure(Funq.Container container)
{
DeviceAdapter.RaiseSomeEvent += DeviceAdapter_RaiseSomeEvent;
}
public void DeviceAdapter_RaiseSomeEvent(object sender, EventArgs e)
{
var serverEvents = Container.Resolve<IServerEvents>();
serverEvents.NotifyAll("cmd.Handler", "Something happend!");
}
Please also see the documentation on Selectors so you know which selector the message should be sent with.
See this answer on different ways you can resolve IOC dependencies from outside of ServiceStack may also be helpful.

Related

How to handle event invocation between injected classes in .NET Core Console App

I've a .Net Core(3.1) Console App, that has 2 service classes, one has an event and other listens to it with a handler to that event. I've setup getting the DI containers but the event field is always null, so not able to call its Invoke(). Any pointers on what am I missing in setting up the services in ConfigureServices() that involves event handling. Below is the complete test code:
public class RefreshEventArgs : EventArgs
{
public string RefreshEventData { get; set; }
}
public interface INotifierService
{
event EventHandler<RefreshEventArgs> RefreshEventHandler;
}
public class NotifierService : INotifierService
{
public event EventHandler<RefreshEventArgs> RefreshEventHandler;
public RefreshEventArgs RefreshEventData { get; set; }
// GeneralAppSettings is a POCO class to read all appsettings.json key values.
private readonly IOptions<GeneralAppSettings> myAppSettings;
public NotifierService(IOptions<GeneralAppSettings> appSettings)
{
myAppSettings = appSettings;
}
public void RunInvokingRefreshEvent()
{
RefreshEventData = new RefreshEventArgs();
RefreshEventData.RefreshEventData = "somedata";
// Main problem! In the below line, RefreshEventHandler is null all the time
RefreshEventHandler?.Invoke(this, RefreshEventData);
}
public void SomeBackgroundThreadMonitorsExternalEvents()
{
// Some external events triggers below method
RunInvokingRefreshEvent();
}
}
Refresh Service
public interface IRefreshService
{
void Refresh(RefreshEventArgs eventData = null);
}
public class RefresherService : IRefreshService
{
private readonly IOptions<GeneralAppSettings> myAppSettings;
private readonly INotifierService notify;
public RefresherService(IOptions<GeneralAppSettings> _appSettings, INotifierService _notifyService)
{
myAppSettings = _appSettings;
notify = _notifyService;
notify.RefreshEventHandler += _notify_RefreshEventHandler;
}
private void _notify_RefreshEventHandler(object sender, RefreshEventArgs e)
{
// Call Refresh() based say based on a config value from myAppSettings
Refresh(e);
}
public void Refresh(RefreshEventArgs eventData = null)
{
// final business logic processing based on eventData
}
}
public class GeneralAppSettings // POCO
{
public string SomeConfigKeyInAppSettingsJson { get; set; }
}
Program
class Program
{
public static IConfiguration Configuration { get; set; }
static void Main(string[] args)
{
// read appsettings
var builder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
// Host builder, setting up container
var host = Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddConfiguration(Configuration);
})
.ConfigureServices((context, services) =>
{
services.Configure<GeneralAppSettings>(Configuration.GetSection("GeneralAppSettings"));
services.AddSingleton<INotifierService, NotifierService>();
services.AddSingleton<IRefreshService, RefresherService>();
})
.Build();
// Need to get NotifierService instance to run some initial logic, so using ActivatorUtilities
var svc = ActivatorUtilities.GetServiceOrCreateInstance<NotifierService>(host.Services);
svc.SomeBackgroundThreadMonitorsExternalEvents();
// Need to get RefresherService instance to have initial Refresh logic so using ActivatorUtilities
var refresh = ActivatorUtilities.GetServiceOrCreateInstance<RefresherService>(host.Services);
refresh.Refresh(null);
// need to keep this main thread alive
Thread.Sleep(Timeout.Infinite);
}
}
When you request something from the DI container, you must request the "Service" type (the interface or first/only generic argument). If you request a type that's not been registered and you use ActivatorUtilities, it will create an instance if and only if all the types required to construct it are available. What's happening to you is you are getting two distinct objects (one registered as the interface and one pseudo-registered as the concrete type)! It doesn't matter that your class implements the interface and you've used it as the "Implementation" type in the registration. DI is always based on the service type and you've not registered any services of type NotifierService directly.
Your problem is that you have a weird coupling between your classes and the method you want to call on NotifierService isn't actually part of the interface. The usual trick would be to just register and request the concrete type as the service type:
services.AddSingleton<NotiferService>();
//...
var notifier = services.GetService<NotifierService>();
That would work, except now you haven't registered INotifierService for injection into the RefresherService.
Never fear, we have a work around. Register the concrete type as a singleton and then use a factory to register the interface:
// register the concrete type directly
services.AddSingleton<NotifierService>();
// use a factory to register the interface
services.AddSingleton<INotifierService>(sp => sp.GetRequiredService<NotifierService>());
Now, the same instance will be returned whether you are requesting the interface or the concrete type. You no longer need to use ActivatorUtilities either (in fact you shouldn't)--you can now use the host's services directly:
var notifier = host.Services.GetRequiredService<NotifierService>();
notifier.SomeBackgroundThreadMonitorsExternalEvents();
All that said, you're project is a perfect candidate for an IHostedService/BackgroundService. You can restructure it a bit (splitting NotifierService into two classes: one with just the event and the other for the background service) such that you'll then only be dealing with interfaces and you'd be able to actually call Host.Run() which will in turn wait for shutdown. This is the standard pattern for things like this, rather than abusing the Host simply for the DI container and including the weird Thread.Sleep.

Autofac Wcf - Inject service depending on data within SOAP Request

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);
}
}

Use factory to resolve dependencies

We have a websolution with autofac. Now we want to reuse things in a windows service/console app where things are only available when a message comes in from an enterprise bus.
I have the following service to reuse
public SettingsService : ISettingsService {
public readonly ITenantIdentifer _tenantIdentifier
public SettingsService (ITenantIdentifier tenantIdentifier) {
this._tenantIdentifier =tenantIdentifier;
}
// do other stuff
}
The current working setup
the ITenantIdentifier for the webcontext is simply registered for the webapplication using builder.RegisterType<WebTenantIdentifier>().As<ITenantIdentifier>();.
Evething works fine.
Our enterprise bus
The enterprise bus can not resolve the ITenantIdentifier until the message is available. So we created a MessageTenantIdentifier and registered a factory.
public class MessageTenantIdentifier : ITenantIdentifier
{
public delegate MessageTenantIdentifier Factory(int tenantId);
public MessageTenantIdentifier(int tenantId, IOtherDependency stuff)
{
_tenantId = tenantId;
// ...
}
}
// somewhere else the this is registered
builder.RegisterType<MessageTenantIdentifier >().As<ITenantIdentifier>().AsSelf();
builder.RegisterGeneratedFactory<MessageTenantIdentifier.Factory>();
The problem
The factory can only be used when the message is being handled in a
public class MsgTypeHandler : IHandleMessages<MsgType>
{
public MsgTypeHandler(ISettingsService settingsService, MessageTenantIdentifier factory) { ...}
public async Task Handle(MsgType message)
{
var tenantId = message.TenantId;
// THIS IS THE MOMENT I CAN CONFIGURE THE MessageTenantIdentifier
var tenantIdentifier = factory.Invoke(tenantId);
// but this factory is not used against the ISettingsService. The service to be reused. <== THE REAL PROBLEM
}
}
The question
So, how can I solve this issue? E.g. how should I setup the registration of the MessageTenantIdentifier in the servicebus?
Or is my dependency setup just plain wrong?
If the MsgTypeHandler class needs an ISettingsService, but the entire object graph can't be resolved until the tenant ID is available, that means that the MsgTypeHandler is the Composition Root. That's OK, but that means that this is where you resolve your entire object graph, so don't inject individual services here; instead, inject the factory you need:
public class MsgTypeHandler : IHandleMessages<MsgType>
{
public MsgTypeHandler(ISettingsServiceFactory factory) {...}
public async Task Handle(MsgType message)
{
var tenantId = message.TenantId;
ISettingsService svc = this.factory.Create(tenantId);
// User svc here...
}
}

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.

StructureMap doesn't appear to be ready during HttpModule constructor -- is this correct?

This question is more to confirm my diagnosis of an issue we encountered--or to find alternative explanations.
We have an HTTPModule which intercepts every request made to our webforms application. It's job is to translate specific querystring parameters which our integration partners send.
More importantly, it was wired to StructureMap like this:
public class SomeModule : IHttpModule
{
public SomeModule()
{
ObjectFactory.BuildUp(this);
}
public IDependency Dependency { get; set; }
}
During a previous release it appeared that the module wasn't being injected by the time it executed it's request-processing. That led to some (ugly) defensive check being added like:
public class SomeModule : IHttpModule
{
public SomeModule()
{
ObjectFactory.BuildUp(this);
if (SomeDependency == null)
{
// HACK: Not sure why this corrects the issue!
Dependency = ObjectFactory.GetInstance<ISomeDependency>();
}
}
public IDependency Dependency { get; set; }
}
You'll notice the HACK comment -- it resolved the issue but without good reason.
Well, this same module has been re-purposed on another site--and the previous hack no longer worked. After looking at it for some time I made the change to move the StructureMap call outside the constructor, and lo-and-behold, it works.
public class SomeModule : IHttpModule
{
public IDependency Dependency { get; set; }
public void IHttpModule.Init(HttpApplication context)
{
Initialize();
// the rest of the code
}
private bool _initialized;
private void Initialize()
{
if (_initialized)
{
return;
}
ObjectFactory.BuildUp(this);
_initialized = true;
}
}
So, my I have a few questions around this behavior:
My suspicion is that StructureMap was not fully initialized/configured when the HttpModule constructor was being called -- agree/disagree, any insight?
I haven't found any reference materials that state when to expect StructureMap to be initialized and ready to service requests. Is there any such documentation?
I wouldn't expect the behavior to be very predictable when you're trying to build up a type in its constructor. Many actions that are normally considered safe are not considered safe in a constructor (like using a virtual property in a class).
Where you moved the code to looks much better and I would leave it there. If you can't have the container create the instance itself (and therefore are forced to build it up) some sort of Initialize method is the preferred place to do the build up action.
To answer the question you have at the end of your post:
It is up to the developer to determine when StructureMap is initialized. In a web application, this is almost always done in the Global.asax in Application_Start(). In that case, I would expect the container to be ready when your module is called.

Categories

Resources