サーバー側のBlazorアプリを作成しています。次のコードはStartup.cs
にあります。
services.AddDbContext<MyContext>(o => o.UseSqlServer(Configuration.GetConnectionString("MyContext")), ServiceLifetime.Transient);
services.AddTransient<MyViewModel, MyViewModel>();
そしてViewModelで:
public class MyViewModel : INotifyPropertyChanged
{
public MyViewModel(MyContext myContext)
{
_myContext = myContext;
}
public async Task<IEnumerable<Dto>> GetList(string s)
{
return await _myContext.Table1.where(....)....ToListAsync();
}
そしてかみそりファイルで。
@inject ViewModels.MyViewModel VM
<input id="search" type="text" @bind="search" />
<input id="search" type="button" value="Go" @onclick="SearchChanged" />
@code {
string search = "";
int currentCount = 0;
async void SearchChanged() {
currentCount++;
dtos = GetList(search);
}
}
ただし、検索ボタンをクリックすると以下のエラーが発生する場合がありますか?
System.InvalidOperationException: '前の操作が完了する前に、このコンテキストで2番目の操作が開始されました。これは通常、DbContextの同じインスタンスを使用する異なるスレッドが原因です。 DbContextでのスレッドの問題を回避する方法の詳細については、 https://go.Microsoft.com/fwlink/?linkid=209791 を参照してください。 '
私は問題を解決しましたが、今では複数のdbContexがあるため、作業単位が失われたと思います。
コンストラクタ:
private AppDbContext _db;
protected override void OnInitialized()
{
_db = new AppDbContext();
var query = _db.Set<Group>().AsQueryable();
}
後で私はそれを処分します:
public void Dispose()
{
_db?.Dispose();
}
エラーメッセージは、EFコンテキストが一度に複数の操作を実行できないという事実に関係しています。
私が理解しているのは、ページを表示している場合、SingalR接続を介して「サービス」ファイルに常時接続しているということです。
ページがサービスに対して複数の呼び出しを行う場合、前の操作が完了する前に、操作を実行するためにコンテキストが呼び出されている可能性があります。
サービスの存続期間中にコンテキストのインスタンスを1つ持つのではなく、呼び出しごとに1つのインスタンスを作成しました。この問題は軽減されるようですが、それが「ベストプラクティス」と見なされるかどうかはまだわかりません。
したがって、たとえば:
public class MyService
{
private MyContext Context => new MyContext(new DbContextOptions<MyContext>()));
private async Task DoSomething()
{
await using var context = this.Context; //New context for the lifetime of this method
var r = await context.Something
.Where(d => d....)
.AsNoTracking()
.FirstOrDefaultAsync()
.ConfigureAwait(false);
// context gets disposed of
// Other code
}
private async Task DoSomethingElse()
{
await using var context = this.Context; //New context for the lifetime of this method
var r = await context.Something
.Where(d => d....)
.AsNoTracking()
.FirstOrDefaultAsync()
.ConfigureAwait(false);
// context gets disposed of
// Other code
}
}