对象实例已被释放,不能再用于需要连接的操作

本文关键字:用于 连接 操作 不能 实例 释放 对象 | 更新日期: 2023-09-27 18:32:50

我使用 EF6。在我的BL层中,我有以下静态类,使用我的上下文,它实现了DBContext:

 public static class AppEnvironment
 {
        public static IUser CurrentUser { get; set; }
        private static IKernel AppKernel { get; set; }
    public static void InjectDependencies(params NinjectModule[] contextNinjectModule)
    {
        AppKernel = new StandardKernel(contextNinjectModule);
    }
    public static void Authorize(string login, string password)
    {
        using (var context = AppKernel.Get<ICaseContext>())
        {
            IUser userToBeAuthorized = context.GetAll<User>().FirstOrDefault(u => u.Login == login);
            if (userToBeAuthorized != null && User.GetMD5Hash(password) == userToBeAuthorized.PasswordHash)
            {
                AppEnvironment.CurrentUser = userToBeAuthorized;
                context.Insert(
                    LogRecord.CreateLogRecord(
                    userToBeAuthorized,
                    "Авторизация (успешно)",
                    LogAction.Read));
            }
        }
    }
    public static ICollection<CaseEntityType> GetCaseListTiny<CaseEntityType>(string queryComment) where CaseEntityType : CaseEntityBase
    {
        using (var context = AppKernel.Get<ICaseContext>())
        {
            var grantedCaseTypesIDs = AppEnvironment.CurrentUser.CaseTypesGranted.Select(casetype => casetype.ID).ToList();
            var cases = context.GetAllIncluding<TaskEntityType>(AppEnvironment.CurrentUser, queryComment, LogAction.Read, t => t.CaseType
                ).Where(t => grantedTaskTypesIDs.Contains(t.CaseType.ID)).ToList();
            return cases;
        }
    }
} 

在我的 UI 层中,我尝试像这样使用 smth:

class Program
{
    static void Main(string[] args)
    {
        AppEnvironment.InjectDependencies(new RealContextNinjectModule());
        AppEnvironment.Authorize("UserName", "Password");
        var caseList = AppEnvironment.GetCaseListTiny<RegularCase>("Get a list");
        foreach (var item in caseList)
        {
            Console.WriteLine(item.Name);
        }
    }
}

但它抛出了 ObjectContextDisposedException。谁能解释一下处理 EF6 上下文类的正确方法是什么?我应该如何在我的 BLL 或 UIL 中使用它?为什么不允许我在处理一次上下文类后再次使用它?我读过很多类似的问题,但每个人都只说急切/懒惰加载。

对象实例已被释放,不能再用于需要连接的操作

"每个人都只说急切/懒惰加载"的原因是因为这就是正在发生的事情。事件的顺序为:

var caseList = AppEnvironment.GetCaseListTiny<RegularCase>("Get a list");

然后,GetCaseTinyList 调用 AppKernel.Get,它将上下文返回到包装在 using 块中的本地 var 上下文中。在块中,它通过对上下文发出调用来创建集合。它不访问该列表,因此集合实际上并未填充;在访问集合之前,不会运行任何 SQL。这是懒惰的演讲。

在 using 块的末尾,它会释放上下文。这将关闭数据库连接,并将上下文对象标记为已释放,因此不可用。

GetCaseTinyList 返回,将未填充的集合传递回去。

最后,对返回的集合运行 foreach 。在第一次访问时,EF 集合尝试针对上下文运行 GetCaseTinyList 的 SQL。可悲的是,上下文已被处理(您确实告诉它处理它,因此它确实如此(。这使得系统完全按照应有的方式抛出您遇到的错误。

解决此问题的一种方法是更改GetCaseTinyCollection以访问集合;如下所示:

public static ICollection<CaseEntityType> GetCaseListTiny<CaseEntityType>(string queryComment) where CaseEntityType : CaseEntityBase
{
    using (var context = AppKernel.Get<ICaseContext>())
    {
        var grantedCaseTypesIDs = AppEnvironment.CurrentUser.CaseTypesGranted.Select(casetype => casetype.ID).ToList();
        var cases = context.GetAllIncluding<TaskEntityType>(AppEnvironment.CurrentUser, queryComment, LogAction.Read, t => t.CaseType
            ).Where(t => grantedTaskTypesIDs.Contains(t.CaseType.ID)).ToList();
        int count = cases.Count(); // or Count<T>();
        return cases;
    }
}

这将强制在释放上下文之前填充集合。但是,它可能还有进一步的缺点:如果集合中的实体依次具有其他对象的集合,那么在访问时,它将尝试填充这些子集合,然后您又回到了现在的位置。

另一种方法是不使用使用/释放模式。但是在某些时候,您将不得不清理上下文,因此您必须考虑如何做到这一点。

此外,在插入/更新 EF 实体时,保留用于读取数据的上下文要容易得多。如果您不这样做,则必须将更新的实体转移到听起来很痛苦的新上下文中(我从未这样做过(。

基本上,您需要重新考虑管理数据库上下文的策略。