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.
Related
InvalidOperationException: A second operation was started on this context before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
public async Task<List<SiteBanner>> GetSiteAllBanners()
{
return await _siteBannerRepository.GetQuery().ToListAsync();
}
public IQueryable<TEntity> GetQuery()
{
return _DbSet.AsQueryable();
}
services.AddScoped<ISiteService, SiteService>();`
[HttpGet("show-banners")]
public async Task<IActionResult> ShowBanners()
{
ViewBag.banners = await _siteService.GetSiteAllBanners();
return View();
}
enter image description here
I have two application e.g) A, B
A has a Saga
B is just web application
A sent Command messages to B and
B sent exception for that Command to A's Saga and A's Saga received well
and B have a #ExceptionHandler which I hope to be invoked but it's not working
How can I make them be invoked?
EDIT
this is A application's Saga that sends command messages to B application
and deals with exception which B sent
#Saga
public class OrderSaga {
#Autowired
private transient CommandGateway commandGateway;
#StartSaga
#SagaEventHandler(associationProperty = "orderId")
public void handle(CreateOrderEvent evt) {
String paymentId = UUID.randomUUID().toString();
SagaLifecycle.associateWith("paymentId", paymentId);
commandGateway.send(new CreatedPaymentCommand(paymentId, evt.getUserId(),evt.getFoodPrice())).exceptionally(exp -> {
System.out.println("got it");
System.out.println(exp.getMessage());
return null;
});
}
}
this is B application that throws exception for test
#Aggregate
#NoArgsConstructor
public class PaymentAggregate {
#AggregateIdentifier
private String paymentId;
private String userId;
private PaymentStatus status;
#CommandHandler
public PaymentAggregate(CreatedPaymentCommand cmd) {
throw new IllegalStateException("this exception was came from payment aggregates");
// AggregateLifecycle.apply(new CreatedPaymentEvent(cmd.getPaymentId(),
// cmd.getUserId(),cmd.getMoney()));
}
#ExceptionHandler(resultType = IllegalStateException.class)
public void error(IllegalStateException exp) {
System.out.println(exp.getMessage());
}
// I want this #ExceptionHandler to be invoked
#EventSourcingHandler
public void on(CreatedPaymentEvent evt) {
this.paymentId = evt.getPaymentId();
this.userId = evt.getUserId();
}
}
A application catch exception well like below
2021-08-24 11:46:43.534 WARN 14244 --- [ault-executor-2] o.a.c.gateway.DefaultCommandGateway : Command 'com.common.cmd.CreatedPaymentCommand' resulted in org.axonframework.commandhandling.CommandExecutionException(this exception was came from payment aggregates)
got it
this exception was came from payment aggregates
but B is not I thought that B's #ExceptionHandler will catch that exception
in short, How can I make B's #ExceptionHandler to be invoked
It doesn't work right now because the exception is thrown from the constructor of your aggregate.
As you are using a constructor command handler, there is no instance present yet.
And without an instance, Axon Framework cannot spot the #ExceptionHandler annotated method you've set up.
This is the only missing point for the exception handlers at this stage. Honestly, the reference guide should be a bit more specific about this. I am sure this will change in the future, though.
There's a different approach for having a command handler that constructs the aggregate and that can use the #ExceptionHandler: with the #CreationPolicy annotation. The reference guide has this to say about it, by the way.
Thus, instead of having a constructor command handler, you would set up a regular command handler using the AggregateCreationPolicy.ALWAYS.
That would adjust your sample like so:
#Aggregate
#NoArgsConstructor
public class PaymentAggregate {
#AggregateIdentifier
private String paymentId;
private String userId;
private PaymentStatus status;
#CommandHandler
#CreationPolicy(AggregateCreationPolicy.ALWAYS)
public void handle(CreatedPaymentCommand cmd) {
throw new IllegalStateException("this exception was came from payment aggregates");
// AggregateLifecycle.apply(new CreatedPaymentEvent(cmd.getPaymentId(),
// cmd.getUserId(),cmd.getMoney()));
}
#ExceptionHandler(resultType = IllegalStateException.class)
public void error(IllegalStateException exp) {
System.out.println(exp.getMessage());
}
// I want this #ExceptionHandler to be invoked
#EventSourcingHandler
public void on(CreatedPaymentEvent evt) {
this.paymentId = evt.getPaymentId();
this.userId = evt.getUserId();
}
}
Please give this a try in your application, #YongD.
I have the following setup to test my Durable Function.
public class TestDurableFunction
{
private readonly ITestRepository _testRepository;
public TestDurableFunction(ITestRepository testRepository)
{
_testRepository = testRepository; // needed for later use
}
[FunctionName("TimerTrigger")]
public async Task Run([TimerTrigger("0 1 * * *", RunOnStartup = true)] TimerInfo myTimer, [DurableClient] IDurableClient starter, ILogger logger)
{
try
{
await starter.StartNewAsync("OrchestrateSavings", null);
}
catch (Exception exception)
{
logger.LogFunctionError(nameof(Run), nameof(TestDurableFunction), exception);
}
}
[FunctionName("OrchestrateTest")]
public async Task<int> OrchestrateTest([OrchestrationTrigger] IDurableOrchestrationContext context)
{
await Task.CompletedTask;
return 10;
}
}
When I start this function locally, then the TimerTrigger function starts with no problems. This function then calls my OrchestrateTest Durable Function.
But that immediately throws the following error in the Command Prompt window:
Microsoft.Azure.WebJobs.Script.WebHost: Unable to load metadata for
function 'OrchestrateTest'. Function 'OrchestrateTest (Orchestrator)'
was aborted. Reason: An internal error occurred while attempting to
execute this function. The execution will be aborted and retried.
Details: System.NullReferenceException: Object reference not set to an
instance of an object.
Anyone any idea what the issue could be?
As per your code the orchestrator function you are invoking is OrchestrateSavings; however there is not orchestrator function registered with that name. As per posted code the orchestrator function name is OrchestrateTest. So change the line to
await starter.StartNewAsync("OrchestrateTest", null);
in the Run method.
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'm experiencing problems with an exception not caught in my workflows.
I have a custom AsyncCodeActivity surrounded by a TryCatch; anyway, the exception is not caught and, worse, my IIS pool hosting the workflow is sometimes restarted.
Looking at this question/answer (Exception escapes from workflow despite TryCatch activity), I'm thinking that the problem is in the way I rethrow exceptions. This is how I usually write async code activities:
protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
{
Action<object> execute = s => this.Execute();
var task = new Task(execute, state, CancellationToken.None, TaskCreationOptions.PreferFairness);
task.ContinueWith(s => callback(s));
task.Start();
return task;
}
protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
{
var task = result as Task;
Contract.Assert(task != null);
if (task.IsFaulted && task.Exception != null)
{
Contract.Assert(
task.Exception.InnerException != null,
"It is expected that the inner exception in a task is not null");
const string Message = "An exception was thrown while doing something";
Logger.ErrorException(Message, task.Exception.InnerException);
throw new WorkflowApplicationException(Message, task.Exception.InnerException);
}
}
private void Execute()
{
// Do something here
}
Is it the correct way to handle exceptions in async code activities? if yes, how should I prevent my workflow from aborting (and sometimes restarting IIS)?
Thanks
I don't think WorkflowApplicationException was meant to be thrown by your code. I wouldn't be surprised if somewhere we are treating this exception differently and not invoking the TryCatch block because this exception (and it's subclasses) are typically thrown by the workflow runtime and there wouldn't be a point of catching them in an activity if the runtime is hosed.
You could try throwing a different exception to see if that makes a difference.