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