ConcurrencyExeption в приложении ASP.NET MVC

Введение в проблему

Прежде всего, я попробовал все, чтобы решить эту проблему безрезультатно. В настоящее время я впервые пытаюсь самостоятельно создать шаблон хранилища.

Мое приложение - это просто блог-сайт, и в нем есть несколько компонентов. Я прямо укажу на проблему для вас.

проблема

Когда я хочу обновить сообщение через систему репозитория, возникает исключение (исключение параллелизма), но я уверен, что этот тип исключения возникает при определении типа столбца "[TimeStamp]" в таблице сообщений. Я знаю, как именно обработать это исключение, и я уверен, что никто не обновляет сообщение, которое я обновляю в настоящее время, потому что оно работает в локальной системе. Я думаю, что нет причин для возникновения этого исключения, кроме причины, которую я не знаю, и, возможно, вы можете помочь мне в этом вопросе.

Я определил проблему для вас, тогда давайте перейдем к блокам кода;

Компоненты

У меня есть этот AdminController

public class AdminController : Controller
{
    private IDbFactory _dbFactory;
    private IUnitOfWork _unitOfWork;

    private ICategoryRepository _categoryRepository;
    private IPostRepository _postRepository;
    private ITagRepository _tagRepository;

    public ICategoryRepository categoryRepository
    {
        get
        {
            return _categoryRepository ?? (_categoryRepository = new CategoryRepository(HttpContext.GetOwinContext().Get<DbFactory>()));
        }
        set
        {
            _categoryRepository = value;
        }
    }
    public IPostRepository postRepository
    {
        get
        {
            return _postRepository ?? (_postRepository = new PostRepository(HttpContext.GetOwinContext().Get<DbFactory>()));
        }

        set
        {
            _postRepository = value;
        }
    }
    public ITagRepository tagRepository
    {
        get
        {
            return _tagRepository ?? (_tagRepository = new TagRepository(HttpContext.GetOwinContext().Get<DbFactory>()));
        }

        set
        {
            _tagRepository = value;
        }
    }
public IDbFactory dbFactory
    {
        get
        {
            return _dbFactory ?? (_dbFactory = 
           HttpContext.GetOwinContext().Get<DbFactory>());
        }
    }
    public IUnitOfWork unitOfWork
    {
        get
        {
            return _unitOfWork ?? (_unitOfWork = 
           HttpContext.GetOwinContext().Get<UnitOfWork>());
        }
    }
[HttpPost]
    [ValidateAntiForgeryToken]
    [ValidateInput(false)]
    public ActionResult UpdatePost([Bind(Include = "IntroText, PostText, CodeText, Header, Author, ImagePath, CategoryID")] ViewPostModel model)
    {
        if (ModelState.IsValid)
        {
            Post post = new Post();
            post = Mapper.Map<ViewPostModel, Post>(model);

            if (model.CodeText != null)
                post.PostText = GetCodedPostText(model.PostText, model.CodeText);

            post.CreatedDate = DateTime.Now.ToShortDateString();
            post.CategoryID = model.CategoryID;
            postRepository.Update(post);
            unitOfWork.SaveChanges(); // Throws and exception (Concurrency Exception)
        }
        ViewBag.Categories = FillCategoriesForDropdownList();
        return RedirectToAction("DetailPost");
    }
}

У меня есть этот универсальный класс RepositoryBase;

private IDbFactory dbFactory;
    private AppDbContext context;
    private ICategoryRepository _categoryRepository;
    private IPostRepository _postRepository;
    private ITagRepository _tagRepository;

    //public ICategoryRepository categoryRepository
    //{
    //    get
    //    {
    //        return _categoryRepository ?? (_categoryRepository = new CategoryRepository(HttpContext.Current.GetOwinContext().Get<DbFactory>()));
    //    }
    //    set
    //    {
    //        _categoryRepository = value;
    //    }
    //}
    //public IPostRepository postRepository
    //{
    //    get
    //    {
    //        return _postRepository ?? (_postRepository = new PostRepository(HttpContext.Current.GetOwinContext().Get<DbFactory>()));
    //    }

    //    set
    //    {
    //        _postRepository = value;
    //    }
    //}
    //public ITagRepository tagRepository
    //{
    //    get
    //    {
    //        return _tagRepository ?? (_tagRepository = new TagRepository(HttpContext.Current.GetOwinContext().Get<DbFactory>()));
    //    }

    //    set
    //    {
    //        _tagRepository = value;
    //    }
    //}

    AppDbContext db
    {
        get
        {
            return context ?? (context = dbFactory.Init());
        }
    }

    public ICategoryRepository categoryRepository
    {
        get
        {
            throw new NotImplementedException();
        }

        set
        {
            throw new NotImplementedException();
        }
    }

    public IPostRepository postRepository
    {
        get
        {
            throw new NotImplementedException();
        }

        set
        {
            throw new NotImplementedException();
        }
    }

    public ITagRepository tagRepository
    {
        get
        {
            throw new NotImplementedException();
        }

        set
        {
            throw new NotImplementedException();
        }
    }

    public static UnitOfWork Create()
    {
        return new UnitOfWork(HttpContext.Current.GetOwinContext().Get<DbFactory>());
    }

    public UnitOfWork(IDbFactory _dbFactory)
    {
        dbFactory = _dbFactory;
    }

    public void SaveChanges()
    {
        db.SaveChanges();
    }

    public void Dispose()
    {

    }
}

У меня есть этот почтовый репозиторий;

public class PostRepository : RepositoryBase<Post>, IPostRepository, IDisposable
{
    public PostRepository(IDbFactory dbFactory) : base(dbFactory) { }

    public static PostRepository Create()
    {
        return new PostRepository(HttpContext.Current.GetOwinContext().Get<DbFactory>());
    }

    public void Dispose()
    {

    }
}

У меня есть этот инициализатор базы данных;

public class AppDbInitializer : DropCreateDatabaseAlways<AppDbContext>
{
    protected override void Seed(AppDbContext context)
    {
        SeedIdentity(context);
        SeedTables(context);
        base.Seed(context);
    }

    private void SeedIdentity(AppDbContext context)
    {
        //var userManager = HttpContext.Current.GetOwinContext().GetUserManager<AppUserManager>();
        //var roleManager = HttpContext.Current.GetOwinContext().Get<AppRoleManager>();
        const string name = "admin@example.com";
        const string password = "SelcuK99.";
        const string roleName = "Admin";

        #region Old
        //var role = roleManager.FindByName(roleName);
        //AppRole role = null;
        //if (role == null)
        //{
        //    role = new AppRole(roleName);
        //    var roleresult = roleManager.Create(role);
        //}
        //AppUser user = null;
        ////var user = userManager.FindByName(name);
        //if (user == null)
        //{
        //    user = new AppUser { UserName = name, Email = name };
        //    var result = userManager.Create(user, password);
        //    result = userManager.SetLockoutEnabled(user.Id, false);
        //}

        //var rolesForUser = userManager.GetRoles(user.Id);
        //if (!rolesForUser.Contains(role.Name))
        //{
        //    var result = userManager.AddToRole(user.Id, role.Name);
        //}
        #endregion

        RoleStore<AppRole> roleStore = new RoleStore<AppRole>(context);
        RoleManager<AppRole> roleManager = new RoleManager<AppRole>(roleStore);
        AppRole role = new AppRole
        {
            Name = roleName
        };
        roleManager.Create(role);

        UserStore<AppUser> userStore = new UserStore<AppUser>(context);
        AppUserManager userManager = new AppUserManager(userStore);
        AppUser user = new AppUser { Email = name, UserName = name};
        userManager.Create(user, password);
        userManager.AddToRole(user.Id, roleName);
    }
}

У меня есть этот класс запуска OwinContext;

    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.CreatePerOwinContext(AppDbContext.Create);
            app.CreatePerOwinContext(DbFactory.Create);
            app.CreatePerOwinContext(TagRepository.Create);
            app.CreatePerOwinContext(CategoryRepository.Create);
            app.CreatePerOwinContext(PostRepository.Create);
            app.CreatePerOwinContext(UnitOfWork.Create);
            app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create);
            app.CreatePerOwinContext<AppRoleManager>(AppRoleManager.Create);
            app.CreatePerOwinContext<AppSignInManager>(AppSignInManager.Create);

            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = 
    DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Account/Login"),
                Provider = new CookieAuthenticationProvider
                {  
                    OnValidateIdentity = 
    SecurityStampValidator.OnValidateIdentity<AppUserManager, AppUser>(
                        validateInterval: TimeSpan.FromMinutes(30),
                        regenerateIdentity: (manager, user) => 
    user.GenerateUserIdentityAsync(manager))
                }
            });



  app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
        }   
    }

Вот AppDbContext

public class AppDbContext : IdentityDbContext<AppUser>
    {
        public AppDbContext() : base("AppDbContext", throwIfV1Schema: false) { }

        public static AppDbContext Create()
        {
            return new AppDbContext();
        }

        public DbSet<Category> Categories { get; set; }
        public DbSet<Post> Posts { get; set; }
        public DbSet<Tag> Tags { get; set; }

        static AppDbContext()
        {
            Database.SetInitializer(new AppDbInitializer());
        }

        //protected override void OnModelCreating(DbModelBuilder modelBuilder)
        //{
        //    modelBuilder.Configurations.Add(new CategoryConfiguration());
        //    modelBuilder.Configurations.Add(new PostConfiguration());
        //}
    }

У меня есть этот пост класс

public class Post : BaseEntity, IAudit
    {
        public int CategoryID { get; set; }
        public string IntroText { get; set; }
        public string PostText { get; set; }
        public string CodeText { get; set; }
        public string Header { get; set; }
        public string Author { get; set; }
        public string ImagePath { get; set; }
        public string CreatedDate { get; set; }
        public string UpdatedDate { get; set; }
        public virtual ICollection<PostTagMapping> PostTags { get; set; }
        public Category Category { get; set; }
    }

и наконец у меня есть эта ViewPostModel;

public class ViewPostModel : BaseEntity
    {
        public string PostText { get; set; }
        public string IntroText { get; set; }
        public string CodeText { get; set; }
        public string Author { get; set; }
        public string Header { get; set; }
        public string ImagePath { get; set; }
        public DateTime? CreatedDate { get; set; }
        public DateTime? UpdatedDate { get; set; }
        public int CategoryID { get; set; }
    }

Я забыл дать вам DbFactory;

public class DbFactory : Disposable, IDbFactory
    {
        private AppDbContext context;

        public static DbFactory Create()
        {
            return new DbFactory();
        }

        public AppDbContext Init()
        {
            int a;
            if (context == null)
                a = 5;
            return (HttpContext.Current.GetOwinContext().Get<AppDbContext>());
        }

        protected override void DisposeCore()
        {
            if (context != null)
                context.Dispose();
        }
    }

Я даю вам все, чтобы решить эту проблему.

Вот мои предположения и вопросы ##

  1. Может быть, где-то может быть состояние гонки, но как это возможно, я использую статический DbContext?
  2. Может быть, есть два запущенных экземпляра DbContext, но как это возможно снова, я использую статический DbContext?

Вот подробности исключения

Сообщение InnerException: оператор обновления, вставки или удаления магазина затронул неожиданное количество строк (0). Объекты могут быть изменены или удалены с момента загрузки объектов. См. http://go.microsoft.com/fwlink/?LinkId=472540 для получения информации о понимании и обработке исключений оптимистичного параллелизма.

Трассировки стека:

at System.Data.Entity.Internal.InternalContext.SaveChanges()
   at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
   at System.Data.Entity.DbContext.SaveChanges()
   at HybridBlog.Model.RepositoryBase`1.Update(TEntity entity) in D:\MVC_Projects\TrialProjects\HybridBlog\HybridBlog.Model\RepositoryBase.cs:line 71
   at HybridBlog.Web.Controllers.AdminController.UpdatePost(ViewPostModel model) in D:\MVC_Projects\TrialProjects\HybridBlog\HybridBlog.Web\Controllers\AdminController.cs:line 153
   at lambda_method(Closure , ControllerBase , Object[] )
   at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
   at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<BeginInvokeSynchronousActionMethod>b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3d()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f()

BaseEntity.cs

public class BaseEntity
    {
        public int ID { get; set; }
    }

1 ответ

Решение

Я сильно подозреваю, что вы не устанавливаете post.ID в вашем методе обновления. Вы можете убедиться в этом, проверив значение post.ID до postRepository.Update(post); вызов.

Я подозреваю, что вам нужно изменить:

public ActionResult UpdatePost([Bind(Include = "IntroText, PostText, CodeText, Header, Author, ImagePath, CategoryID")] ViewPostModel model)
{

чтобы:

public ActionResult UpdatePost([Bind(Include = "ID, IntroText, PostText, CodeText, Header, Author, ImagePath, CategoryID")] ViewPostModel model)
{
Другие вопросы по тегам