Asp.net core mvc 释放控制器后写入数据库 处境

Asp.net core mvc 释放控制器后写入数据库 处境,asp.net-core-mvc,entity-framework-core,Asp.net Core Mvc,Entity Framework Core,我们有一个控制器,用户可以提交任意数量的电子邮件地址,邀请其他(潜在)成员作为朋友。如果在数据库中找不到地址,我们将向该用户发送电子邮件。由于用户不必等待此过程完成以继续工作,因此这是异步完成的 如果服务器响应缓慢、停机或过载,发送电子邮件可能需要很长时间。电子邮件发送者应该根据从电子邮件服务器接收到的状态更新数据库,例如,当发生永久性故障时(例如,如果地址不存在),将好友请求设置为“错误”状态。为此,电子邮件组件实现函数SendImmediateAsync(From、To、Subject、Co

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

如果服务器响应缓慢、停机或过载,发送电子邮件可能需要很长时间。电子邮件发送者应该根据从电子邮件服务器接收到的状态更新数据库,例如,当发生永久性故障时(例如,如果地址不存在),将好友请求设置为“错误”状态。为此,电子邮件组件实现函数
SendImmediateAsync(From、To、Subject、Content、Callback、UserArg)
。消息传递(或失败)后,将使用有关传递状态的某些参数调用回调

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

问题: 如何在控制器被释放后很长时间内写入数据库?我还没有弄清楚如何为自己手动创建
DbContext
对象。类型为
ApplicationDbContext
的对象被传递给控制器的构造函数,我希望自己可以实例化一个,但是构造函数没有我可以提供的参数(例如连接字符串)。我希望避免手动创建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
    }
}

为什么不将dbContext作为userArgs传递给SendImmediateAsync呢?然后,dbContext将不会被释放,并且可以在执行回调时传回。我很确定这会起作用。

为什么不将dbContext作为userArgs传递给SendImmediateAsync呢?然后,dbContext将不会被释放,并且可以在执行回调时传回。我很确定这应该是可行的。

首先,当您在ASP.NET Core的依赖项注入中使用EF Core时,每个DbContext实例的作用域都是每个请求,除非您在“.AddDbContext”中另有指定。这意味着您不应该在HTTP请求完成后尝试重新使用DbContext实例。看

另一方面,DbContextOptions是单例的,可以跨请求重用

如果需要关闭HTTP请求并在之后执行操作,则需要创建一个新的DbContext作用域来管理它的生存期

其次,可以重载DbContext的基本构造函数并直接传入DbContextOptions。看

总之,这就是解决方案可能的样子

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
}
公共类成员控制器:控制器
{
私有DbContextOptions_选项;
公共成员控制器(DbContextOptions选项)
{
_选项=选项;
}
[HttpPost]
[ValidateAntiForgeryToken]
公共IActionResult Friends()
{
SendImmediateAsync(FROM、TO、SUBJECT、CONTENT、CreateDelegate(_options)req.Id);
}
私有静态操作CreateDelegate(DbContextOptions选项)
{
返回(G,任意)=>
{
使用(var context=newapplicationdbcontext(选项))
{
//工作
SaveChanges();
}
};
}
}
公共类ApplicationDbContext:DbContext
{
公共应用程序DBContext(DbContextOptions选项):基本(选项){}
//你剩下的东西
}

当然,这假定您的“MailHandler”类正确地使用并发来运行委托,这样它就不会阻止处理HTTP请求的线程。

首先,当您将EF Core与ASP.NET Core的依赖项注入一起使用时,每个DbContext实例的作用域都是每个请求,除非您在中另有指定“.AddDbContext”。这意味着您不应在HTTP请求完成后尝试重新使用DbContext的实例。请参阅

另一方面,DbContextOptions是单例的,可以跨请求重用

如果需要关闭HTTP请求并在之后执行操作,则需要创建一个新的DbContext作用域来管理它的生存期

其次,可以重载DbContext的基本构造函数并直接传入DbContextOptions

总之,这就是解决方案可能的样子

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
}
公共类成员控制器:控制器
{
私有DbContextOptions_选项;
公共成员控制器(DbContextOptions选项)
{
_选项=选项;
}
[HttpPost]
[ValidateAntiForgeryToken]
公共IActionResult Friends()
{
SendImmediateAsync(FROM、TO、SUBJECT、CONTENT、CreateDelegate(_options)req.Id);
}
私有静态操作CreateDelegate(DbContextOptions选项)
{
返回(G,任意)=>
{
使用(var context=newapplicationdbcontext(选项))
{
//工作
SaveChanges();
}
};
}
}
公共类ApplicationDbContext:DbContext
{
公共应用程序DBContext(DbContextOptions选项):基本(选项){}
//你剩下的东西
}

当然,这假设您的“邮件处理程序”“类正确地使用并发性来运行委托,因此它不会阻止处理HTTP请求的线程。

我尝试了,但没有成功。我认为实体框架明确地用控制器处理它。哇,这很有趣。在控制器内部,可以创建新的dbContext吗