You'll have to excuse the strange title but after several hours of looking at the same issue, it's the best I could come up with!
I initially had signalr embedded with my MVC project but moved it out into an self-hosted OWIN application.
When ever a hub is called upon, on the initial load it will load the hub twice and subsequent calls it will load it three times.
Here is my hub, and I followed the documentation to the tee:
public class TestHub : Hub
{
private readonly ILifetimeScope _scope;
private ITestService _testService;
public TestHub(ILifetimeScope scope)
{
_scope = scope.BeginLifetimeScope();
_testService = _scope.Resolve<ITestService>();
}
public void SignalRTest()
{
var types = _testService.SomeMethod();
Clients.Caller.populateSignalRTest(types);
}
protected override void Dispose(bool disposing)
{
if (disposing && _scope != null)
{
_scope.Dispose();
}
base.Dispose (disposing);
}
}
Here is the OWIN configuration:
var listener = (HttpListener)appBuilder.Properties[typeof(HttpListener).FullName];
listener.AuthenticationSchemes = AuthenticationSchemes.Ntlm;
appBuilder.UseCors(CorsOptions.AllowAll);
var builder = new ContainerBuilder();
// ... Other modules being imported ...
builder.RegisterModule<NLogModule>();
builder.RegisterType<TestService> ().As<ITestService> ();
builder.RegisterHubs(Assembly.GetExecutingAssembly());
var config = new HubConfiguration();
var container = builder.Build();
config.Resolver = new AutofacDependencyResolver(container);
config.EnableDetailedErrors = true;
appBuilder.UseAutofacMiddleware(container);
appBuilder.MapSignalR("/signalr", config);
Autofac has to resolve all dependencies before running any methods - the application calls upon mulitple Hubs and this takes a long time to resolve.
Here are the versions I am using:
SignalR 2.1.2 (Also tried 2.2.0)
AutoFac 3.5.2
Has anyone come across this or know why this is happening?
Thanks
A colleague did a bit of debugging whilst I raged and did something else and discovered it was one of the services rather than AutoFac or signalR. We have an Index and the constructor method of the service was verifying the index was valid. Remove it and it was fine.
Related
I want to resolve the Named registration where the name is given at runtime.
Background story - I am exposing my endpoints with multiple versions, e.g.:
https://localhost/myservice/api/v1/allcities
https://localhost/myservice/api/v2/allcities
...
The controller needs to invoke a different version of the injected service based on the version of the invoked endpoint.
To illustrate, I would expect when I invoke https://localhost/myservice/api/v1/blahblah it executes using MyServiceV1 and when I invoke https://localhost/myservice/api/v2/blahblah it executes using MyServiceV2
I'm using .NET Core 3.1 API with Microsoft.AspNetCore.Mvc.Versioning, Autofac 6.
I can get what version was invoked via IHttpContextAccessor as contextAccessor.HttpContext.GetRequestedApiVersion().
In this question I want to focus on resolving a specific version of the service at runtime.
My idea was to register the service as Named (or Keyed, doesn't matter) and using the Registration middleware registration intercept the resolution process and inject the proper version of the Named service.
Code:
public interface IMyService
{
string GetImplementationVersion();
}
[MyServiceVersioning("1.0")]
public class MyService1 : IMyService
{
public string GetImplementationVersion() => "Example 1.0";
}
[MyServiceVersioning("2.0")]
public class MyService2 : IMyService
{
public string GetImplementationVersion() => "Example 2.0";
}
public class MyMasterService
{
private IMyService _myService;
public MyMasterService(IMyService myService)
{
_myService = myService;
}
public string GetInjectedVersion() => _myService.GetImplementationVersion();
}
The registrations (Edited for completeness)
// ...
builder.RegisterType<VersionService>().As<IVersionService>();
// next two lines registered using extension that's in the next code block example
builder.RegisterVersioned<MyServiceV1, IMyService>();
builder.RegisterVersioned<MyServiceV2, IMyService>();
builder.Register<MyMasterService>();
And finally the implementation of RegisterVersioned extension where the question lies:
public static class AutofacVersioningExtensions
{
public static IRegistrationBuilder<T, ConcreteReflectionActivatorData, SingleRegistrationStyle>
RegisterVersioned<T, TInterface>(this ContainerBuilder builder) where T: class
{
var versioningAttribute = typeof(T).GetCustomAttribute<MyServiceVersionAttribute>();
if (versioningAttribute == null)
{
// no versioning exists, do it simply
return builder.RegisterType<T>().As<TInterface>();
}
return builder.RegisterType<T>().As<TInterface>().Named<TInterface>(versioningAttribute.Version).ConfigurePipeline(p =>
{
p.Use(PipelinePhase.RegistrationPipelineStart, (context, next) =>
{
var invokedVersion = context.Resolve<IVersionService>().CurrentVersion;
// HERE --> in the next line issue is that we have obvious circular resolution
// + it only resolves the last registration
// (I could do Resolve<IEnumerable<TInterface>> to get all registrations but that's an overhead that I'd like to avoid if possible).
var resolved = context.ResolveNamed<TInterface>(invokedVersion);
if (resolved != null)
{
context.Instance = resolved;
}
else
{
next(context);
}
});
});
}
}
Do you have any ideas? Is my approach even on the right path?
It seems like you could make this easier by using a lambda registration.
builder.RegisterType<MyServiceV1>().Keyed<IMyService>("1.0");
builder.RegisterType<MyServiceV2>().Keyed<IMyService>("2.0");
builder.Register<MyMasterService>();
builder.Register(ctx =>
{
var version = ctx.Resolve<IHttpContextAccessor>().HttpContext.GetRequestedApiVersion();
return ctx.ResolveKeyed<IMyService>(version);
}).As<IMyService>().InstancePerLifetimeScope();
This way, when you resolve an un-keyed IMyService, it'll go through the detection process and return the right keyed version. That keyed instance will be cached for the duration of the request.
I would like to initialize some dependencies resolved from the MassTransit serviceProvider in the same way Asp.Net Core does with the pipeline's middlewares.
In particular I would like to inspect the incoming message before the consumer is called and extract the tenant from it (I'm currently working on a multitenant web application with single database per tenant).
With this informations I need to initialize some scoped instances (Ef Core DbContext for example).
I know that I can inject them in the Consumer through constructor but this means that I must do that everytime I write a new one, so I suppose that a filter should be the right place (correct me if I'm wrong).
The problem raises when I need to access the current consumer scope to resolve the dependencies that I need. I was thinking that the behavior of the MassTransit' pipeline was similar to the Asp.Net one regarding middleware injection but I was probably wrong.
I haven't found any documentation on how to do that clearly without cluttering the code of the filter, so any suggestion is going to be really appreciated.
This is the filter that I need to modify:
public class TenantContextInitializerFilter<T> : IFilter<T> where T : class, ConsumeContext
{
public void Probe(ProbeContext context) { }
public async Task Send(T context, IPipe<T> next)
{
//Resolve scoped instance here and do something before Consumer is called
var connectionStringProvider = scope.GetService<IConnectionStringProvider>();
await next.Send(context);
}
}
public class RegistrationsDeliveredEventConsumer : IConsumer<IRegistrationsDelivered>
{
private readonly IConnectionStringProvider _connectionStringProvider;
public RegistrationsDeliveredEventConsumer(IConnectionStringProvider connectionStringProvider)
{
//This should be the same instance that has been resolved in the filter' Send() method
_connectionStringProvider = connectionStringProvider;
}
public async Task Consume(ConsumeContext<IRegistrationsDelivered> context)
{
}
}
This is a simplified example of my code but this should be enough
There's two facets to consider: 1) are filters registered as services/pulled from the service collection when using the ASP.NET Core integration and 2) what lifetime do the filters have if they are. I'm not familiar with the MassTransit ASP.NET Core integration, but it looks like you should be good based on a cursory review. You'll need to confirm that both of those requirements are met.
For dependency injection, in general, constructor injection is the way to go unless there's a very specific need to do something different, which does not seem to be the case here. In short, you need a constructor for your filter.
What exactly you need to inject is a function of the lifetime of the filter. If it has a transient lifetime, then you can inject your scoped dependencies directly. If it has a singleton lifetime, then you'll need to inject IServiceProvider instead, and do the following whenever you need to use one of those dependencies:
using (var scope = _serviceProvider.CreateScope())
{
var dep = scope.ServiceProvider.GetRequiredService<MyDependency>();
// do something with `dep`
}
Here's a draft... I'm sure there are missing pieces, so let me know if you have questions.
public class TenantContextInitializerFilter<T> : IFilter<T> where T : class, ConsumeContext
{
private readonly Func<string, IDbConnection> _dbContextAccessor;
public void Probe(ProbeContext context) { }
public TenantContextInitializerFilter(Func<string, IDbConnection> dbContextAccessor)
{
_dbContextAccessor = dbContextAccessor;
}
public async Task Send(T context, IPipe<T> next)
{
var tenantId = ""; // place holder
using (var dbContext = _dbContextAccessor(tenantId))
{
//... do db logic
}
await next.Send(context);
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IConnectionStringProvider>(
provider => null /* TODO figure out how to fetch scoped instance from a cache or some storage mechanism*/);
services.AddScoped(provider =>
{
IDbConnection Accessor(string tenantId)
{
if (provider.GetService<IConnectionStringProvider>()
.TryGetConnectionString(tenantId, out var connectionString, out var providerName))
return new SqlConnection(connectionString);
throw new Exception();
}
return (Func<string, IDbConnection>)Accessor;
});
}
}
Please help, this is driving me crazy!
My Server -> SignalR (JS) Client method execution works fine via the SignalR pipeline, but not when called via a background service. I'm using ASP.NET MVC / Castle Windsor DI
I use a custom Dependency Resolver which I register during Owin Startup
I observe that via the NotificationHub (SignalR pipeline), Clients is resolved to a HubConnectionContext class:
However, via my background service, Clients resolves to a HubConnectionContextBase class:
... so I'm pretty sure it's a DI issue. I just can't see what I'm doing wrong. Also any tips to Debug would be greatly appreciated. Thanks
Application Start:
bootstrapper = ContainerBootstrapper.Bootstrap();
this.container = bootstrapper.Container;
var resolverSignalR = new DependencyResolverSignalR(container);
GlobalHost.DependencyResolver = resolverSignalR;
OwinConfig:
app.MapSignalR(url, DependencyResolverSignalR.CreateHubConfiguration());
DependencyResolverSignalR:
public class DependencyResolverSignalR : DefaultDependencyResolver
{
public static HubConfiguration CreateHubConfiguration()
{
var signalrDependencyResolver = new DependencyResolverSignalR(_container);
var configuration = new HubConfiguration {
EnableDetailedErrors = true,
Resolver = signalrDependencyResolver
};
return configuration;
}
private static IWindsorContainer _container;
public DependencyResolverSignalR(IWindsorContainer container)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
_container = container;
}
public override object GetService(Type serviceType)
{
return _container.Kernel.HasComponent(serviceType) ? _container.Resolve(serviceType) : base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
return _container.Kernel.HasComponent(serviceType) ? _container.ResolveAll(serviceType).Cast<object>() : base.GetServices(serviceType);
}
}
NotificationService:
(runs via a loop every 10 seconds - AFTER client has connected)
// Not working
var hubContext = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
var clients = hubContext.Clients;
clients.All.receiveMessage(testMessage);
NotificationHub:
public override Task OnConnected()
{
var connectionId = Context.ConnectionId;
...
...
// Working fine
base.Clients.All.receiveMessage(testMessage);
return base.OnConnected();
}
Client:
omitted since it works fine via the signalr pipeline
I would expect the receiveMessage() client method called via the NotificationService to be executed on the client in exactly the same way it does when called via the SignalR pipeline. Instead nada. No error message, the call just silently does nothing.
I've even tried following the guide here (though it's geared towards Ninject) SignalR document to resolve the Clients (IHubConnectionContext) directly. Exactly the same result.
Code:
var resolverSignalR = new DependencyResolverSignalR(container);
container.Register(Component
.For<IHubConnectionContext<dynamic>>()
.UsingFactoryMethod(() =>
resolverSignalR.Resolve<IConnectionManager().GetHubContext<NotificationHub>().Clients));
Solved! It was an issue with the HubConfiguration - I'm not sure what exactly. But just bypassing it (which is possible because I'm already replacing the DependencyResolver in Application_Start()) fixed the issue.
Old OwinConfig:
app.MapSignalR(url, DependencyResolverSignalR.CreateHubConfiguration());
Replaced by:
app.MapSignalR();
but make sure you have something like this in Application_Start() (or wherever you initialise your DI container)
var resolverSignalR = new DependencyResolverSignalR(container);
GlobalHost.DependencyResolver = resolverSignalR;
I am using Serilog with Autofac and .NET Core. I am using a serilog enricher, which requires an instance during setup.
var enricher = new ContextEnricher();
Configuration = new LoggerConfiguration()
.MinimumLevel.ControlledBy(new LoggingLevelSwitch { MinimumLevel = logLevel })
.Enrich.WithThreadId()
.Enrich.With(enricher)
.WriteTo.ApplicationInsightsTraces(applicationInsightsKey)
.WriteTo.Console();
Log.Logger = Configuration.CreateLogger();
I ideally want my logger to be built before my DI container is built so that I can trap errors as soon as possible. The issue is that my log enricher needs to consume something in RequestScope, but is set up before DI even takes place. The obvious direction is to use a factory function after the container is built.
public class ContextEnricher : IContextEnricher
{
public Func<ICorrelationProvider> GetCorrelationProvider { get; set; }
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
if (GetCorrelationProvider == null) return;
var correlationProvider = GetCorrelationProvider();
if (correlationProvider == null) return;
logEvent.AddOrUpdateProperty(new LogEventProperty("CorrelationId", new ScalarValue(correlationProvider.CorrelationId)));
}
}
I based this solution off of this article: https://robdmoore.id.au/blog/2013/03/23/resolving-request-scoped-objects-into-a-singleton-with-autofac
The problem is, the article uses:
return () => DependencyResolver.Current.GetService<T>();
As the factory function - but the DependencyResolver does not exist in .NET Core.
I have tried using:
return () => Container.Resolve<T>();
But get this runtime error:
No scope with a tag matching 'AutofacWebRequest' is visible from the
scope in which the instance was requested. If you see this during
execution of a web application, it generally indicates that a
component registered as per-HTTP request is being requested by a
SingleInstance() component (or a similar scenario). Under the web
integration always request dependencies from the dependency resolver
or the request lifetime scope, never from the container itself.
What is the correct way to achieve this in .NET Core without access to the dependency resolver?
Using .NET 4.5.2, Visual studio 2017, C# 7.1, Unity, NServiceBus 6.
I receive the following error:
My application is a console app, here's some of the Program.cs code:
private static async Task ConfigureUnity()
{
IUnityContainer container = new UnityContainer();
var endpointConfiguration = new EndpointConfiguration("NSB.ChannelAdvisorService");
var transport = endpointConfiguration.UseTransport<LearningTransport>();
endpointConfiguration.AssemblyScanner().ExcludeAssemblies("netstandard");
endpointConfiguration.UseContainer<UnityBuilder>(
customizations =>
{
customizations.UseExistingContainer(container);
});
var endpointInstance = Endpoint.Start(endpointConfiguration).GetAwaiter().GetResult();
//register
container.RegisterType(typeof(IGenericHttpRequestRepository<>), typeof(GenericHttpRequestRepository<>), new TransientLifetimeManager());
container.RegisterType<IOrderRepository, OrderRepository>();
container.RegisterType<IShipmentRepository, ShipmentRepository>();
container.RegisterType<IOrderProcessService, OrderProcessService>();
container.RegisterType<IShipmentService, ShipmentService>();
container.RegisterInstance(endpointConfiguration);
//resolve
var orderProcessService = container.Resolve<IOrderProcessService>();
var shipmentService = container.Resolve<IShipmentService>();
.....
As you can see I'm using Unity and NServiceBus, this is to register DI and also use it withing NServicebus so i can DI it into my service to send a command.
The service trys to DI "IEndpointInstance"
public class OrderProcessService : IOrderProcessService
{
private static Logger logger = LogManager.GetCurrentClassLogger();
private readonly IEndpointInstance _endpoint;
public OrderProcessService(IEndpointInstance endpoint)
{
_endpoint = endpoint;
}
public async Task PostNewOrderBatch()
{
var list = _orderRepository.GetBatchedOrders();
foreach(var item in list)// parallel this?
{
await _endpoint.Send(item.ToObject<ProcessBatchOrdersCommand>()).ConfigureAwait(false);
_orderRepository.DeleteFile(item.Property("FilePath").Value.ToString());
}
}
}
I get the feeling it could be an issue about the order of things, I don't think I've missed anything out as far as i can tell in some examples?
In NServiceBus v6 and later the endpoint instance is no longer automatically registered in the container. You need to register the endpoint instance returned from Endpoint.Start(configuration) on the existing container.
See https://docs.particular.net/nservicebus/dependency-injection/#using-an-existing-instance-endpoint-resolution