I use MassTransit in a .NET core web application (web api) to use SQS. It was working fine for publishing messages. But after I tried to add a consumer, I ran into an issue.
Here is my code
public static void UseMassTransit(this IServiceCollection services, MassTransitConfiguration massTransitConfiguration)
{
services.AddMassTransit(x =>
{
x.AddConsumer<CustomerChangeConsumer>();
x.UsingAmazonSqs((context, cfg) =>
{
cfg.Host(massTransitConfiguration.Host, h =>
{
h.AccessKey(massTransitConfiguration.AccessKey);
h.SecretKey(massTransitConfiguration.SecretKey);
cfg.ReceiveEndpoint("CustomerChangeConsumer",
configurator =>
{
configurator.ConfigureConsumer<CustomerChangeConsumer>(context);
});
// scope topics as well
h.EnableScopedTopics();
});
});
});
services.AddMassTransitHostedService();
}
The problems is that ReceiveEndpoint gives a NullReferenceException.
This is strange as ConfigureConsumer is executed.However, ReceiveEndpoint just cannot fully finish without throwing this exception.
Here is the stacktrace:
MassTransit.AmazonSqsTransport.Configurators.ConfigurationHostSettings.FormatHostAddress()
System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
System.Lazy`1.CreateValue()
System.Lazy`1.Value
MassTransit.AmazonSqsTransport.Configurators.ConfigurationHostSettings.HostAddress
MassTransit.AmazonSqsTransport.Configuration.AmazonSqsHostConfiguration.HostAddress
MassTransit.AmazonSqsTransport.Configuration.AmazonSqsReceiveEndpointConfiguration.FormatInputAddress()
System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
System.Lazy`1.CreateValue()
System.Lazy`1.Value
MassTransit.AmazonSqsTransport.Configuration.AmazonSqsReceiveEndpointConfiguration.InputAddress
MassTransit.Monitoring.Health.EndpointHealth.EndpointConfigured(T configurator)
MassTransit.Monitoring.Health.BusHealth.MassTransit.EndpointConfigurators.IEndpointConfigurationObserver.EndpointConfigured[T](T configurator)
MassTransit.EndpointConfigurators.EndpointConfigurationObservable.<>c__DisplayClass0_0`1.<EndpointConfigured>b__0(IEndpointConfigurationObserver observer)
GreenPipes.Util.Connectable`1.All(Func`2 callback)
MassTransit.EndpointConfigurators.EndpointConfigurationObservable.EndpointConfigured(T configurator)
MassTransit.AmazonSqsTransport.Configuration.AmazonSqsHostConfiguration.CreateReceiveEndpointConfiguration(QueueReceiveSettings settings, IAmazonSqsEndpointConfiguration endpointConfiguration, Action`1 configure)
MassTransit.AmazonSqsTransport.Configuration.AmazonSqsHostConfiguration.CreateReceiveEndpointConfiguration(String queueName, Action`1 configure)
MassTransit.AmazonSqsTransport.Configuration.AmazonSqsHostConfiguration.ReceiveEndpoint(String queueName, Action`1 configureEndpoint)
MassTransit.AmazonSqsTransport.Configurators.AmazonSqsBusFactoryConfigurator.ReceiveEndpoint(String queueName, Action`1 configureEndpoint)
Kinley.SMPD.CustomerService.API.Extensions.MassTransitExtensions.<>c__DisplayClass0_1.<UseMassTransit>b__2(IAmazonSqsHostConfigurator h) in MassTransitExtensions.cs: line: 23
How come? And how to resolve?
You shouldn’t configure receive endpoints inside the host configuration closure. Try moving it outside the .Host() method and see if that resolves your issue.
Related
I'm trying to set up an GraphApi with Hot Chocolate in ASP.NET Core.
Now I want to split my application in multiple projects/components.
There's an Users component which has a UsersMutation containing 1 field:
public sealed class UsersMutation
{
public Task CreateUser([Service] ICreateUserMutationHandler handler, CreateUserParameters parameters)
=> handler.Handle(parameters);
}
I try to add it to the GraphQl schema like this:
public sealed class Mutation
{
public UsersMutation Users => new UsersMutation();
}
Configuration:
public static class GraphApiConfiguration
{
public static IServiceCollection AddGraphApi<TQuery, TMutation>(this IServiceCollection services)
where TQuery : class
where TMutation : class
{
services.AddGraphQLServer()
.AddQueryType<TQuery>()
.AddMutationType<TMutation>();
services.AddScoped<TQuery>();
services.AddScoped<TMutation>();
return services;
}
}
Finally in startup.cs:
services.AddGraphApi<Query, Mutation>();
But I get the following error trying to see the schema in the playground:
HotChocolate.SchemaException: For more details look at the `Errors` property.
1. The object type `UsersMutation` has to at least define one field in order to be valid. (HotChocolate.Types.ObjectType<ChocoGraph.Components.Users.GraphApi.UsersMutation>)
at HotChocolate.Configuration.TypeInitializer.Initialize(Func`1 schemaResolver, IReadOnlySchemaOptions options)
at HotChocolate.SchemaBuilder.Setup.InitializeTypes(SchemaBuilder builder, IDescriptorContext context, IReadOnlyList`1 types, LazySchema lazySchema)
at HotChocolate.SchemaBuilder.Setup.Create(SchemaBuilder builder, LazySchema lazySchema, IDescriptorContext context)
at HotChocolate.SchemaBuilder.Create(IDescriptorContext context)
at HotChocolate.SchemaBuilder.HotChocolate.ISchemaBuilder.Create(IDescriptorContext context)
at HotChocolate.Execution.RequestExecutorResolver.CreateSchemaAsync(NameString schemaName, RequestExecutorSetup options, RequestExecutorOptions executorOptions, IServiceProvider serviceProvider, TypeModuleChangeMonitor typeModuleChangeMonitor, CancellationToken cancellationToken)
at HotChocolate.Execution.RequestExecutorResolver.CreateSchemaServicesAsync(NameString schemaName, RequestExecutorSetup options, CancellationToken cancellationToken)
at HotChocolate.Execution.RequestExecutorResolver.GetRequestExecutorNoLockAsync(NameString schemaName, CancellationToken cancellationToken)
at HotChocolate.Execution.RequestExecutorResolver.GetRequestExecutorAsync(NameString schemaName, CancellationToken cancellationToken)
at HotChocolate.Execution.RequestExecutorProxy.GetRequestExecutorAsync(CancellationToken cancellationToken)
at HotChocolate.AspNetCore.HttpPostMiddleware.HandleRequestAsync(HttpContext context, AllowedContentType contentType)
at HotChocolate.AspNetCore.HttpPostMiddleware.InvokeAsync(HttpContext context)
at HotChocolate.AspNetCore.WebSocketSubscriptionMiddleware.InvokeAsync(HttpContext context)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
'iisexpress.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.10\System.Buffers.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
The program '[44280] iisexpress.exe: Program Trace' has exited with code 0 (0x0).
The program '[44280] iisexpress.exe' has exited with code -1 (0xffffffff).
What am I missing to achieve this? This seemed to work fine if I had the CreateUser field in the Mutation.cs file, but adding this extra step seems to break it.
I found the issue:
Mutation has to return something in order to be a valid field.
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.
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?
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();
The Description I had a legacy type that is HttpRequestScoped and a legacy web service consuming that service. To resolve services in legacy concerns, I have a global resolver. This was all working well in 1.4, and now that I'm using 2.1.12 I'm experiencing DependencyResolutionException.
The Code In 2.1.12, my Global.asax.cs:
builder.Register(c => new SomeLegacyType(HttpContext.Current)) // note: it relies on HttpContext.Current
.As<SomeLegacyType>()
.HttpRequestScoped();
_containerProvider = new ContainerProvider(builder.Build()); // this is my app's IContainerProvider
Setup.Resolver = new AutofacResolver(_containerProvider.ApplicationContainer);
Setup.Resolver is a singleton, and it is being set to AutofacResolver which looks something like this:
public class AutofacResolver : IResolver
{
private readonly IContainer _container;
public AutofacResolver(IContainer container)
{
_container = container;
}
public TService Get<TService>()
{
return _container.Resolve<TService>();
}
}
The web service looks like this:
[WebService]
public LegacyWebService : WebService
{
[WebMethod(EnableSession=true)]
public String SomeMethod()
{
var legacyType = Setup.Resolver.Get<SomeLegacyType>();
}
}
The Exception The following exception when Setup.Resolver.Get<SomeLegacyType>() is called:
Autofac.Core.DependencyResolutionException: No scope matching the expression 'value(Autofac.Builder.RegistrationBuilder`3+<>c__DisplayClass0[SomeAssembly.SomeLegacyType,Autofac.Builder.SimpleActivatorData,Autofac.Builder.SingleRegistrationStyle]).lifetimeScopeTag.Equals(scope.Tag)' is visible from the scope in which the instance was requested.
at Autofac.Core.Lifetime.MatchingScopeLifetime.FindScope(ISharingLifetimeScope mostNestedVisibleScope)
at Autofac.Core.Resolving.ComponentActivation..ctor(IComponentRegistration registration, IResolveOperation context, ISharingLifetimeScope mostNestedVisibleScope)
at Autofac.Core.Resolving.ResolveOperation.Resolve(ISharingLifetimeScope activationScope, IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Lifetime.LifetimeScope.Resolve(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Container.Resolve(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.TryResolve(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance)
at Autofac.ResolutionExtensions.Resolve(IComponentContext context, Service service, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.Resolve[TService](IComponentContext context, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.Resolve[TService](IComponentContext context)
Side Question Is there a better way to have properties injected in ASMX, the same way my ASPX pages are injected (rather than use Setup.Resolver)? I use the AttributedInjectionModule because of legacy concerns. It doesn't appear that the module works on ASMX.
If you configure your 'resolver' to use the RequestLifetime rather than ApplicationContainer all should work as expected.
This means your IContainer parameter will have to change to ILifetimeScope.
I'm not sure about a better way to inject ASMX dependencies, there may be one but I don't think Autofac supports it.