5

每次我尝试将用户附加到组(或创建组)时都会收到此错误。

这是 GroupController :

public class GroupsController : Controller
{
       private Context db = new Context();

        [HttpPost]
        public ActionResult Create(Group group)
        {
            if (ModelState.IsValid)
            {
                group.owner = db.Users.Attach((User)Session["user"]);
    //current user stored in session
                db.Groups.Add(group);
                db.SaveChanges();

                return RedirectToAction("Index", "Home");
            }
            return View(group);
        }
}

变量 Session["user"] 在登录时设置,这里是 AccountController 代码:

public class AccountController : Controller
{
    private Context db = new Context();

    [HttpPost]
    public ActionResult LogOn(LoginTemplate login, string returnUrl)
    {

            User result = db.Users.Where(m => m.email == login.email).Where(m => m.password == login.password).SingleOrDefault();

            if (result != null)
            {
                Session["logged"] = true;
                Session["user"] = result;
                return RedirectToAction("Index", "Home");
            }
     }
 }

这是我的上下文:

public class Context : DbContext
{

    public Context() : base("name=myContext")
    {
    }
    private static Context _instance;

    public DbSet<User> Users { get; set; }
    public DbSet<Group> Groups { get; set; }

//I tried to use this method in order to always get the same context but does not work

    public static Context getContext()
    {
        if (_instance == null)
        {
            _instance = new Context();
        }

        return _instance;
    }
}

这是 User.cs

       public class User
{
    [Key]
    public int userId { get; set; }

    [Display(Name="Firstname")]
    public string firstname { get; set; }

    [Display(Name = "Lastname")]
    public string lastname { get; set; }

    public virtual ICollection<Group> membership { get; set;}

}

这是 group.cs :

        public class Group
{
    [Key]
    public int idGroup { get; set; }
    public string name { get; set; } 
    public User owner { get; set; }
    public virtual ICollection<User> members { get; set; }

}

我试图在我的上下文中做一个“单例”(总是在每个控制器中重用相同的上下文)但是当我这样做时,我收到“Db closed”错误消息......

4

4 回答 4

2

初步答案

你所描述的对我来说似乎是预期的行为。

怎么了?

  1. 当您登录时加载一个用户实体,并保存在会话中。但是由于跟踪用户实体的上下文对用户实体有引用,更重要的是,用户实体对上下文有引用,所以上下文不会被释放,即使你的控制器将被垃圾回收,上下文也是不是。

  2. 组控制器是实例化的,它有一个单独的上下文。然后您创建一个组实体,此时该组实体还没有对上下文的引用(或相反)。所以将用户实体连接到组实体就可以了。但是,当您将组实体附加到新上下文时,上下文开始跟踪此实体以及组实体引用的所有其他实体。所以这包括用户实体。由于原始上下文也在跟踪用户实体,因此会导致异常。

我可以看到一个简单的解决方案,即当您要将用户实体连接到组实体时,使用用户实体的 id 而不是对象本身。这样,在组实体和用户实体之间不会创建任何引用,因此应该可以正常工作。

更新

在第二次阅读时,我发现您在组实体中没有用户 ID 属性,因此我的解决方案不起作用。

于 2012-11-18T18:03:34.080 回答
2

问题是由virtual实体导航属性的关键字引起的。EF 将创建一个延迟加载代理,然后它能够​​动态加载导航属性,但要做到这一点,它需要保存对上下文的引用。当您稍后将实体附加到另一个上下文时,您会收到此异常,因为代理仍被它指向的旧上下文跟踪。

您可以通过在加载用户时禁用代理创建和跟踪来修复错误,以确保加载不再引用上下文的纯(非代理)POCO 实体:

public class AccountController : Controller
{
    private Context db = new Context();

    public AccountController()
    {
        db.Configuration.ProxyCreationEnabled = false;
    }

    [HttpPost]
    public ActionResult LogOn(LoginTemplate login, string returnUrl)
    {
        User result = db.Users.AsNoTracking()
            .Where(m => m.email == login.email)
            .Where(m => m.password == login.password)
            .SingleOrDefault();

        if (result != null)
        {
            Session["logged"] = true;
            Session["user"] = result;
            return RedirectToAction("Index", "Home");
        }
    }
}

它将禁用整个控制器的代理和延迟加载,但是Include当您需要加载导航属性时,您可以将其替换为急切加载 ( )。

即使这样可以解决您的问题,在我看来,将实体存储在会话状态中仍然不是最佳做法。最好使用专门的UserForSessionModel类(不是数据库实体)或仅存储Id并在需要时从数据库重新加载用户。

编辑

您也可以在Create操作中使用“存根实体”来避免附加另一个上下文引用的加载用户:

var userStub = new User { userId = ((User)Session["user"]).userId };
group.owner = db.Users.Attach(userStub);
db.Groups.Add(group);
db.SaveChanges();

用户建立关系唯一需要的就是它的主键,所以这样一个只填充键的空用户就足够了。

于 2012-11-18T18:33:48.613 回答
0

当我有一个缓存查找实体并从缓存中检索它们的缓存层时,我遇到了同样的错误。缓存的实体是代理实体,并且仍然具有对最初检索它们的 DBContext 的引用。

其主要症状是带有查找引用的域实体的第一次保存工作正常,但随后的保存失败。

我希望这对某人有所帮助,因为这个问题使我的生活中断了 2 天。

于 2015-01-19T22:59:05.843 回答
0

无需将 db.Users.Attach 与保存在会话状态中的 User 对象一起使用。您要保存的对象是新组,而不是用户。我的猜测是,当您调用 db.Users.Attach((User)Session["user"]) 时会引发异常,这可能表明当前用户的实体已在当前上下文中由 EF 管理。

于 2012-11-18T17:39:15.563 回答