I'm just wondering if it's possible to have async/await during DI.
Doing the following, the DI fails to resolve my service.
services.AddScoped(async provider =>
{
var client = new MyClient();
await client.ConnectAsync();
return client;
});
where as the following works perfectly fine.
services.AddScoped(provider =>
{
var client = new MyClient();
client.ConnectAsync().Wait();
return client;
});
Although it is theoretically possible to use async/await during object resolution, you should consider the following constraints:
Constructors can't be asynchronous, and
Construction of object graphs should be simple, reliable and fast
Because of these constraints, verything that involves I/O should be postponed until after the object graph has been constructed.
So instead of injecting a connected MyClient, MyClient should connect when it is used for the first timeānot when it is created.
Since your MyClient is not an application component but a third-party component, this means that you can't ensure that it "connect[s] when it is used for the first time."
This shouldn't be a problem, however, because the Dependency Inversion Principle already teaches us that:
the abstracts are owned by the upper/policy layers
This means that application components should not depend on third-party components directly, but instead they should depend on abstractions defined by the application itself. As part of the Composition Root, adapters can be written that implement these abstractions and adapt application code to the third-party libraries.
An important advantage of this is that you are in control over the API that your application components use, which is the key to success here, as it allows the connectivity issues to be hidden behind the abstraction completely.
Here's an example of how your application-tailored abstraction might look like:
public interface IMyAppService
{
Task<Data> GetData();
Task SendData(Data data);
}
Do note that this abstraction lacks an ConnectAsync method; this is hidden behind the abstraction. Take a look at the following adapter for instance:
public sealed class MyClientAdapter : IMyAppService, IDisposable
{
private readonly Lazy<Task<MyClient>> connectedClient;
public MyClientAdapter()
{
this.connectedClient = new Lazy<Task<MyClient>>(async () =>
{
var client = new MyClient();
await client.ConnectAsync();
return client;
});
}
public async Task<Data> GetData()
{
var client = await this.connectedClient.Value;
return await client.GetData();
}
public async Task SendData(Data data)
{
var client = await this.connectedClient.Value;
await client.SendData(data);
}
public void Dispose()
{
if (this.connectedClient.IsValueCreated)
{
this.connectedClient.Value.Dispose();
}
}
}
The adapter hides the connectivity details from the application code. It wraps the creation and connection of MyClient in a Lazy<T>, which allows the client to be connected just once, independently of in which order the GetData and SendData methods are called, and how many times.
This allows you to let your application components depend on IMyAppService instead of MyClient and register the MyClientAdapter as IMyAppService with the appropriate lifestyle.
Related
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.
Does any one know if it is possible to host multiple instances of WebApplicationFactory<TStartop>() in the same unit test?
I have tried and can't seem to get anywhere with this one issue.
i.e
_client = WebHost<Startup>.GetFactory().CreateClient();
var baseUri = PathString.FromUriComponent(_client.BaseAddress);
_url = baseUri.Value;
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer", "Y2E890F4-E9AE-468D-8294-6164C59B099Y");
WebHost is just a helper class that allows me to build factory and then a client easily in one line.
Under the covers all it does is this:
new WebApplicationFactory<TStartup>() but a few other things too.
It would be nice if i could stand up another instace of a different web server to test server to server functionality.
Does anyone know if this is possible or not?
Contrary to what the accepted answer states, it is actually pretty easy to test server to server functionality using two WebApplicationFactory instances:
public class OrderAPIFactory : WebApplicationFactory<Order>
{
public OrderAPIFactory() { ... }
protected override void ConfigureWebHost(IWebHostBuilder builder) { ... }
}
public class BasketAPIFactory : WebApplicationFactory<BasketStartup>
{
public BasketAPIFactory() { ... }
protected override void ConfigureWebHost(IWebHostBuilder builder) { ... }
}
Then you can instantiate the custom factories as follows:
[Fact]
public async Task TestName()
{
var orderFactory = new OrderAPIFactory();
var basketFactory = new BasketAPIFactory();
var orderHttpClient = orderFactory.CreateClient();
var basketHttpClient = basketFactory.CreateClient();
// you can hit eg an endpoint on either side that triggers server-to-server communication
var orderResponse = await orderHttpClient.GetAsync("api/orders");
var basketResponse = await basketHttpClient.GetAsync("api/basket");
}
I also disagree with the accepted answer about it necessarily being bad design: it has its use-cases. My company has a microservices infrastructure which relies on data duplication across microservices and uses an async messaging queue with integration events to ensure data consistency. Needless to say that messaging functionality plays a central role and needs to be tested properly. A test setup as described here is pretty useful in this situation. For example it allows us to thoroughly test how messages are being dealt with by a service that was down at the moment those messages were published:
[Fact]
public async Task DataConsistencyEvents_DependentServiceIsDown_SynchronisesDataWhenUp()
{
var orderFactory = new OrderAPIFactory();
var orderHttpClient = orderFactory.CreateClient();
// a new order is created which leads to a data consistency event being published,
// which is to be consumed by the BasketAPI service
var order = new Order { ... };
await orderHttpClient.PostAsync("api/orders", order);
// we only instantiate the BasketAPI service after the creation of the order
// to mimic downtime. If all goes well, it will still receive the
// message that was delivered to its queue and data consistency is preserved
var basketFactory = new BasketAPIFactory();
var basketHttpClient = orderFactory.CreateClient();
// get the basket with all ordered items included from BasketAPI
var basketResponse = await basketHttpClient.GetAsync("api/baskets?include=orders");
// check if the new order is contained in the payload of BasketAPI
AssertContainsNewOrder(basketResponse, order);
}
It is possible to host multiple communicating instances of WebApplicationFactory in single integration test.
Let's say we have master service named WebApplication, which depends on utility service named WebService using named HttpClient with name "WebService".
Here is example of integration test:
[Fact]
public async Task GetWeatherForecast_ShouldReturnSuccessResult()
{
// Create application factories for master and utility services and corresponding HTTP clients
var webApplicationFactory = new CustomWebApplicationFactory();
var webApplicationClient = webApplicationFactory.CreateClient();
var webServiceFactory = new WebApplicationFactory<Startup>();
var webServiceClient = webServiceFactory.CreateClient();
// Mock dependency on utility service by replacing named HTTP client
webApplicationFactory.AddHttpClient(clientName: "WebService", webServiceClient);
// Perform test request
var response = await webApplicationClient.GetAsync("weatherForecast");
// Assert the result
response.EnsureSuccessStatusCode();
var forecast = await response.Content.ReadAsAsync<IEnumerable<WeatherForecast>>();
Assert.Equal(10, forecast.Count());
}
This code requires CustomWebApplicationFactory class to be implemented:
// Extends WebApplicationFactory allowing to replace named HTTP clients
internal sealed class CustomWebApplicationFactory
: WebApplicationFactory<WebApplication.Startup>
{
// Contains replaced named HTTP clients
private ConcurrentDictionary<string, HttpClient> HttpClients { get; } =
new ConcurrentDictionary<string, HttpClient>();
// Add replaced named HTTP client
public void AddHttpClient(string clientName, HttpClient client)
{
if (!HttpClients.TryAdd(clientName, client))
{
throw new InvalidOperationException(
$"HttpClient with name {clientName} is already added");
}
}
// Replaces implementation of standard IHttpClientFactory interface with
// custom one providing replaced HTTP clients from HttpClients dictionary
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.ConfigureServices(services =>
services.AddSingleton<IHttpClientFactory>(
new CustomHttpClientFactory(HttpClients)));
}
}
And finally, CustomHttpClientFactory class is required:
// Implements IHttpClientFactory by providing named HTTP clients
// directly from specified dictionary
internal class CustomHttpClientFactory : IHttpClientFactory
{
// Takes dictionary storing named HTTP clients in constructor
public CustomHttpClientFactory(
IReadOnlyDictionary<string, HttpClient> httpClients)
{
HttpClients = httpClients;
}
private IReadOnlyDictionary<string, HttpClient> HttpClients { get; }
// Provides named HTTP client from dictionary
public HttpClient CreateClient(string name) =>
HttpClients.GetValueOrDefault(name)
?? throw new InvalidOperationException(
$"HTTP client is not found for client with name {name}");
}
The complete code of example you may find here: https://github.com/GennadyGS/AspNetCoreIntegrationTesting
The pros of such approach are:
ability to test interactions between the services;
no need to mock internals of services so that you can consider them as black boxes;
tests are stable to any refactorings including changes in communication protocol;
tests are fast, self-contained, do not require any prerequisites and give predictable results.
The main cons of such approach is possible conflicting dependencies of participating services (e.g. different major versions of EFCore) in real world scenarios due to the fact that all services using in test are running in single process.
There are several mitigations of such problem. One of them is to apply modular approach to services' implementations and load modules in runtime according to configuration file. This may allow to replace configuration file in tests, exclude several modules from loading and replace missing services with simpler mocks. The example of applying such approach you may find in branch "Modular" of the example repository above.
I was based on Gennadii Saltyshchak's solution to create this, which is exaclty what I was looking for: Two servers communicating with one another via a fallback mechanism.
In this example one server runs on port 80 and the other on 82 and there is an api endpoint called fallback that calls the hello endpoint on the fallback server.
Full solution can be found here: https://github.com/diogonborges/integration-test-communicating-servers
public class Tests
{
private HttpClient _port80Client;
private HttpClient _port82Client;
[SetUp]
public void Setup()
{
// Create application factories for master and utility services and corresponding HTTP clients
var port80Factory = new CustomWebApplicationFactory(80, 82);
_port80Client = port80Factory.CreateClient();
port80Factory.Server.Features.Set<IServerAddressesFeature>(new ServerAddressesFeature {Addresses = {"http://localhost:80"}});
var port82Factory = new CustomWebApplicationFactory(82, 80);
_port82Client = port82Factory.CreateClient();
port82Factory.Server.Features.Set<IServerAddressesFeature>(new ServerAddressesFeature {Addresses = {"http://localhost:82"}});
// Mock dependency on utility service by replacing named HTTP client
port80Factory.AddHttpClient(Constants.Fallback, _port82Client);
port82Factory.AddHttpClient(Constants.Fallback, _port80Client);
}
[Test]
public async Task Port80_says_hello()
{
var response = await _port80Client.GetAsync("hello");
var content = await response.Content.ReadAsStringAsync();
Assert.AreEqual("hello from http://localhost:80", content);
}
[Test]
public async Task Port80_falls_back_to_82()
{
var response = await _port80Client.GetAsync("hello/fallback");
var content = await response.Content.ReadAsStringAsync();
Assert.AreEqual("hello from http://localhost:82", content);
}
[Test]
public async Task Port82_says_hello()
{
var response = await _port82Client.GetAsync("hello");
var content = await response.Content.ReadAsStringAsync();
Assert.AreEqual("hello from http://localhost:82", content);
}
[Test]
public async Task Port82_falls_back_to_80()
{
var response = await _port82Client.GetAsync("hello/fallback");
var content = await response.Content.ReadAsStringAsync();
Assert.AreEqual("hello from http://localhost:80", content);
}
}
No. It's not possible. WebApplicationFactory leans on xUnit's IClassFixture, which has to be applied at the class level, meaning you only get one bite at the apple. The WebApplicationFactory itself is capable of being customized per test, which fulfills most use cases where you're need a "different" one, but it doesn't help you wanting two totally separate active test servers at the same time.
However, that said, what you're wanting is a bad test design in the first place. The whole point of testing is to eliminate variables so you can actually ensure the piece of the SUT is actually working. Even in an integration testing environment, you're still just looking at one particular interaction between pieces of your application. Have two test servers, feeding off each other, effectively multiplies the variables giving you no assurance that either side is working correctly.
One of my dependencies (DbContext) is registered using the WebApiRequestLifestyle scope.
Now, my background job uses IoC and depends on the service that was registered above using the WebApiRequestLifestyle. I'm wondering how this works when Hangfire calls the method i registered for the background job. Will the DbContext be treated like a transistent object since the web api is not involved?
Any guidance would be great!
Here is my initialize code that occurs during start up:
public void Configuration(IAppBuilder app)
{
var httpConfig = new HttpConfiguration();
var container = SimpleInjectorWebApiInitializer.Initialize(httpConfig);
var config = (IConfigurationProvider)httpConfig.DependencyResolver
.GetService(typeof(IConfigurationProvider));
ConfigureJwt(app, config);
ConfigureWebApi(app, httpConfig, config);
ConfigureHangfire(app, container);
}
private void ConfigureHangfire(IAppBuilder app, Container container)
{
Hangfire.GlobalConfiguration.Configuration
.UseSqlServerStorage("Hangfire");
Hangfire.GlobalConfiguration.Configuration
.UseActivator(new SimpleInjectorJobActivator(container));
app.UseHangfireDashboard();
app.UseHangfireServer();
}
public static Container Initialize(HttpConfiguration config)
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();
InitializeContainer(container);
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.RegisterWebApiControllers(config);
container.RegisterMvcIntegratedFilterProvider();
container.Register<Mailer>(Lifestyle.Scoped);
container.Register<PortalContext>(Lifestyle.Scoped);
container.RegisterSingleton<TemplateProvider, TemplateProvider>();
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
config.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);
return container;
}
Here is my code that kicks off the background job:
public class MailNotificationHandler : IAsyncNotificationHandler<FeedbackCreated>
{
private readonly Mailer mailer;
public MailNotificationHandler(Mailer mailer)
{
this.mailer = mailer;
}
public Task Handle(FeedbackCreated notification)
{
BackgroundJob.Enqueue<Mailer>(x => x.SendFeedbackToSender(notification.FeedbackId));
BackgroundJob.Enqueue<Mailer>(x => x.SendFeedbackToManagement(notification.FeedbackId));
return Task.FromResult(0);
}
}
Finally here is the code that runs on the background thread:
public class Mailer
{
private readonly PortalContext dbContext;
private readonly TemplateProvider templateProvider;
public Mailer(PortalContext dbContext, TemplateProvider templateProvider)
{
this.dbContext = dbContext;
this.templateProvider = templateProvider;
}
public void SendFeedbackToSender(int feedbackId)
{
Feedback feedback = dbContext.Feedbacks.Find(feedbackId);
Send(TemplateType.FeedbackSender, new { Name = feedback.CreateUserId });
}
public void SendFeedbackToManagement(int feedbackId)
{
Feedback feedback = dbContext.Feedbacks.Find(feedbackId);
Send(TemplateType.FeedbackManagement, new { Name = feedback.CreateUserId });
}
public void Send(TemplateType templateType, object model)
{
MailMessage msg = templateProvider.Get(templateType, model).ToMailMessage();
using (var client = new SmtpClient())
{
client.Send(msg);
}
}
}
I'm wondering how this works when Hangfire calls the method i registered for the background job. Will the DbContext be treated like a transistent object since the web api is not involved?
As the design decisions describe, Simple Injector will never allow you to resolve an instance outside an active scope. So that DbContext will neither be resolved as transient or singleton; Simple Injector will throw an exception when there's no scope.
Every application type requires its own type of scoped lifestyle. Web API requires the AsyncScopedLifestyle (in previous versions WebApiRequestLifestyle), WCF an WcfOperationLifestyle and MVC the WebRequestLifestyle. For Windows Services you will typically use an AsyncScopedLifestyle.
If your Hangfire jobs run in a Windows Service, you will have to use either a ThreadScopedLifestyle or the AsyncScopedLifestyle. Those scopes require explicit starting.
When running the jobs on a background thread in a web (or Web API) application, there is no access to the required context and this means that Simple Injector will throw an exception if you try to do so.
You however are using the Hangfire.SimpleInjector integration library. This library implements a custom JobActivator implementation called SimpleInjectorJobActivator and this implementation will create start a Scope for you on the background thread. Hangfire will actually resolve your Mailer within the context of this execution context scope. So the Mailer constructor argument in your MailNotificationHandler is actually never used; Hangfire will resolve this type for you.
The WebApiRequestLifestyle and AsyncScopedLifestyle are interchangeable; the WebApiRequestLifestyle uses an execution context scope in the background and the SimpleInjectorWebApiDependencyResolver actually starts an execution context scope. So the funny thing is that your WebApiRequestLifestyle can be used for background operations as well (although it can be a bit confusing). So your solution works and works correctly.
When running in MVC, however, this will not work, and in that case you would have to create a Hybrid lifestyle, for instance:
var container = new Container();
container.Options.DefaultScopedLifestyle = Lifestyle.CreateHybrid(
new AsyncScopedLifestyle(),
new WebRequestLifestyle());
You can register your DbContext as follows:
container.Register<DbContext>(() => new DbContext(...), Lifestyle.Scoped);
Here's some feedback on your application's design, if you don't mind.
Prevent letting application code, such as your MailNotificationHandler, from taking a direct dependency on an external library such as Hangfire. This is a direct violation of the Dependency Inversion Principle and makes your application code very hard to test and maintain. Instead, let solely your Composition Root (the place where you wire your dependencies) take a dependency on Hangfire. In your case, the solution is really straightforward and I would even say pleasant, and it would look as follows:
public interface IMailer
{
void SendFeedbackToSender(int feedbackId);
void SendFeedbackToManagement(int feedbackId);
}
public class MailNotificationHandler : IAsyncNotificationHandler<FeedbackCreated>
{
private readonly IMailer mailer;
public MailNotificationHandler(IMailer mailer)
{
this.mailer = mailer;
}
public Task Handle(FeedbackCreated notification)
{
this.mailer.SendFeedbackToSender(notification.FeedbackId));
this.mailer.SendFeedbackToManagement(notification.FeedbackId));
return Task.FromResult(0);
}
}
Here we added a new IMailer abstraction and made the MailNotificationHandler dependent on this new abstraction; unaware of the existence of any background processing. Now close to the part where you configure your services, define an IMailer proxy that forwards the calls to Hangfire:
// Part of your composition root
private sealed class HangfireBackgroundMailer : IMailer
{
public void SendFeedbackToSender(int feedbackId) {
BackgroundJob.Enqueue<Mailer>(m => m.SendFeedbackToSender(feedbackId));
}
public void SendFeedbackToManagement(int feedbackId) {
BackgroundJob.Enqueue<Mailer>(m => m.SendFeedbackToManagement(feedbackId));
}
}
This requires the following registrations:
container.Register<IMailer, HangfireBackgroundMailer>(Lifestyle.Singleton);
container.Register<Mailer>(Lifestyle.Transient);
Here we map the new HangfireBackgroundMailer to the IMailer abstraction. This ensures that the BackgroundMailer is injected into your MailNotificationHandler, while the Mailer class is resolved by Hangfire when the background thread is started. The registration of the Mailer is optional, but advisable, since it has become a root object, and since it has dependencies, we want Simple Injector to be aware of this type to allow it to verify and diagnose this registration.
I hope you agree that from perspective of the MailNotificationHandler, the application is much cleaner now.
I am creating an application where SignalR is used to broadcast real-time tweets to a map. I am using the C# Tweetinvi library (tweetinvi.codeplex.com) to handle all of the logic associated with connecting to the Twitter Streaming API.
The Twitter API specifies that only one streaming connection can be open to Twitter at any time. As I am using SignalR, there is a dependency between the Streaming connection and the Hub class. I know that the Hub class is transient, meaning that it is created each time a client requests it, so I need to ensure that the instance of my Twitter Stream class injected into the Hub class is a singleton, or at least IFilteredStream is only created once in the lifetime of the application. Here is the boilerplate code to connect to the API:
public class TweetStream
{
private IFilteredStream _stream;
public TweetStream()
{
var consumerKey = ConfigurationManager.AppSettings.Get("twitter:ConsumerKey");
var consumerSecret = ConfigurationManager.AppSettings.Get("twitter:ConsumerSecret");
var accessKey = ConfigurationManager.AppSettings.Get("twitter:AccessKey");
var accessToken = ConfigurationManager.AppSettings.Get("twitter:AccessToken");
TwitterCredentials.SetCredentials(accessKey, accessToken, consumerKey, consumerSecret);
_stream = Stream.CreateFilteredStream();
}
// Return singular instance of _stream to Hub class for usage.
public IFilteredStream Instance
{
get { return _stream; }
}
}
The IFilteredStream interface exposes a lambda method as below which allows for receiving Tweets in real-time, which I would like to be able to access from within my SignalR Hub class:
_stream.MatchingTweetReceived += (sender, args) => {
Clients.All.broadcast(args.Tweet);
};
The source for this method can be found here
I've tried to implement Autofac, and it seems that the connection to the Twitter API happens, however nothing more happens. I've tried to debug this, but am unsure how to debug such a scenario using dependency injection. My Hub class currently looks like this:
public class TwitterHub : Hub
{
private readonly ILifetimeScope _scope;
private readonly TweetStream _stream;
// Inject lifetime scope and resolve reference to TweetStream
public TwitterHub(ILifetimeScope scope)
{
_scope = scope.BeginLifetimeScope();
_stream = scope.Resolve<TweetStream>();
var i = _stream.Instance;
_stream.MatchingTweetReceived += (sender, args) => {
Clients.All.broadcast(args.Tweet);
};
i.StartStreamMatchingAllConditions();
}
}
And finally, my OWIN Startup class, where I register my dependencies and Hub with Autofac:
[assembly: OwinStartup(typeof(TwitterMap2015.App_Start.OwinStartup))]
namespace TwitterMap2015.App_Start
{
public class OwinStartup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
// use hubconfig, not globalhost
var hubConfig = new HubConfiguration {EnableDetailedErrors = true};
builder.RegisterHubs(Assembly.GetExecutingAssembly()); // register all SignalR hubs
builder.Register(i => new TweetStream()).SingleInstance(); // is this the correct way of injecting a singleton instance of TweetStream?
var container = builder.Build();
hubConfig.Resolver = new AutofacDependencyResolver(container);
app.MapSignalR("/signalr", hubConfig);
}
}
}
Sorry if this question is a bit of a mess, I'm having a hard time of understand what kind of architecture I need to implement to get this working! Open to advice / recommendations on how this could be improved, or how it should be done!
IMO this cannot work because you are wiring your event to call over the context of a specific hub instance, regardless of any code related to Autofac (which might have issues too but I'm not a big expert on it). Your hub's constructor will be called each time a new connection happens or a method is called from a client, so:
you are subscribing that event potentially multiple times per client. I don't know the Twitter API you are using, but on this note the fact that you call i.StartStreamMatchingAllConditions() all these times seems wrong to me
each time you create a closure over the Clients member of that instance in your event handler, which is supposed to go away when the hub is destroyed (so probably you are leaking memory)
What you need to do, given that your are calling over Client.All, and therefore this is a pure broadcast independent on any specific caller, is:
initialize your Twitter connection in the constructor of your TwitterStream service
in that same place (maybe with some indirection, but probably not necessary) take an instance of the hub context of your TwitterHub
subscribe to the event and use the context you just retrieved to broadcast over it
Such constructor might look like this:
public service TwitterStream : ??? <- an interface here?
{
...
public TwitterStream (ILifetimeScope scope ??? <- IMO you don't need this...)
{
//Autofac/Twitter stuff
...
var context = GlobalHost.DependencyResolver.GetHubContext<TwitterHub>();
_stream.MatchingTweetReceived += (sender, args) => {
context.Clients.All.broadcast(args.Tweet);
};
//maybe more Autofac/Twitter stuff
...
}
...
}
TwitterHub must exist, but in case you just need it to do this kind of broadcast to all, with no special code needed to monitor connections or handle client-generated calls, it could well be empty and it's just fine that your actual hub-related code lives outside of it and uses a IHubContext to broadcast messages. Such a code would take care of handling all the existing connected clients each time a tweet arrives, so no need to track them.
Of course if you have more requirements for actually handling clients separarely, then things might need to be different, but your code does not make me think otherwise.
I'm using a third-party library that has a setup structure like this:
IEngine engine = /* singleton provided elsewhere */
var server = new FooServer();
server.AddService("Data1", () => new Data1(engine));
server.AddService("Data2", () => new Data2(engine));
server.Start();
...
server.Dispose();
(The lambda is essentially a factory method; it will internally invoke that whenever it wants a new instance for its own purposes.)
Except that a further complication is that instead of adding the services directly, I'm using reflection to find and register them, so that they just need to be defined to work instead of needing to be explicitly listed out. Originally I wanted to do this completely self-contained, but constructing generic lambda methods based on reflected types just seemed too complicated, so for the moment I've settled with a Register method provided by each type:
class Data1 : DataProvider
{
public static void Register(FooServer server, IEngine engine)
{
server.AddService("Data1", () => new Data1(engine));
}
... (constructor, Dispose, other stuff)
}
var server = new FooServer();
foreach (var type in Utils.GetConcreteTypesWithBase<DataProvider>())
{
var method = type.GetMethod("Register", new[] { typeof(FooServer), typeof(IEngine) });
if (method != null)
{
method.Invoke(null, new object[] { server, engine });
}
// a more ideal approach would be to construct the needed lambda and call
// AddService directly instead of using Register, but my brain fails me.
}
server.Start();
...
server.Dispose();
Needless to say, this is a bit ugly and I'm sure there's a better way to do it. One other thing is that I'm already using Castle Windsor to create the IEngine and a few other things that use it, and I was wondering how to better integrate with that. (Currently I'm just Resolveing the engine at the point where this code needs it -- it's a singleton so lifetimes aren't thorny.)
What I'd really love is a way to use method parameter or constructor injection so that each DataProvider could have a different set of parameters based on their actual dependencies (instead of the union of all dependencies), just like you'd do when everything was under Windsor's control. But again, I'm not sure where to even start. I haven't really used Windsor much beyond the basics.
Note that FooServer, DataProvider and the AddService<T>(string name, Func<T> factory) where T: DataProvider method are in external code and I can't change them. The rest (including the engine) is my code. And again note that I do not create the Data1 instances in my code at all, just a factory lambda that tells the external server how to create them when it wants one.
Following qujck's answer with a few necessary edits resulted in the following code, for posterity:
var container = ...;
var server = new FooServer();
foreach (var type in Utils.GetConcreteTypesWithBase<DataProvider>())
{
var t = type; // necessary due to the lambda capturing
container.Register(Component.For(t).LifestyleTransient());
server.AddService(t.Name, () => {
var service = (DataProvider) container.Resolve(t);
service.Closed += (s, e) => container.Release(service);
return service;
});
}
server.Start();
...
server.Dispose();
This behaves as desired, though I'm still interested in methods to improve it further. (I was curious if there was some way to use Castle's own Classes.FromAssembly... etc syntax to tidy up the discovery and registration of the services, but haven't had much luck working that out.)
You could define lambda's that resolve from the container. This offers the benefits of managing all of your services and their related lifetimes in one place (the container).
You would need some way of establishing the name of each registration - in the example I have registered each service as the name of the type:
[Fact]
public void Configure1()
{
IWindsorContainer container = new WindsorContainer();
var server = new MockFooServer();
container.Register(Component.For<IEngine>().ImplementedBy<Engine>());
foreach (Type type in Utils.GetConcreteTypesWithBase<DataProvider>())
{
container.Register(Component.For(type));
server.AddService(type.Name, () => container.Resolve(type) as DataProvider);
}
var service1 = server.services[typeof(Service1).Name]();
Assert.IsType<Service1>(service1);
}
With a Mock FooServer for the test:
public class MockFooServer
{
public Dictionary<string, Func<DataProvider>> services =
new Dictionary<string, Func<DataProvider>>();
public void AddService<T>(string key, Func<T> factory) where T : DataProvider
{
this.services.Add(key, factory as Func<DataProvider>);
}
}