1

我有使用 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 操作方法负责编辑用户详细信息。这是它将视图模型传递给视图的方式:

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

And shares it in the same context in the scope of the thread which is implemented in the Ninject Controller Factory by AddBindings() method:

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

A PROBLEM: For some strange reason, once every so often, when user object is being edited, context object presents wrong values for the user properties. It is happening on the level of EF somewhere between the DbContext and actual data. Especially, that data in SQL server is always right. It looks like EF is caching previous values for the properties and takes those instead of sourcing them from database. I have not observed when this behaviour exactly occurs, but it happens rather often - every second or third time when object is edited. Sometimes it happens couple of times in the row.

I have used the same setup in previous application and everything was fine. The only difference this time is that I'm using WebGrid Helper, and only pages with WebGrid Helper seem to cause this problem in my application???

4

2 回答 2

1

Does your data render correctly if you try this?

Load Entities with 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);
}

Detach Entities after saving:

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

This will give you detached entities which aren't tracked in the EF context, an approach that may or not be acceptable depending on your application. Even if you can't use it in the long run, give it a try to see if the problem is really the EF caching.

于 2013-01-24T11:30:41.947 回答
0

I have noticed this behavior also. To demonstrate, Open View, make the change directly in the dB. BANG you will see the change is not reflected in in your view.

EF does cache data and will only "refresh" (bad choice of word here) this data if the context you read from the dB is also the context you had written to the dB with.

Hope this points you int he right direction.

于 2013-01-24T11:23:46.127 回答