【问题标题】:Strange Entity Framework behaviour - caching?奇怪的实体框架行为 - 缓存?
【发布时间】:2013-01-08 01:28:12
【问题描述】:

我有使用 EF 4.3 和迁移的 ASP.NET MVC 4 应用程序。我使用 WebGrid Helper 为系统用户显示详细信息:

@grid.GetHtml(
headerStyle: "gridHeader",
footerStyle: "gridFooter",
firstText: "<< First",
previousText: "< Previous", 
nextText: "Next >",
lastText: "Last >>",
alternatingRowStyle: "gridAlternativeRow",
columns: new[] {
   grid.Column("Login", header: "User Login", canSort: true),
   grid.Column("FullName", header: "User Name", canSort: true),
   grid.Column("Email", header: "User Email", canSort: true),
   grid.Column("Category", header: "User Category", canSort: true),
   grid.Column(
    "", 
    header: "", 
    format: @<text>
            @Html.ActionLink("Edit",   "Edit",   new { id=item.Id} )
            </text>
    )                                                
})

如您所见,Edit 操作方法负责编辑用户详细信息。这是将 View Model 传递给视图的方式:

    public ActionResult Edit(int Id)
    {
        User user = repo.GetUser(Id);

        RegisterModel rm = new RegisterModel();
        rm.Id = user.Id;
        rm.Name = user.FullName;
        rm.UserName = user.Login;
        rm.Email = user.Email;
        rm.UserCategory = user.Category;
        rm.Categories = new List<SelectListItem>();

        List<Category> categories = repo.GetAllCategories();
        foreach (var item in categories)
        {
            SelectListItem sli = new SelectListItem();
            sli.Value = null;
            sli.Text = item.Title;
            if (user.Category == item.Title) sli.Selected = true;
            rm.Categories.Add(sli);
        }

        return View(rm);
    }

这就是它保存细节的方式:

    [HttpPost]
    public ActionResult Edit(RegisterModel rm, string NewPassword, string OldLogin)
    {
        if (NewPassword != "")
        {
            var token = WebSecurity.GeneratePasswordResetToken(OldLogin);
            WebSecurity.ResetPassword(token, NewPassword);
        }

        User user = new User();
        user.Id = Convert.ToInt32(rm.Id);
        user.FullName = rm.Name;
        user.Email = rm.Email;
        user.Category = rm.UserCategory;
        user.Login = rm.UserName;

        string result = repo.UpdateUserDetails(user);

        return RedirectToAction("Index");
    }

然后它重定向到索引操作方法,该方法获取用户列表并将其传递回带有 WebGrid Helper 的视图。

每次访问存储库时,我都会从 DbContext 对象中获取用户的最新值:

    public List<User> GetAllUsersWithoutAdmin()
    {
        return context.Users.Where(x => x.Id != 1).OrderBy(x => x.FullName).ToList();
    }

    public User GetUser(int userId)
    {
        return context.Users.FirstOrDefault(x => x.Id == userId);
    }

    public string UpdateUserDetails(User user)
    {
        string info;

        try
        {
            User uUser = context.Users.FirstOrDefault(x => x.Id == user.Id);
            uUser.Category = user.Category;
            uUser.Email = user.Email;
            uUser.FullName = user.FullName;
            uUser.Login = user.Login;

            context.SaveChanges();
            info = "success";
        }
        catch (Exception err)
        {
            info = err.Message;
        }

        return info;
    }

另外我使用 UoW 模式来解决在同一个控制器中使用不同存储库的问题:

public interface IUnitOfWork
{
    int SaveChanges();
}

然后每个存储库都实现这个接口:

    private ActivityLogContext context;

    public UserRepository(IUnitOfWork _context)
    {
        context = _context as ActivityLogContext;
    }

并在 Ninject Controller Factory 通过 AddBindings() 方法实现的线程范围内共享它:

    private void AddBindings()
    {
        ninjectKernel.Bind<IActivityRepository>().To<ActivityRepository>();
        ninjectKernel.Bind<IUserRepository>().To<UserRepository>();
        ninjectKernel.Bind<ICategoryRepository>().To<CategoryRepository>();
        ninjectKernel.Bind<IUnitOfWork>().To<ActivityLogContext>().InThreadScope();
    }

一个问题: 出于某种奇怪的原因,每隔一段时间,当编辑用户对象时,上下文对象就会为用户属性提供错误的值。它发生在 DbContext 和实际数据之间某处的 EF 级别。特别是,SQL 服务器中的数据总是正确的。看起来 EF 正在缓存属性的先前值并获取这些值而不是从数据库中获取它们。我没有观察到这种行为何时确切发生,但它经常发生 - 在编辑对象时每隔第二次或第三次发生一次。有时它会连续发生几次。

我在以前的应用程序中使用了相同的设置,一切都很好。这次唯一的区别是我使用的是 WebGrid Helper,而且似乎只有带有 WebGrid Helper 的页面会在我的应用程序中导致这个问题???

【问题讨论】:

    标签: c# entity-framework caching asp.net-mvc-4 webgrid


    【解决方案1】:

    如果您尝试这样做,您的数据是否正确呈现?

    使用 AsNoTracking 加载实体:

    public List<User> GetAllUsersWithoutAdmin()
    {
        return context.Users.AsNoTracking().Where(x => x.Id != 1)
            .OrderBy(x => x.FullName).ToList();
    }
    
    public User GetUser(int userId)
    {
        return context.Users.AsNoTracking().FirstOrDefault(x => x.Id == userId);
    }
    

    保存后分离实体:

    public string UpdateUserDetails(User user)
    {
        string info;
    
        try
        {
            User uUser = context.Users.FirstOrDefault(x => x.Id == user.Id);
            uUser.Category = user.Category;
            uUser.Email = user.Email;
            uUser.FullName = user.FullName;
            uUser.Login = user.Login;
    
            context.SaveChanges();
    
            // detach the entity after saving it
            Context.Entry(uUser).State = System.Data.EntityState.Detached;
    
            info = "success";
        }
        catch (Exception err)
        {
            info = err.Message;
        }
    
        return info;
    }
    

    这将为您提供未在 EF 上下文中跟踪的分离实体,根据您的应用程序可能会或不会接受这种方法。即使你不能长期使用它,也可以尝试一下,看看问题是否真的是 EF 缓存。

    【讨论】:

    • 谢谢,如果有任何改变,我会尝试。
    • 您的解决方案有帮助!但是我只能在您在答案中突出显示的两种方法中使用它。无法在编辑中使用它,因为我需要在那里跟踪更改。
    • 您的解决方案似乎解决了问题,但我仍然不知道是什么原因造成的。
    • System.Data.EntityState.Detached 在 EF 6.1 中变为 System.Data.Entity.EntityState.Detached
    【解决方案2】:

    我也注意到了这种行为。为了演示,打开视图,直接在 dB 中进行更改。 BANG 你会看到变化没有反映在你的视图中。

    如果您从 dB 读取的上下文也是您写入 dB 的上下文,EF 会缓存数据,并且只会“刷新”(此处选择错误的词)此数据。

    希望这能为您指明正确的方向。

    【讨论】:

    • 感谢您的帮助。我仍然没有想到如何解决此问题,因为我正在通过 Edit 操作方法中的上下文进行更改,因此应该反映
    猜你喜欢
    • 2015-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多