ObjectInstance已被释放,不能再用于需要连接的操作

时间:2016-02-02 13:04:09

标签: c# entity-framework

我使用EF6。在我的BL-Layer中,我有以下静态类,使用我的上下文,它实现了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中使用它?为什么我不允许再次使用我的上下文类,在它被处置一次之后呢?我已经阅读了很多类似的问题,但每个人都说只有关于急切/懒惰加载的问题。

1 个答案:

答案 0 :(得分:2)

&#34;每个人都只说关于渴望/延迟加载的问题&#34;是因为那就是发生了什么。事件的顺序是:

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

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

在使用块的末尾,它处理上下文。这将关闭数据库连接,并将上下文对象标记为已丢弃,因此无法使用。

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

这将强制在处理上下文之前填充集合。然而,它可能还有其他缺点:如果集合中的实体反过来又收集了其他对象,那么当它们被访问时,它会尝试填充这些子集合,并且你会回到现在的位置。

另一种方法是不使用using / dispose模式。但在某些时候,你将不得不清理上下文,所以你必须考虑如何做到这一点。

此外,在插入/更新EF实体以保持您用于读取数据的上下文时,它会容易得多。如果你不需要将更新的实体转移到一个听起来很痛苦的新环境(我从未这样做过)。

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