1

EF 4.3 と移行を使用した ASP.NET MVC 4 アプリケーションがあります。WebGrid ヘルパーを使用して、システム ユーザーの詳細を表示します。

@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");
    }

次に、ユーザーのリストをソースとする Index アクション メソッドにリダイレクトし、それを WebGrid ヘルパーを使用してビューに戻します。

リポジトリにアクセスするたびに、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;
    }

AddBindings() メソッドによって Ninject Controller Factory に実装されているスレッドのスコープ内の同じコンテキストで共有します。

    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 がプロパティの以前の値をキャッシュしているように見え、データベースから取得する代わりにそれらを取得します。この動作が正確にいつ発生するかは観察していませんが、かなり頻繁に発生します。オブジェクトが編集されるたびに 2 回または 3 回発生します。場合によっては、連続して数回発生することもあります。

以前のアプリケーションで同じ設定を使用しましたが、すべて問題ありませんでした。今回の唯一の違いは、私が WebGrid Helper を使用していることと、WebGrid Helper を使用するページだけが私のアプリケーションでこの問題を引き起こしているように見えることです???

4

2 に答える 2

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 キャッシュにあるかどうかを確認してみてください。

于 2013-01-24T11:30:41.947 に答える
0

私もこの行動に気づきました。デモンストレーションを行うには、ビューを開き、dB で直接変更します。BANG 変更がビューに反映されていないことがわかります。

EF はデータをキャッシュし、dB から読み取ったコンテキストが dB に書き込んだコンテキストでもある場合にのみ、このデータを「更新」します (ここでは単語の選択が不適切です)。

これがあなたを正しい方向に向けてくれることを願っています。

于 2013-01-24T11:23:46.127 に答える