コントローラが破棄された後にDBに書き込む

asp.net-core-mvc entity-framework-core

質問

状況

私たちには、ユーザーが任意の数のEメールアドレスを送信して、他の(潜在的な)メンバーを友人として招待できるコントローラがあります。アドレスがデータベースに見つからない場合は、そのユーザーに電子メールメッセージを送信します。ユーザは、このプロセスが完了するのを待つ必要がないので、これを続行するには、これは非同期で行われます。

サーバが遅く応答したり、ダウンしたり、過負荷になったりすると、電子メールの送信に時間がかかることがあります。電子メール送信者は、電子メールサーバから受信したステータスに従ってデータベースを更新する必要があります。たとえば、アドレスが存在しない場合など、永続的な障害が発生した場合はフレンドリクエストを「エラー」状態に設定します。この目的のために、電子メールコンポーネントはSendImmediateAsync(From,To,Subject,Content,Callback,UserArg)関数を実装します。メッセージが配信された後(または失敗した場合)、配信状態に関する特定の引数を使用してコールバックが呼び出されます。

最終的にデリゲートを呼び出すと、DbContextオブジェクトが既に破棄されています(コントローラも存在しているため)。接続文字列を受け取るコンストラクタがないため、 new ApplicationDbContext()を使用して手動で新しいオブジェクトを作成できません。

質問

コントローラを廃棄した後は、どうすればデータベースに書き込むことができますか?自分でDbContextオブジェクトを手動で作成する方法をまだ理解していません。 ApplicationDbContext型のオブジェクトがControllerのコンストラクタに渡され、自分自身でインスタンス化することができますが、コンストラクタには(たとえば接続文字列などの)引数を指定することはできません。 SQL接続を手動で作成して手動でINSERT文をアセンブルすることを避け、すでに設定したエンティティモデルを使用する方が好きです。

コード

このコードは、可読性のエラーチェックを行わずに影響を受けるセグメントを示しています。

[Authorize]
public class MembersController : Controller
{
    private ApplicationDbContext _context;

    public MembersController(ApplicationDbContext context)
    {
        _context = context;
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Friends()
    {
        MailHandler.SendImmediateAsync(FROM,TO,SUBJECT,CONTENT,
            delegate (Guid G, object any)
            {
                //THIS IS NOT WORKING BECAUSE _context IS DISPOSED
                var ctx = _context;

                Guid Result = (Guid)any; //user supplied argument

                if (G != Guid.Empty)
                {
                    ctx.MailConfirmation.Add(new MailConfirmation()
                    {
                        EntryId = Result,
                        For = EntryFor.FriendRequest,
                        Id = G
                    });

                    if (G == MailHandler.ErrorGuid)
                    {
                        var frq = _context.FriendRequest.SingleOrDefault(m => m.Id == Result);
                        frq.Status = FriendStatus.Error;
                        ctx.Update(frq);
                    }
                    ctx.SaveChanges();
                }
            }, req.Id);
        //rendering view
    }
}

受け入れられた回答

まず、ASP.NET Coreの依存関係注入でEFコアを使用している場合、「.AddDbContext」で別途指定しない限り、各DbContextインスタンスはリクエストごとにスコープされます。これは、HTTPリクエストが完了した後でDbContextのインスタンスを再利用しようとするべきではないことを意味します。 https://docs.asp.net/en/latest/fundamentals/dependency-injection.html#service-lifetimes-and-registration-optionsご覧ください

一方、DbContextOptionsはシングルトンであり、リクエスト間で再利用することができます。

後でHTTPリクエストを閉じてアクションを実行する必要がある場合は、新しいDbContextスコープを作成して、そのライフタイムを管理する必要があります。

次に、DbContextの基本コンストラクタをオーバーロードし、DbContextOptionsを直接渡すことができます。 https://docs.efproject.net/en/latest/miscellaneous/configuring-dbcontext.htmlを参照してください。

一緒に、これは解決策のようなものです。

public class MembersController : Controller
{
    private DbContextOptions<ApplicationDbContext> _options;

    public MembersController(DbContextOptions<ApplicationDbContext> options)
    {
        _options = options;
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Friends()
    {
        MailHandler.SendImmediateAsync(FROM,TO,SUBJECT,CONTENT, CreateDelegate(_options) req.Id);
    }

    private static Action<Guid, object> CreateDelegate(DbContextOptions<ApplicationDbContext> options)
    {
        return (G, any) => 
        {
            using (var context = new ApplicationDbContext(options))
            {
                //do work
                context.SaveChanges();
            }
        };
    }
}

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base (options) { }

    // the rest of your stuff
}

これは、もちろん、あなたの "MailHandler"クラスがHTTP要求を処理するスレッドをブロックしないように、デリゲートを実行するために並行性を使用していることを前提としています。


人気のある回答

なぜ、あなたのSendImmediateAsyncにuserArgsとしてdbContextを渡すだけではありませんか?その後、dbContextは処理されず、コールバックを実行すると戻されます。それがうまくいくはずです。



Related

ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ