0

Introduction to the Problem

First of all I tried everything to figure out this problem by no result. I am currently trying to design a repository pattern on my own for the first time.

My application is just a Blog Website and it has some components. I will directly point to the problem for you.

Problem

When I want to update a post through repository system throws an exception (Concurrency Exception) but I am sure that this type of exception occurs when you define a "[TimeStamp]" column type in Post Table. I know how to handle this exception exactly and I am sure nobody is updating the post which I am updating currently because it works on local system. I think no reason to occur this exception except a reason which I don't know and maybe you can help me at this point.

I defined the problem explicitly for you then let's go code blocks;

Components

I have this 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");
    }
}

I have this RepositoryBase generic class;

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()
    {

    }
}

I have this Post Repository;

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()
    {

    }
}

I have this Database Initializer;

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 = "[email protected]";
        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);
    }
}

I have this OwinContext startup class;

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

Here is 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());
        //}
    }

I have this Post class

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

and finaly I have this 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; }
    }

I forget to give you the 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();
        }
    }

I give you everything to solve this issue.

Here is my assumptions and questions ##

  1. Maybe somewhere there could be a race condition but how is that possible I am using static DbContext ?
  2. Maybe there are two running DbContext instances but how is that possible again I am using static DbContext ?

Here is the details of the Exception

InnerException Message: Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions.

StackTrace:

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; }
    }
9
  • Check this Ans I think it will help to resolve your problem : stackoverflow.com/questions/44070400/… Commented Jun 7, 2017 at 12:10
  • Thank you for your comment but it did not help. I have very diffirent senario here. Commented Jun 7, 2017 at 12:17
  • 2
    Never use an (effectively) static, long lived context. This is not how it was designed to be used. The Entity Framework context should be short lived and disposed of in the smallest scope possible (ideally with a using statement). It should never be reused for separate requests/threads and is not thread-safe. There's no advantage to keeping a long-lived context. It brings nothing but problems (including what will appear like a memory leak). The cost of querying the DB far outweighs the cost of newing up a context... it's very lightweight to do so. Commented Jun 7, 2017 at 12:21
  • @spender Thank you for giving me your perfect experience. I will keep in mind and try it right now. I was thinking about that would be a problem keeping the dbcontext long lived. I alsa I want to ask you something, why does microsoft IdentityFramework demo project use DbContext inside OwinContext ? and also Is that necessary for identity or it is a mistake ? Commented Jun 7, 2017 at 12:26
  • @mjwills I edited the post, the problem is on the AdminController component. Commented Jun 7, 2017 at 12:27

1 Answer 1

0

I strongly suspect you aren't setting post.ID in your update method. You can verify this by checking the value of post.ID prior to the postRepository.Update(post); call.

I suspect you need to change:

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

to:

public ActionResult UpdatePost([Bind(Include = "ID, IntroText, PostText, CodeText, Header, Author, ImagePath, CategoryID")] ViewPostModel model)
{
Sign up to request clarification or add additional context in comments.

1 Comment

Woooooow. How can I forget this. Thank you so much man. It worked right now. Everything because of mapper :). I am using it for the first time. So problem has been solved by you guys helps. Thank you everyone helpful.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.