Используя код, который вы разместили в своем новом ответе. Но та же ошибка, когда я добавляю файл и вызывается метод UpdateVergadering
.
vergaderingРепозиторий:
private readonly IDbContextFactory<ApplicationDbContext> _factory;
public VergaderingRepository(IDbContextFactory<ApplicationDbContext> dbContextFactory, IDbContextFactory<ApplicationDbContext> factory)
{
_factory = factory;
}
public async ValueTask<int> UpdateVergadering(Vergadering vergadering)
{
using var dbContext = _factory.CreateDbContext();
dbContext.Set<Vergadering>().Update(vergadering);
return await dbContext.SaveChangesAsync();
}
public async ValueTask<Vergadering> GetVergaderingVoorLiveNotulenAsync (int vergaderingId)
{
using var dbContext = _factory.CreateDbContext();
dbContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
return await dbContext.Set<Vergadering>().SingleOrDefaultAsync(x => x.Id == vergaderingId);
}
Ошибка, которую я получаю:
System.InvalidOperationException: «Экземпляр объекта типа «Bestuurslid» не может быть отслежен, поскольку другой экземпляр с тем же значением ключа для {'Id'} уже отслеживается. При присоединении существующих сущностей убедитесь, что присоединен только один экземпляр сущности с заданным значением ключа.
Ваш код никогда не завершает рендеринг компонента, вы зацикливаетесь внутри OnInitialized
. Вы тоже путаете OnInitialized
и OnInitializedAsync
.
Вот демонстрационная страница, которая показывает, как использовать System.Timers.Timer
с обработчиком событий, подключенным к таймеру, для обработки получения данных и обновления пользовательского интерфейса. OnInitializedAsync
получает исходные данные, устанавливает таймер, подключает обработчик событий и завершает работу.
@page "/"
@implements IDisposable
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<div class = "alert alert-success">
@_message
</div>
@code {
private string? _message = "Not Set";
private System.Timers.Timer _timer = new System.Timers.Timer(2000);
protected async override Task OnInitializedAsync()
{
// Initial data get
_message = await GetData();
// set uo the timer and hook up the event handler
_timer.AutoReset = true;
_timer.Elapsed += this.OnTimerElapsed;
_timer.Start();
}
// Event handler for the timer
private async void OnTimerElapsed(object? sender, System.Timers.ElapsedEventArgs e)
{
_message = await GetData();
// must call this like this as the timer may be running on a different thread
await this.InvokeAsync(StateHasChanged);
}
private async ValueTask<string> GetData()
{
// emulate an async call to a Db or API
await Task.Delay(100);
return DateTime.Now.ToLongTimeString();
}
// Dispose of the event handler when the Renderer has finished with the component
public void Dispose()
=> _timer.Elapsed -= this.OnTimerElapsed;
}
Настройте DbContextFactory:
services.AddDbContextFactory<MyDbContext>(
options =>
options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test"));
А затем используйте фабрику, чтобы получить экземпляры контекста Db по мере необходимости.
public sealed class MeetingBroker
{
private readonly IDbContextFactory<MyDbContext> _factory;
public MeetingBroker(IDbContextFactory<MyDbContext> factory)
{
_factory = factory;
}
public ValueTask<Vergadering> GetVergaderingByIdAsync(int vergaderingId)
{
using var dbContext = _factory.CreateDbContext();
// if you aren't editing the data then you don't need tracking. Imporves performance
dbContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
return await dbContext.Set<TRecord>().SingleOrDefaultAsync(x => x.Id == vergaderingId));
}
Вы реализовали фабрику, но не шаблон "Единица работы". Ваша реализация использует один и тот же контекст для всех действий в репозитории и вызовет конфликты использования.
Blazor живет в асинхронном мире, поэтому вам нужно кодировать для ситуаций, когда у вас есть параллельные процессы, работающие на одних и тех же ресурсах.
Ваш шаблон репозитория должен выглядеть так:
private readonly IDbContextFactory<ApplicationDbContext> _factoy;
public VergaderingRepository(IDbContextFactory<ApplicationDbContext> dbContextFactory)
{
// assigned the factory not a context
_factory = dbContextFactory;
}
public async ValueTask<Vergadering> GetVergaderingVoorLiveNotulenAsync (int vergaderingId)
{
// creates a context for each transaction
using dbContext = dbContextFactory.CreateDbContext();
return await dbContext.Set<Vergadering>().SingleOrDefaultAsync(x => x.Id == vergaderingId);
}
@Shaun Я обновил свой вопрос вашим ответом, но он не работает
Поставьте точку останова на OnTimerElapsed
и проверьте объект Meeting
, который вы получаете из GetData
. Также что такое vergaderingBL.GetVergaderingById(VergaderingId)
, вызов API или вызов конвейера данных в БД? Это асинхронно?
@Shaun Я обновил свой вопрос выше, указав запрошенный вами код.
@Shaun Я поставил точку останова на OnTimerElapsed
и получил следующее сообщение об ошибке System.InvalidOperationException: 'A second operation was started on this context instance 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.'
См. — learn.microsoft.com/en-us/ef/core/dbcontext-configuration/… . Я обновил свой ответ.
@Shaun получает ошибку при вызове базы данных System.InvalidOperationException: 'A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext.
@Shaun Я обновил свой код в вопросе
Вы внедрили DbContextFactory?
@Шон Да, есть. Я добавил это к своему вопросу.
Вы реализовали фабрику, но не шаблон "Единица работы". Ваша реализация использует один и тот же контекст для всех действий в репозитории и вызовет конфликты использования. Blazor живет в асинхронном мире, поэтому вам нужно кодировать параллельные процессы, работающие на одних и тех же ресурсах.
@Shaun Я обновил свой вопрос (код репозитория). На той же странице, где показано оперативное обновление, вы можете загрузить файл. Таким образом, одновременно с EFC вызывается запрос на обновление. Но я получаю сообщение об ошибке: System.InvalidOperationException: 'The instance of entity type 'Vergadering' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.'
@Шон Как это возможно? Я использую NoTracking в методе LiveAsync?
ПОСМОТРИТЕ код вашего репозитория. Какие DbContext
используют оба метода? Не создавайте контекст уровня класса - _db = dbContextFactory.CreateDbContext();
. Точно скопируйте мой GetVergaderingByIdAsync
.
@Shaun Я обновил свой вопрос вашим кодом в моем репозитории. Все та же ошибка
Смотрите ответ ниже.
private readonly IDbContextFactory<ApplicationDbContext> _factory;
public VergaderingRepository(IDbContextFactory<ApplicationDbContext> dbContextFactory, IDbContextFactory<ApplicationDbContext> factory)
=> _factory = factory;
public async ValueTask<Vergadering> GetVergaderingVoorLiveNotulenAsync (int vergaderingId)
{
using var dbContext = _factory.CreateDbContext();
// Turning off tracking as this is only a query.
dbContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
return await dbContext.Set<Vergadering>().SingleOrDefaultAsync(x => x.Id == vergaderingId);
}
public async ValueTask<int> UpdateVergadering(Vergadering vergadering)
{
using var dbContext = _factory.CreateDbContext();
// Tracking is required for updates
//dbContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
dbContext.Set<Vergadering>().Update(vergadering);
return await dbContext.SaveChangesAsync();
}
@Шон, нет, не работает. System.InvalidOperationException: 'The instance of entity type 'Bestuurslid' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
Итак, что такое Bestuurslid
?
@Shaun Столбец в таблице Vergaderingen
. public ICollection<Bestuurslid> Aanwezigen { get; set; }
У вас есть проблемы в вашей модели данных EF, которые вам нужно отследить. Я не эксперт по EF.
Не меняйте вопрос, это делает недействительными ответы. Разместите продолжение.