处置控制器后写入数据库 [英] Write to DB after Controller has been disposed

查看:38
本文介绍了处置控制器后写入数据库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有一个控制器,用户可以在其中提交任意数量的电子邮件地址,以邀请其他(潜在)成员成为朋友.如果在数据库中找不到地址,我们将向该用户发送一封电子邮件.由于用户不必等待此过程完成即可继续工作,因此此操作是异步进行的.

We have a controller where users can submit any number of E-Mail addresses to invite other (potential) members as friends. If an address is not found in the database, we send an E-Mail message to that user. Since the user does not has to wait for this process to complete in order to continue working this is done asynchronously.

如果服务器响应缓慢,出现故障或过载,则发送电子邮件可能会花费很长时间.电子邮件发送者应该根据从电子邮件服务器接收到的状态来更新数据库,从而例如将好友请求设置为错误".状态,例如发生永久性故障(例如,该地址不存在).为此,电子邮件组件实现了 SendImmediateAsync(From,To,Subject,Content,Callback,UserArg)函数.在邮件已传递(或失败)之后,将使用有关传递状态的某些参数来调用回调.

Sending E-Mails can take a long time if servers respond slowly, are down or overloaded. The E-Mail sender should update the database according to the status received from the E-Mail server, so for example setting the friend request into "Error" state, when a permanent failure occurs, for example if the address does not exists. For this purpose, the E-Mail component implements the function SendImmediateAsync(From,To,Subject,Content,Callback,UserArg). After the message has been delivered (or it failed), the Callback is called with certain arguments about the Delivery state.

当它最终调用委托时,DbContext对象已经被释放(因为控制器也已经放置),并且由于没有构造函数,所以我无法使用 new ApplicationDbContext()手动创建一个新对象接受连接字符串.

When it eventually calls the delegate, the DbContext object has already been disposed (since the controller has been too) and I cannot manually create a new one using new ApplicationDbContext() because there is no constructor that accepts a connection string.

在处置完控制器后,如何写数据库?我尚未想出如何为自己手动创建 DbContext 对象.类型为 ApplicationDbContext 的对象传递给Controller的构造函数,我希望自己可以实例化一个实例,但是Constructor没有可以提供的参数(例如,连接字符串).我想避免手动创建SQL连接并手动组装INSERT语句,而是希望使用我们已经设置的实体模型.

How can I write to the database long after the controller has been disposed? I have not yet figured out how to manually create a DbContext object for myself. An object of type ApplicationDbContext is passed to the constructor of the Controller and I hoped I could instantiate one for myself, but the Constructor has no arguments I can supply (for example connection string). I want to avoid to manually create an SQL Connection and assemble INSERT statements manually and would prefer to work with the entity model we have already set up.

代码仅显示受影响的段,而没有任何错误检查可读性.

The code shows the affected segment only without any error checking for readability.

[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
    }
}

推荐答案

首先,当您将EF Core与ASP.NET Core的依赖项注入一起使用时,每个DbContext实例都按请求限定作用域,除非您在".AddDbContext".这意味着在该HTTP请求完成之后,您不应尝试重用DbContext的实例.请参见 https://docs.asp.net/zh-CN/latest/fundamentals/dependency-injection.html#service-lifetimes-and-registration-options

First of all, when you are using EF Core with ASP.NET Core's dependency injection, each DbContext instance is scoped per-request, unless you have specified otherwise in ".AddDbContext". This means you should not attempt to re-use an instance of DbContext after that HTTP request has completed. See https://docs.asp.net/en/latest/fundamentals/dependency-injection.html#service-lifetimes-and-registration-options

DbContextOptions是单例,可以在请求中重复使用.

DbContextOptions, on the other hand, are singletons and can be re-used across requests.

如果您需要关闭HTTP请求并随后执行操作,则需要创建一个新的DbContext范围以管理其生命周期.

If you need to close the HTTP request and perform an action afterwards, you'll need to create a new DbContext scope an manage it's lifetime.

第二,您可以重载DbContext的基本构造函数并直接传递DbContextOptions.请参见 https://docs.efproject.net/en/latest/miscellaneous/configuration-dbcontext.html

Second of all, you can overload DbContext's base constructor and pass in DbContextOptions directly. See https://docs.efproject.net/en/latest/miscellaneous/configuring-dbcontext.html

这就是解决方案的样子.

Together, this is what a solution might look like.

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请求的线程.

This assumes, of course, that your "MailHandler" class is properly using concurrency to run the delegate so it doesn't block the thread processing the HTTP request.

这篇关于处置控制器后写入数据库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆