繁体   English   中英

当一个方法调用另一个方法并且都使用 DbContext 时处理 DbContext 的正确方法

[英]Proper way to dispose DbContext when one method calls another and both use DbContext

我是实体框架的新手,当一种方法调用另一种方法时,我对处理 DbContext 的正确方法感到困惑。 这些都是独立调用的,这就是 DeleteSheet 是分开的原因。 但是,DeleteChain 调用 DeleteSheet 以在删除父项(链)时删除子项。

我尝试在每种方法中进行处理,但是当我运行 DeleteChain 时,我得到:

The operation cannot be completed because the DbContext has been disposed.

db.Chains.Remove(chain);

我猜这是因为我在删除孩子时在 DeleteSheet 中处理了它。

以下是我的方法:

public class ControllerHelper
{
    private UranusContext db = new UranusContext();

    public void DeleteChain(int? chainId)
    {
        var chain = db.Chains.Include(c => c.Sheets)
            .SingleOrDefault(c => c.ChainId == chainId);

        // Removes sheets (children)
        foreach (var sheet in chain.Sheets.ToList())
        {
            DeleteSheet(sheet.SheetId);
        }

        db.Chains.Remove(chain);
        db.SaveChanges();
        db.Dispose();
    }

    public void DeleteSheet(int? sheetId)
    {
        var sheet = db.Sheets.Include(s => s.FileDetails)
            .Include(s => s.SheetsCounties)
            .SingleOrDefault(s => s.SheetId == sheetId);

        foreach (var fileDetails in sheet.FileDetails.ToList())
        {
            db.FileDetails.Remove(fileDetails);
        }

        foreach (var sheetsCounties in sheet.SheetsCounties.ToList())
        {
            db.SheetsCounties.Remove(sheetsCounties);
        }

        db.Sheets.Remove(sheet);
        db.SaveChanges();

        db.Dispose();
    }
}

在这种情况下,如何正确处理 DbContext? 我很困惑,因为如果我只在 DeleteChain 中处理,然后在 DeleteChain 之外调用 DeleteSheet,它就不会处理。

编辑:尝试#2。 我将每个包装在使用中并单独处理它们。 但是,当我删除DeleteSheet中的DeleteChain DeleteSheetDeleteChain没有意识到我这样做并抛出: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted. The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted. DeleteChain db.SaveChanges()上。

尝试#2。

public void DeleteChain(int? chainId)
{
    using (UranusContext db = new UranusContext())
    {
        var chain = db.Chains.Include(c => c.Sheets)
            .SingleOrDefault(c => c.ChainId == chainId);

        // Removes sheets (children)
        foreach (var sheet in chain.Sheets.ToList())
        {
            DeleteSheet(sheet.SheetId);
            //db.Sheets.Remove(sheet);
        }

        db.Chains.Remove(chain);
        db.SaveChanges();
    }
}

public void DeleteSheet(int? sheetId)
{
    using (UranusContext db = new UranusContext())
    {
        var sheet = db.Sheets.Include(s => s.FileDetails)
            .Include(s => s.SheetsCounties)
            .SingleOrDefault(s => s.SheetId == sheetId);

        foreach (var fileDetails in sheet.FileDetails.ToList())
        {
            db.FileDetails.Remove(fileDetails);
        }

        foreach (var sheetsCounties in sheet.SheetsCounties.ToList())
        {
            db.SheetsCounties.Remove(sheetsCounties);
        }

        db.Sheets.Remove(sheet);
        db.SaveChanges();
    }
}

您可能希望在单独的方法中移动工作表删除逻辑,并从 DeleteChain 和 DeleteSheet 中调用它,如下所示:

public class ControllerHelper
{
    public void DeleteChain(int? chainId) {
        using (var db = new UranusContext()) {
            var chain = db.Chains.Include(c => c.Sheets)
                .SingleOrDefault(c => c.ChainId == chainId);

            // Removes sheets (children)
            foreach (var sheet in chain.Sheets.ToList()) {
                DeleteSheet(db, sheet);
            }

            db.Chains.Remove(chain);
            db.SaveChanges();
        }
    }

    public void DeleteSheet(int? sheetId) {
        using (var db = new UranusContext()) {
            var sheet = db.Sheets.Include(s => s.FileDetails)
                .Include(s => s.SheetsCounties)
                .SingleOrDefault(s => s.SheetId == sheetId);

            DeleteSheet(db, sheet);
            db.SaveChanges();
        }
    }

    private void DeleteSheet(UranusContext db, Sheet sheet) {
        foreach (var fileDetails in sheet.FileDetails.ToList()) {
            db.FileDetails.Remove(fileDetails);
        }

        foreach (var sheetsCounties in sheet.SheetsCounties.ToList()) {
            db.SheetsCounties.Remove(sheetsCounties);
        }

        db.Sheets.Remove(sheet);
    }
}

最简单的方法是将 DbContext 作为参数传递:

public class ControllerHelper
{
    public void DeleteChain(int? chainId, UranusContext db)
    {
        var chain = db.Chains.Include(c => c.Sheets)
            .SingleOrDefault(c => c.ChainId == chainId);

        // Removes sheets (children)
        foreach (var sheet in chain.Sheets.ToList())
        {
            DeleteSheet(sheet.SheetId, db);
        }

        db.Chains.Remove(chain);
    }

    public void DeleteSheet(int? sheetId, UranusContext db)
    {
        var sheet = db.Sheets.Include(s => s.FileDetails)
            .Include(s => s.SheetsCounties)
            .SingleOrDefault(s => s.SheetId == sheetId);

        foreach (var fileDetails in sheet.FileDetails.ToList())
        {
            db.FileDetails.Remove(fileDetails);
        }

        foreach (var sheetsCounties in sheet.SheetsCounties.ToList())
        {
            db.SheetsCounties.Remove(sheetsCounties);
        }

        db.Sheets.Remove(sheet);
    }
}

然后像这样使用它

public void Foo(int? someChainId, int? someSheetId, bool required = false)
{
    ControllerHelper hlp = new ControllerHelper();
    using (UranusContext db = new UranusContext())
    {
        hlp.DeleteChain(someChainId);

        if(_required)
            hlp.DeleteSheet(int? someSheetId);

        db.SaveChanges();
    }
}

尝试使用模式并将上下文作为参数传递给 DeleteSheet 方法,因此:

    public class ControllerHelper
{
    public void DeleteChain(int? chainId)
    {
    using (UranusContext db = new UranusContext())
    {
            var chain = db.Chains.Include(c => c.Sheets)
                .SingleOrDefault(c => c.ChainId == chainId);

            // Removes sheets (children)
            foreach (var sheet in chain.Sheets.ToList())
            {
                DeleteSheet(sheet.SheetId, db);
            }

            db.Chains.Remove(chain);
            db.SaveChanges();
    }
    }

    public void DeleteSheet(int? sheetId, UranusContext db)
    {
        var sheet = db.Sheets.Include(s => s.FileDetails)
            .Include(s => s.SheetsCounties)
            .SingleOrDefault(s => s.SheetId == sheetId);

        foreach (var fileDetails in sheet.FileDetails.ToList())
        {
            db.FileDetails.Remove(fileDetails);
        }

        foreach (var sheetsCounties in sheet.SheetsCounties.ToList())
        {
            db.SheetsCounties.Remove(sheetsCounties);
        }

        db.Sheets.Remove(sheet);
        db.SaveChanges();
    }
}

这样,您可以确保只有在所有删除都完成后才会处理上下文。 我总是尽量避免一次使用多个上下文,我之前已经被它严重烧伤了!

在您的设计中, ControllerHelperUranusContext的组合。 这意味着您的设计是只要ControllerHelper存在, UranusContext存在,并且您希望一旦ControllerHelper不再存在, UranusCibtext就停止存在。

通过使它成为一个组合,您向类的用户承诺,只要您的类存在,您的所有函数都可以工作。 因此,在您的用户通知您他们不再需要您的功能之前,您不应在您的任何函数中使用Dispose() UranusContext 这可以通过使您的ControllerHelper IDisposable 来完成

如果任何类实现了 IDisposable,这个类的设计者会鼓励用户在不再需要该对象的功能后尽快调用Dispose()

在您的情况下,代码将是:

public class ControllerHelper : IDisposable()
{
    private UranusContext db = new UranusContext();

    public void Dispose()
    {
        if (this.db != null)
        {
            this.db.Dispose();
            this.db = null;
        }
        GC.SuppressFinalize(this);
    }

    public void DeleteChain(int? chainId)
    {
       // Do what you need to do, but don't Dispose()
       // nor use using, we want db to be functional
    }
    public void DeleteSheet(int? sheetId)
    {
       // Do what you need to do, but don't Dispose()
       // nor use using, we want db to be functional
    }
}

用法是:

using (var controllerHelper = new ControllerHelper())
{
    // call functions from controllerhelper
}
// the controllerHelper is not needed anymore, Dispose() is called automatically
// and thus db is Disposed

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM