3
votes

I added a timer in a Blazor page, so some data (pulled from database) can refresh every 5 seconds. It works fine when I'm on the page, but when I move to a different page and come back to the original one, I get this error:

"This connection was used with an ambient transaction. The original ambient transaction needs to be completed before this connection can be used outside of it" I've included the stack trace at the end

I've tried to dispose the Timer when the page disposes, but it didn't solve it. It looks like this issue is due to how the DB context is registered in the Dependency Injection container, but

Here is the C# code of my page. It calls the main service, who in turns gets data through a repository.

@functions {

    protected Dictionary<int, int> RunningTaskProcessQueueCountDictionary;
    protected Dictionary<int, int> PendingTaskProcessQueueCountDictionary;
    protected Timer CountRefreshTimer = null;

    protected override async Task OnInitAsync()
    {
        CountRefreshTimer = new Timer(new TimerCallback(async _ =>
        {
            await RefreshCount();
            await base.Invoke(StateHasChanged);
        }), null, 0, 5000);
    }


    private async Task RefreshCount()
    {
        RunningTaskProcessQueueCountDictionary = await mainService.GetRunningTaskProcessQueueCountByTaskAppAsync(null);
        PendingTaskProcessQueueCountDictionary = await mainService.GetPendingTaskProcessQueueCountByTaskAppAsync(null);
    }

    public void Dispose()
    {
        if (CountRefreshTimer != null)
        {
            CountRefreshTimer.Dispose();
        }
    }
}

Here is an extract of the startup.cs where DB context is registered:

// Register the DB Context
var connection = Configuration.GetConnectionString("SQL01.xxx");
services.AddDbContext<SQL01xxxContext>(options => options.UseSqlServer(connection));

// Register all repositories and services (using Scrutor)
services.Scan(scan =>
    scan.FromAssemblies(typeof(IMainService).Assembly, typeof(ITaskAppRepository).Assembly)
        .AddClasses()
        .AsMatchingInterface()
        .WithScopedLifetime());

services.AddScoped<DbContext, SQL01xxxContext>();
services.AddScoped<IUnitOfWork<SQL01xxxContext>, UnitOfWork<SQL01xxxContext>>();

This is the stacktrace of the error

System.InvalidOperationException HResult=0x80131509 Message=This connection was used with an ambient transaction. The original ambient transaction needs to be completed before this connection can be used outside of it. Source=Microsoft.EntityFrameworkCore.Relational
StackTrace: at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.HandleAmbientTransactions() at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.d__42.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable1.AsyncEnumerator.d__12.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.<ExecuteAsync>d__72.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable1.AsyncEnumerator.d__11.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at System.Linq.AsyncEnumerable.SelectEnumerableAsyncIterator2.d__7.MoveNext() in D:\a\1\s\Ix.NET\Source\System.Interactive.Async\Select.cs:line 106 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at System.Linq.AsyncEnumerable.AsyncIterator1.d__10.MoveNext() in D:\a\1\s\Ix.NET\Source\System.Interactive.Async\AsyncIterator.cs:line 112 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor1.EnumeratorExceptionInterceptor.d__5.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Linq.AsyncEnumerable.d__63.MoveNext() in D:\a\1\s\Ix.NET\Source\System.Interactive.Async\Aggregate.cs:line 120 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at SIQC.Enterprise.GenericRepository.Common.IQueryableExtensions.d__11.MoveNext() in C:\TFS\[...]\IQueryableExtensions.cs:line 28 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at SIQC.Enterprise.GenericRepository.RepositoryBase.ReadOnlyRepository1.<GetAsync>d__12.MoveNext() in C:\TFS\[...]\ReadOnlyRepository.cs:line 174 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at ToolsWebManagementData.IntravexV21.Repository.TaskProcessQueueRepository.d__2.MoveNext() in C:\TFS[...]\TaskProcessQueueRepository.cs:line 46 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at TWMBlazorSSB.Services.MainService.<GetRunningTaskProcessQueueCountByTaskAppAsync>d__21.MoveNext() in C:\TFS\[...]\MainService.cs:line 79 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at TWMBlazorSSB.Pages.TaskApp.TaskApps.d__7.MoveNext() in C:\TFS[...]\TaskApps.razor:line 91 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at TWMBlazorSSB.Pages.TaskApp.TaskApps.<b__6_0>d.MoveNext() in C:\TFS[...]\TaskApps.razor:line 79

Any help would be appreciated. Thanks

1

1 Answers

1
votes

It looks like this issue is due to how the DB context is registered

That is correct. This is because AddDbContext<> uses (as default) ServiceLifetime.Scoped. But there is no useful Scope.

This is still under discussion by the Blazor team, maybe wait for the next preview before considering a workaround.

But I see it is now bumped to preview-9.