Trouble configuring SignalR in owin hosted Nancy app, plus MassTransit - signalr

Trying to set up a SignalR hub (the canonical chat) to run in an OWIN-hosted Nancy app and MassTransit. The MassTransit part works:
public ChatMessageConsumer(IEnumerable<IChatMessageProcessor> messageProcessors,
IChatHub chatHub,
ILogger logger) { ... }
public async Task Consume(ConsumeContext<IChatMessage> context)
{
_chatHub.BroadcastMessage( ... );
}
Those messages triggered by MassTransit events get to my browser client. The ones triggered from the client never do:
public class ChatHub : Hub, IChatHub
{
public ChatHub(ILifetimeScope lifetimeScope)
{
_hubLifetimeScope = lifetimeScope.BeginLifetimeScope();
// snip ...
}
// snip ...
}
public void Send(string name, string message)
{
// publish message on bus
_bus.Publish<IChatMessage>(/* snip ... */);
_hubInstanceContext.Clients.All.BroadcastMessage(/* ... */);
}
public void BroadcastMessage(string service, string message, string correlationId) { /* snip ... */ }
As best as I can tell, the framework is looking for a parameterless constructor, which I don't have (and obviously don't want). In my startup I create a configuration, register the Autofac resolver as the dependency resolver, and start SignalR with the configuration. Nancy is started using an Autofac bootstrapper with the same container.
public void Configuration(IAppBuilder app)
{
var container = ResolveDependencies();
var hubConfiguration = new HubConfiguration
{
EnableDetailedErrors = true,
Resolver = container.Resolve<IDependencyResolver>()
};
app.UseAutofacMiddleware(container);
app.MapSignalR(hubConfiguration);
app.Map("/site", sb => sb.UseNancy(options => options.Bootstrapper = new ChatBootstrapper(container)));
// snip MassTransit bus start/stop, etc. ...
}
static IContainer ResolveDependencies()
{
var builder = new ContainerBuilder();
// snip MassTransit consumers, etc. ...
builder.RegisterType<ChatHub>().As<IChatHub>().ExternallyOwned().SingleInstance();
builder.RegisterType<AutofacDependencyResolver>().As<IDependencyResolver>().SingleInstance();
builder.Register(i => i.Resolve<IDependencyResolver>()
.Resolve<IConnectionManager>()
.GetHubContext<ChatHub, IChatHub>())
.SingleInstance()
.ExternallyOwned();
// snip Logger registrations, etc. ...
}
Nancy is working (that's how I post events to MassTransit). MassTransit is working -- events are consumed and are able to broadcast SignalR events that are received by the client. SignalR is partially working -- I've obviously got a connection to a hub because I can receive messages broadcast from MassTransit consumers, but when I try to send a message from a client, I get a 500 internal server error. If I add a parameterless constructor to the hub, when I try to send from the client, that constructor is called, leading me to think that there's something wrong with how I've setup Autofac for SignalR. In that case, when the Send method is called, the _hubInstanceContext and _bus members, that should have been set via DI in the constructor are null. I've tried adding a IHubContext<IMyHub> property, as suggested in https://stackoverflow.com/a/36476106/173225, but that is never set, supporting my suspicion that DI isn't being used at all in this case.
I've reviewed the Autofac docs, many questions here on SO and several blog posts, but can't see what I might be missing. I should also note I did have the client chat working before adding Autofac to the mix (i.e. I could send a message and receive a broadcastMessage).
Any suggestions?
Edit: adding callstack in parameterless constructor:
> Chat.Service.exe!Chat.Service.Hubs.ChatHub.ChatHub() Line 25 C#
mscorlib.dll!System.Activator.CreateInstance(System.Type type, bool nonPublic) Unknown
mscorlib.dll!System.Activator.CreateInstance(System.Type type) Unknown
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Hubs.DefaultHubActivator.Create(Microsoft.AspNet.SignalR.Hubs.HubDescriptor descriptor) Line 29 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Hubs.DefaultHubManager.ResolveHub(string hubName) Line 89 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Hubs.HubDispatcher.CreateHub(Microsoft.AspNet.SignalR.IRequest request, Microsoft.AspNet.SignalR.Hubs.HubDescriptor descriptor, string connectionId, Microsoft.AspNet.SignalR.Hubs.StateChangeTracker tracker, bool throwIfFailedToCreate) Line 455 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Hubs.HubDispatcher.OnReceived(Microsoft.AspNet.SignalR.IRequest request, string connectionId, string data) Line 180 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.PersistentConnection.ProcessRequestPostGroupRead.AnonymousMethod__5() Line 282 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.FromMethod(System.Func<System.Threading.Tasks.Task> func) Line 771 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.PersistentConnection.ProcessRequestPostGroupRead.AnonymousMethod__4(string data) Line 282 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Transports.ForeverTransport.ProcessSendRequest() Line 151 C#
[Resuming Async Method]
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine) Unknown
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run() Unknown
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture.AnonymousMethod__72_0(System.Action f) Line 1036 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture.AnonymousMethod__71_0(System.Action<System.__Canon> f, System.__Canon state) Line 1028 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture<System.__Canon, System.__Canon, System.__Canon>(Microsoft.AspNet.SignalR.TaskAsyncHelper.CulturePair preservedCulture, System.Func<System.__Canon, System.__Canon, System.__Canon> func, System.__Canon arg1, System.__Canon arg2) Line 1009 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture<System.Action>(Microsoft.AspNet.SignalR.TaskAsyncHelper.CulturePair preservedCulture, System.Action<System.Action> action, System.Action arg) Line 1031 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture(Microsoft.AspNet.SignalR.TaskAsyncHelper.CulturePair preservedCulture, System.Action action) Line 1037 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAwaiterHelper.PreserveCultureUnsafeOnCompleted.AnonymousMethod__0() Line 48 C#
mscorlib.dll!System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(System.Action action, bool allowInlining, ref System.Threading.Tasks.Task currentTask) Unknown
mscorlib.dll!System.Threading.Tasks.Task.FinishContinuations() Unknown
mscorlib.dll!System.Threading.Tasks.Task.FinishStageThree() Unknown
mscorlib.dll!System.Threading.Tasks.Task<System.__Canon>.TrySetResult(System.__Canon result) Unknown
mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<Microsoft.AspNet.SignalR.Hosting.INameValueCollection>.SetResult(Microsoft.AspNet.SignalR.Hosting.INameValueCollection result) Unknown
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.Owin.ServerRequest.ReadForm() Line 111 C#
[Resuming Async Method]
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine) Unknown
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run() Unknown
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture.AnonymousMethod__72_0(System.Action f) Line 1036 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture.AnonymousMethod__71_0(System.Action<System.__Canon> f, System.__Canon state) Line 1028 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture<System.__Canon, System.__Canon, System.__Canon>(Microsoft.AspNet.SignalR.TaskAsyncHelper.CulturePair preservedCulture, System.Func<System.__Canon, System.__Canon, System.__Canon> func, System.__Canon arg1, System.__Canon arg2) Line 1009 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture<System.Action>(Microsoft.AspNet.SignalR.TaskAsyncHelper.CulturePair preservedCulture, System.Action<System.Action> action, System.Action arg) Line 1031 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAsyncHelper.RunWithPreservedCulture(Microsoft.AspNet.SignalR.TaskAsyncHelper.CulturePair preservedCulture, System.Action action) Line 1037 C#
Microsoft.AspNet.SignalR.Core.dll!Microsoft.AspNet.SignalR.TaskAwaiterHelper.PreserveCultureUnsafeOnCompleted.AnonymousMethod__0() Line 48 C#
mscorlib.dll!System.Threading.Tasks.AwaitTaskContinuation.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() Unknown
mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() Unknown
mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() Unknown
[Async Call]
Microsoft.Owin.dll!Microsoft.Owin.Mapping.MapMiddleware.Invoke(System.Collections.Generic.IDictionary<string, object> environment) Line 64 C#
[Async Call]
Autofac.Integration.Owin.dll!Owin.AutofacAppBuilderExtensions.RegisterAutofacLifetimeScopeInjector.AnonymousMethod__0(Microsoft.Owin.IOwinContext context, System.Func<System.Threading.Tasks.Task> next) Line 333 C#
[Async Call]
Microsoft.Owin.Host.HttpListener.dll!Microsoft.Owin.Host.HttpListener.OwinHttpListener.ProcessRequestAsync(System.Net.HttpListenerContext context) Line 262 C#
[Async Call]
Microsoft.Owin.Host.HttpListener.dll!Microsoft.Owin.Host.HttpListener.OwinHttpListener.ProcessRequestsAsync() Line 244 C#

I needed to change:
builder.RegisterType<ChatHub>().As<IChatHub>().ExternallyOwned().SingleInstance();
to:
builder.RegisterType<ChatHub>().ExternallyOwned();
builder.Register(c => c.Resolve<ChatHub>()).As<IChatHub>();
At this point I'm not entirely sure why, but I'm going to do some digging in the Autofac source and update this answer.
I had previously tried many variations, with and without SingleInstance and AsImplementedInterfaces, etc., but nothing worked until I stripped it back to exactly what is presented in the Autofac docs.

Related

EF Core error: The connection does not support MultipleActiveResultSets

I search thru all the links which had solution for the error. But, none of them were applicable for me as I had asynchronous code already and doing everything they have suggested.
We have Azure Functions based on .NET Core 3.1. We use latest version of Entity Framework Core. We are intermittently getting this error:
System.InvalidOperationException: The connection does not support MultipleActiveResultSets.
at Microsoft.Data.SqlClient.SqlCommand.<>c.b__164_0(Task1 result) at System.Threading.Tasks.ContinuationResultTaskFromResultTask2.InnerInvoke()
at System.Threading.Tasks.Task.<>c.<.cctor>b__274_0(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
End of stack trace from previous location where exception was thrown
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
End of stack trace from previous location where exception was thrown ---
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.AsyncEnumerator.InitializeReaderAsync(DbContext _, Boolean result, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func4 operation, Func4 verifySucceeded, TState state, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func4 operation, Func4 verifySucceeded, TState state, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.AsyncEnumerator.MoveNextAsync()
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable1 source, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable1 source, CancellationToken cancellationToken)
When we looked at the logs in AppInsights, we found that the exceptions occurred at the same time with exact same error for same function at the same place. But, it was for three different invocations (InvocationId) but same host instance (HostInstanceId) and different operation Id (Operation ID). Expectation is that for every new invocation, new dbContext will be instantiated as the AddDbContextPool adds scoped dbContext as dependency by default. Not sure if we can deduct anything out of it.
Below is our implementation approach. Appreciate any help on this. Thanking in advance.
We add DbContext to the services using following statement in the startup file:
builder.Services.AddDbContextPool<OurDbContext>(options =>
{
options.UseSqlServer("connectionstring"), builder =>
{
builder.EnableRetryOnFailure(3, TimeSpan.FromSeconds(2), null);
});
});
OurDbContext class has the following constructor:
public OurDbContext(DbContextOptions<OurDbContext> options)
: base(options)
{
}
And then we inject OurDbContext class in different repositories which uses this context to talk to SQL. Similar to below:
public class TypesRepo : RepoBase<Types>, ITypesRepo
{
public TypesRepo(OurDbContext ourDbContext) : base(ourDbContext)
{
}
public async Task RetrieveTypesAsync(List<string> types)
{
var records = await RetrieveConditionAsync(x => types.Contains(x.Type));
return records?.Select(x => new { x.Type, x.TypeId })
.ToDictionary(x => x.Type, x => x.TypeId);
}
}
public abstract class RepoBase<T> where T : class
{
protected OurDbContext OurDbContext { get; set; }
public RepoBase(OurDbContext OurDbContext)
{
this.OurDbContext = OurDbContext;
}
public async Task<List<T>> RetrieveConditionAsync(Expression<Func<T, bool>> expression)
{
return await OurDbContext.Set<T>().Where(expression).AsNoTracking().ToListAsync();
}
}
We inject above Repo class in Function classes and call above methods such as
await _typesRepo.RetrieveAsync()
P.S.: Based on below comment
I think dbcontextpool will reuse the dbcontext instance if it the connection is not active/unused but not the one which is active.
AddDbContext or AddDbContextPool
Your connection string needs to specify MultipleActiveRecordSets=true; to tell SqlServer to make sure this feature is enabled on the connections you make to it.
For example:
Data Source=localhost;Initial Catalog=master;Integrated Security=True;MultipleActiveResultSets=True
More info here: https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql/enabling-multiple-active-result-sets
We recently ran into a similar issue, and it was due to having a request spin up multiple threads simultaneously, and each thread was using the same DbContext to parallelize a heavy database query.
Further, we use DbReader for working with the results, and that locks up the connection until the reader is disposed of.
That prevents the other threads, which are re-using the same DbContext / connection instance, which also are using DbReader to process results.
Enabling the multiple-active result sets feature solves this for us, and in our case, we actually do not notice a performance hit at all with this.

Task-like objects support in Kestrel

I am trying to return task-like object from the controller action:
[HttpGet("test")]
public async FuncAwaitable<string> GetAsync() => "OK";
It works in the VS2019 wizard generated ASP.NET Core solution but fails with Kestrel:
The problem is somewhere in middleware:
ArgumentException
System.ArgumentException: 'this' type cannot be an interface itself.
at System.RuntimeTypeHandle.VerifyInterfaceIsImplemented(RuntimeTypeHandle interfaceHandle)
at System.RuntimeType.GetInterfaceMap(Type ifaceType)
at System.Reflection.RuntimeReflectionExtensions.GetRuntimeInterfaceMap(TypeInfo typeInfo, Type interfaceType)
at Microsoft.Extensions.Internal.AwaitableInfo.IsTypeAwaitable(Type type, AwaitableInfo& awaitableInfo)
at Microsoft.Extensions.Internal.CoercedAwaitableInfo.IsTypeAwaitable(Type type, CoercedAwaitableInfo& info)
at Microsoft.Extensions.Internal.ObjectMethodExecutor..ctor(MethodInfo methodInfo, TypeInfo targetTypeInfo, Object[] parameterDefaultValues)
at Microsoft.Extensions.Internal.ObjectMethodExecutor.Create(MethodInfo methodInfo, TypeInfo targetTypeInfo, Object[] parameterDefaultValues)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvokerCache.GetCachedResult(ControllerContext controllerContext)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvokerProvider.OnProvidersExecuting(ActionInvokerProviderContext context)
at Microsoft.AspNetCore.Mvc.Internal.ActionInvokerFactory.CreateInvoker(ActionContext actionContext)
at Microsoft.AspNetCore.Mvc.Internal.MvcAttributeRouteHandler.<>c__DisplayClass12_0.<RouteAsync>b__0(HttpContext c)
at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
at Shared.AspNetCore.CorrelationHeader.<>c__DisplayClass0_0.<<UseCorrelation>b__0>d.MoveNext() in C:\proj\ah\shared\src\Shared.AspNetCore\AspNetCore\CorrelationHeader.cs:line 16
Any ideas?

"Value of ContainerControlledLifetimeManager can only be set once" when using an open-generic singleton registration

I'm using the Unity.Container NuGet package 5.11 and I want to register an open-generic type such that each concrete type is a singleton.
Background: I'm working in an old-school ASP.NET WebForms application and I've made a crude reimplementation of Microsoft.Extensions.Logging like so:
// `ILogger` is implemented elsewhere. Specifically I wrote an implementation using Serilog.
public interface ILogger
{
void Log( LogLevel level, String messageTemplate, params Object[] args );
}
// Unlike `ILogger`, this `ILogger<TSourceContext>` is not implemented by the code elsewhere (that uses Serilog, described above).
// Instead, it's only implemented by the internal class below:
public interface ILogger<TSourceContext> : ILogger
{
}
internal class LoggerWithSourceContext<TSourceContext> : ILogger<TSourceContext>
{
private readonly ILogger realLogger;
internal LoggerWithSourceContext( ILoggerFactory factory )
{
if( factory == null ) throw new ArgumentNullException(nameof(factory));
this.realLogger = factory.CreateLogger( sourceContext: typeof(TSourceContext).FullName );
}
public void Log( LogLevel level, String messageTemplate, params Object[] args )
{
this.realLogger.Log( level, messageTemplate, args );
}
}
// This is implemented elsewhere in my Serilog code.
public interface ILoggerFactory
{
ILogger CreateLogger( String sourceContext );
}
This is registered in my root Unity container like so:
IUnityContainer rootContainer = ...
rootContainer
.RegisterSingleton<ILoggerFactory,MySerilogLoggerFactory>()
.RegisterSingleton( from: typeof(ILogger<>), to: typeof(LoggerWithSourceContext<>);
When this is used, the first time an ILogger<T> is resolved it works fine - however the second time an ILogger<T> is resolved it fails with this error:
InvalidOperationException
ContainerControlledLifetimeManager can only be set once
I was able to work-around it by changing the registration from a Singleton to a Transient registration using RegisterType, but I'd like it to use a Singleton registration to mitigate the risks of a memory or resource leak:
IUnityContainer rootContainer = ...
rootContainer
.RegisterSingleton<ILoggerFactory,MySerilogLoggerFactory>()
.RegisterType( from: typeof(ILogger<>), to: typeof(LoggerWithSourceContext<>);
Full stack trace:
ContainerControlledLifetimeManager can only be set once
-2146233079
System.InvalidOperationException
at Unity.Lifetime.ContainerControlledLifetimeManager.<>c.<SetValue>b__7_0(Object o, ILifetimeContainer c) in C:\projects\unity\Abstractions\src\Lifetime\Managers\ContainerControlledLifetimeManager.cs:line 70
at Unity.Lifetime.ContainerControlledLifetimeManager.SetValue(Object newValue, ILifetimeContainer container) in C:\projects\unity\Abstractions\src\Lifetime\Managers\ContainerControlledLifetimeManager.cs:line 70
at Unity.Strategies.LifetimeStrategy.PostBuildUp(BuilderContext& context) in C:\projects\unity\Container\src\Strategies\LifetimeStrategy.cs:line 86
at Unity.UnityContainer.<>c.<.ctor>b__41_3(BuilderStrategy[] chain, BuilderContext& context) in C:\projects\unity\Container\src\UnityContainer.Resolution.cs:line 431
at Unity.Builder.BuilderContext.Resolve(Type type, String name, InternalRegistration registration) in C:\projects\unity\Container\src\Builder\Context\BuilderContext.cs:line 177
at Unity.Builder.BuilderContext.Resolve(Type type, String name) in C:\projects\unity\Container\src\Builder\Context\BuilderContext.cs:line 67
at Unity.Builder.BuilderContext.Resolve(ParameterInfo parameter, Object value) in C:\projects\unity\Container\src\Builder\Context\BuilderContext.cs:line 217
at Unity.Processors.ConstructorProcessor.<>c__DisplayClass16_0.<GetResolverDelegate>b__0(BuilderContext& c) in C:\projects\unity\Container\src\Processors\Constructor\ConstructorResolution.cs:line 77
at Unity.Processors.MemberProcessor`2.<>c__DisplayClass8_0.<GetResolver>b__0(BuilderContext& c) in C:\projects\unity\Container\src\Processors\Abstracts\MemberProcessor.cs:line 162
at Unity.Processors.MemberProcessor`2.<>c__DisplayClass8_0.<GetResolver>b__0(BuilderContext& c) in C:\projects\unity\Container\src\Processors\Abstracts\MemberProcessor.cs:line 162
at Unity.Processors.MemberProcessor`2.<>c__DisplayClass8_0.<GetResolver>b__0(BuilderContext& c) in C:\projects\unity\Container\src\Processors\Abstracts\MemberProcessor.cs:line 162
at Unity.Strategies.BuildPlanStrategy.PreBuildUp(BuilderContext& context) in C:\projects\unity\Container\src\Strategies\BuildPlanStrategy.cs:line 88
at Unity.UnityContainer.<>c.<.ctor>b__41_2(BuilderContext& context) in C:\projects\unity\Container\src\UnityContainer.Resolution.cs:line 363
at Unity.UnityContainer.Unity.IUnityContainer.Resolve(Type type, String name, ResolverOverride[] overrides) in C:\projects\unity\Container\src\UnityContainer.IUnityContainer.cs:line 244
Now in latest unity version 5.11.7 this problem fixed

Correctly using of dependency injected DbContext to query Database

I have set up my own context using (as I believe is correct):
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextPool<JobTrackingContext>(options => options.UseNpgsql(connection));
services.AddScoped<IJobRepository, JobRepository>();
}
Then I define my JobTrackingContext as follows:
public JobTrackingContext(DbContextOptions<JobTrackingContext> options)
: base(options)
{
public DbSet<Job> Jobs { get; set; }
}
Now I can define a repository to actually create/edit/delete Jobs:
public class JobRepository : GenericRepository<Job, long>, IJobRepository
{
private Job currentJob;
public JobRepository(JobTrackingContext jobTrackingContext, JobTrackingSettings settings)
: base(jobTrackingContext)
{
_settings = settings;
}
public async Task StartSync(JobType jobType, JobTriggerType jobTriggerType)
{
var tempJob = new Job(jobType, jobTriggerType);
await _dbContext.Jobs.AddAsync(tempJob);
await _dbContext.SaveChangesAsync();
}
}
And all this code gets instantiated by a Post-request to this API:
public async void Post()
{
_logger.LogDebug("Going to start account sync");
await _jobRepository.StartSync(JobType.ZRequestSync, JobTriggerType.Scheduled);
try
{
await _sync.StartAsync();
await _jobRepository.ChangeSyncStatus(JobStatusType.Finished);
}
catch (Exception e)
{
_logger.LogError(e, "Error occured during sync :(");
await _jobRepository.ChangeSyncStatus(JobStatusType.Failed);
}
}
Yet when I do this, I get an Exception with the message Reset() called on connector with state Connecting. I do not understand where this comes from.
When I do not use the injected version, but instead do this:
using (var c = new JobTrackingContext())
{
var job = new Job(jobType, jobTriggerType)
await c.Jobs.AddAsync(job);
await c.SaveChangesAsync();
}
All seems to be working fine. It seems that the context gets disposed too early. But how can I prevent this and/or what am I missing?
The Full Stacktrace:
System.ObjectDisposedException
HResult=0x80131622
Message=Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Source=Microsoft.EntityFrameworkCore
StackTrace:
at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
at Microsoft.EntityFrameworkCore.DbContext.<SaveChangesAsync>d__52.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter1.GetResult()
at ZShared.JobRepository.<StartSync>d__4.MoveNext() in C:\Users\richa\Documents\Codes\Company\Product\Shared\Folder\JobRepository.cs:line 38
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at ZAccountSyncService.AccountSyncController.<Post>d__4.MoveNext() in C:\Users\richa\Documents\Code\Company\Product\SubProduct\AccountSyncController.cs:line 32
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
The gist of the problem is the declaration of your AccountSyncController.Post method. You have async void Post() instead of async Task Post().
When there is no task, a request has nothing to await and ends before the call of method _sync.StartAsync() is completed. With an end of a request comes also the end of a lifetime scope. On lifetime scoped end, all instances with scoped lifetime get disposed. Thus, your context is disposed before you get to the call of SaveChanges method. And this is the cause of your exception.

SignalR notification using SqlScaleoutConfiguration gives random exception for Rebus.Transport.TransactionContext not serializable

I use:
SignalR 2.2.2 in SqlScaleoutConfiguration
Rebus 3.0.1
Some events stored in Rebus are handled by a notification hub and pushed to the clients using signalR.
Everything works fine, but this morning, after having published a new version, none of the clients received the "new version" message probably because of the following exception:
10:39:04.586| |ERROR| |ProcessId=8196| |ThreadId=5| |SignalR.SqlMessageBus| |Stream 0 : Error starting SQL notification listener: System.Runtime.Serialization.SerializationException: Type 'Rebus.Transport.TransactionContext' in Assembly 'Rebus, Version=3.0.1.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.
Server stack trace:
at System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context)
at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()
at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo)
at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
at System.Runtime.Remoting.Channels.CrossAppDomainSerializer.SerializeMessageParts(ArrayList argsToSerialize)
at System.Runtime.Remoting.Messaging.SmuggledMethodCallMessage..ctor(IMethodCallMessage mcm)
at System.Runtime.Remoting.Messaging.SmuggledMethodCallMessage.SmuggleIfPossible(IMessage msg)
at System.Runtime.Remoting.Channels.CrossAppDomainSink.SyncProcessMessage(IMessage reqMsg)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at System._AppDomain.CreateInstance(String assemblyName, String typeName)
at System.Data.SqlClient.SqlDependency.CreateProcessDispatcher(_AppDomain masterDomain)
at System.Data.SqlClient.SqlDependency.ObtainProcessDispatcher()
at System.Data.SqlClient.SqlDependency.Start(String connectionString, String queue, Boolean useDefaults)
at Microsoft.AspNet.SignalR.SqlServer.ObservableDbOperation.StartSqlDependencyListener()
The message in Rebus queue results as correctly handled.
The handler is this:
public async Task Handle(ApplicationVersionEvent message)
{
await
Clients.All.CheckApplicationVersion(new ApplicationCurrentVersionNotification
{
CurrentVersion = message.CurrentVersion
});
}
It was resolved by a restart, but I need to understand what happened.
I similar issues are:
https://github.com/SignalR/SignalR/issues/3401
https://github.com/SignalR/SignalR/issues/3404
SQL Query Notifications do not always work in scaleout setup (SQL Server)
https://github.com/rebus-org/Rebus/issues/493
Rebus, exception when creating AppDomain / Instance from async Handler
but I think this is not the same case.
It is really hard for me to tell you what's going on here besides what you have already discovered: SignalR for some weird reason seems to want to serialize the values stashed in the current execution context, and one of those values is Rebus' current transaction context.
As explained in the links you included, Rebus stores an "ambient transaction" this way when handling a message, allowing all of its own operations to be enlisted in the same unit of work.
You could use the approach explained here, where the transaction context is temporarily removed in a safe way like this
public async Task Handle(SomeMessage message)
{
var transactionContext = AmbientTransactionContext.Current;
AmbientTransactionContext.Current = null;
try
{
JuggleWithAppDomainsInHere();
}
finally
{
AmbientTransactionContext.Current = transactionContext;
}
}
possibly moving relevant bits to the constructor/Dispose method respectively in a class that implements IDisposable, making for a smoother API:
using(new DismantleAmbientRebusStuff())
{
JuggleWithAppDomainsInHere();
}
I think someone who knows a lot about SignalR would need to chime in if we were to find out what really happened.
I forgot this issue, but I solved it by a workaround a little later on.
The clue is that SqlMessageBus serializes the context when it is initialized and this happens the first time it is retrieved invoking GetHubContext, so I forced its initialization before executing any command.
app.MapSignalR();
var context = new OwinContext(app.Properties);
var token = context.Get<CancellationToken>("host.OnAppDisposing");
if (token != CancellationToken.None)
{
token.Register(() =>
{
log.Info("host.OnAppDisposing");
// code to run when server shuts down
BackendMessageBusConfig.DisposeContainers();
});
}
// this code brings forward SignalR SqlMessageBus initialization
var forceInit = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
BackendMessageBusConfig.Register();

Resources