ASP.Net Core(MVC6)存储库模式意外处置

Dan*_*elC 9 c# asp.net asp.net-core-mvc asp.net-core

当我尝试添加评论时,出现以下错误:

ObjectDisposedException:无法访问已处置的对象.

当代码运行第二行时:

m_context.Comments.Add(comment);
m_context.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

为什么要处理上下文?如果将TryAddComment方法移动到控制器中,它不会提前调用Dispose.

这是我的Controller和Repository类的样子(简化).

CommentsController.cs:

public class CommentsController : Controller
{

    private ICommentRepository m_commentRepository;

    public CommentsController(ICommentRepository commentRepository)
    {
        m_commentRepository = commentRepository;
    }

    // POST: api/Comments
    [HttpPost]
    public async Task<IActionResult> PostComment([FromBody] CommentAddViewModel commentVM)
    {
        Comment comment = new Comment
        {
            ApplicationUserId = User.GetUserId(),
            PostId = commentVM.PostId,
            Text = commentVM.Text
        };

        bool didAdd = m_commentRepository.TryAddComment(comment);

        if (!didAdd)
        {
            return new HttpStatusCodeResult(StatusCodes.Status409Conflict);
        }

        return CreatedAtRoute("GetComment", new { id = comment.CommentId }, comment);
    }

}
Run Code Online (Sandbox Code Playgroud)

CommentRepository.cs:

public class CommentRepository : ICommentRepository, IDisposable
{

    public ApplicationDbContext m_context;

    public CommentRepository(ApplicationDbContext context)
    {
        m_context = context;
    }
    public bool TryAddComment(Comment comment)
    {
        m_context.Comments.Add(comment);
        m_context.SaveChanges();

        return true;
    }
    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                m_context.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:

如果我使用本地CommentRepository,它按预期工作.例如:

    CommentRepository localCommentRepo = new CommentRepository(m_context);
    bool didAdd = localCommentRepo.TryAddComment(comment);
Run Code Online (Sandbox Code Playgroud)

EDIT2:

在Startup.cs中,我将IcommentRepository注册为Scoped并按预期工作.最初是Singleton.为什么单身人士会导致这个问题?

services.AddSingleton<ICommentRepository, CommentRepository>(); //breaks
services.AddScoped<ICommentRepository, CommentRepository>(); //works
Run Code Online (Sandbox Code Playgroud)

EDIT3:

ApplicationDbContext.cs:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        // Customize the ASP.NET Identity model and override the defaults if needed.
        // For example, you can rename the ASP.NET Identity table names and more.
        // Add your customizations after calling base.OnModelCreating(builder);

    }
    public DbSet<Post> Posts { get; set; }
    public DbSet<Comment> Comments { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

Tse*_*eng 10

您的存储库和您的存储库都不DbContext应该是单身.注册它们的正确方法是,services.AddScoped或者services.AddTransient,它DbContext不应该比请求更长寿,而且AddScoped完全是为了这个.

AddScopedDbContext在作用域的生命周期内(在ASP.NET Core中等于请求的生命周期),将返回相同的a (和存储库,如果您将其注册)的实例.

使用时AddScope,不应自行处理上下文,因为解析存储库的下一个对象将具有已处置的上下文.

实体框架默认将上下文注册为作用域,因此您的存储库应该是作用域(与上下文和请求相同的生命周期)或瞬态(每个服务实例获取它自己的存储库实例,但请求中的所有存储库仍然共享相同上下文).

制作上下文单例会导致严重的问题,特别是对于内存(您使用的内容越多,上下文消耗的内存就越多,因为它必须跟踪更多记录).所以a DbContext应该尽可能短暂.

上下文的持续时间具有以下优点:如果出现问题,您仍可以在请求期间回滚所有操作并将其作为单个事务处理.