Insert/Update 多对多通用存储库
Insert/Update many to many with Generic Repository
我在通过通用存储库插入和更新时遇到问题,在通用插入或更新中没有问题,但在多对多关系中我在插入时遇到错误:
实体对象不能被 IEntityChangeTracker 的多个实例引用。
更新:
无法定义两个对象之间的关系,因为它们附加到不同的 ObjectContext 对象。
我的代码是
界面
public interface IGenericRepository<TEntity>:IDisposable
{
void Insert(TEntity entity);
void Update(TEntity entity);
}
通用 class
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
private ApplicationDbContext context=null;
private DbSet<TEntity> dbSet=null;
public GenericRepository()
{
this.context = new ApplicationDbContext();
this.dbSet = context.Set<TEntity>();
}
public GenericRepository(ApplicationDbContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
public virtual void Insert(TEntity entity)
{
Error is here---> this.context.Set<TEntity>().Add(entity);
// dbSet.Add(entity);
context.SaveChanges();
}
public virtual void Update(TEntity entity)
{
Error is here---> dbSet.Attach(entity);
context.Entry(entity).State = EntityState.Modified;
context.SaveChanges();
}
}
控制码为
private IGenericRepository<Blog> _Repository = null;
private IGenericRepository<BlogTag> _RepositoryTag = null;
private IGenericRepository<BlogCategory> _RepositoryCategory = null;
public BlogsController()
{
this._Repository = new GenericRepository<Blog>(new DbContext());
this._RepositoryTag = new GenericRepository<BlogTag>(new DbContext());
this._RepositoryCategory = new GenericRepository<BlogCategory>(new DbContext());
}
public async Task<ActionResult> Create([Bind(Include = "BlogID,BlogTitle,BlogContent,VisitCount,Preview")] Blog blog
,string[] SelectedTags,string[] SelectedCategories, HttpPostedFileBase files)
{
if (SelectedTags != null)
{
blog.BlogTags = new List<BlogTag>();
foreach (var tag in SelectedTags)
{
var tagToAdd = _RepositoryTag.GetById(int.Parse(tag));
blog.BlogTags.Add(tagToAdd);
}
}
if (SelectedCategories != null)
{
blog.BlogCategories = new List<BlogCategory>();
foreach (var cat in SelectedCategories)
{
var catToAdd = _RepositoryCategory.GetById(int.Parse(cat));
blog.BlogCategories.Add(catToAdd);
}
}
if (ModelState.IsValid)
{
blog.DateTimeInsert = DateTime.UtcNow;
blog.DateTimeModify = DateTime.UtcNow;
blog.ImagePath= files != null ? Path.GetFileName(files.FileName) : "";
blog.BlogContent = HttpUtility.HtmlEncode(blog.BlogContent);
_Repository.Insert(blog);
return RedirectToAction("Index");
}
ViewBag.BlogTags = new SelectList(_RepositoryTag.Get(), "BlogTagID", "TagName");
ViewBag.BlogCategories = new SelectList(_RepositoryCategory.Get(), "BlogCategoryID", "CategoriesName");
return View(blog);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "BlogID,BlogTitle,BlogContent,VisitCount,Preview")] Blog blog
, string[] SelectedTags, string[] SelectedCategories, HttpPostedFileBase files)
{
if (Request["BlogID"] == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
int id = int.Parse(Request["BlogID"].ToString());
var blogsToUpdate = _Repository.Query(i => i.BlogID == id, null).Include(t => t.BlogTags).Include(t => t.BlogCategories).Single();
if (TryUpdateModel(blogsToUpdate, "",
new string[] { "BlogID", "BlogTitle", "BlogContent", "VisitCount","Preview" }))
{
try
{
UpdateInstructorCourses(SelectedTags, SelectedCategories, blogsToUpdate);
blogsToUpdate.DateTimeModify = DateTime.UtcNow;
blogsToUpdate.DateTimeInsert = DateTime.UtcNow;
blogsToUpdate.BlogContent = HttpUtility.HtmlEncode(blogsToUpdate.BlogContent);
await _Repository.UpdateAsync(blogsToUpdate, d => d.BlogTitle, d => d.VisitCount, d => d.BlogContent, d => d.ImagePath);
return RedirectToAction("Index");
}
catch (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
}
AssignedDDLCHKBoxValues(blogsToUpdate);
return View(blogsToUpdate);
}
private void UpdateInstructorCourses(string[] SelectedTags, string[] SelectedCategories, Blog blogsToUpdate)
{
if (SelectedTags == null)
{
blogsToUpdate.BlogTags = new List<BlogTag>();
return;
}
if (SelectedCategories == null)
{
blogsToUpdate.BlogCategories = new List<BlogCategory>();
return;
}
var SelectedTagsHS = new HashSet<string>(SelectedTags);
var SelectedCategoriesHS = new HashSet<string>(SelectedCategories);
var blogTags = new HashSet<int>(blogsToUpdate.BlogTags.Select(c => c.BlogTagID));
foreach (var tag in _RepositoryTag.Get())
{
if (SelectedTagsHS.Contains(tag.BlogTagID.ToString()))
{
if (!blogTags.Contains(tag.BlogTagID))
{
blogsToUpdate.BlogTags.Add(tag);
}
}//if
else
{
if (blogTags.Contains(tag.BlogTagID))
{
blogsToUpdate.BlogTags.Remove(tag);
}
}//else
}//foreach tag
var blogcategories = new HashSet<int>
(blogsToUpdate.BlogCategories.Select(c => c.BlogCategoryID));
foreach (var Category in _RepositoryCategory.Get())
{
if (SelectedCategoriesHS.Contains(Category.BlogCategoryID.ToString()))
{
if (!blogcategories.Contains(Category.BlogCategoryID))
{
blogsToUpdate.BlogCategories.Add(Category);
}
}//if
else
{
if (blogcategories.Contains(Category.BlogCategoryID))
{
blogsToUpdate.BlogCategories.Remove(Category);
}
}//else
}//foreach skill
}
您的问题是您使用多个上下文来处理单个实体。
在您的控制器构造函数中,您有这些行:
this._Repository = new GenericRepository<Blog>(new DbContext());
this._RepositoryTag = new GenericRepository<BlogTag>(new DbContext());
this._RepositoryCategory = new GenericRepository<BlogCategory>(new DbContext());
在这里,您正在创建 3 个应该协同工作的存储库 9) 以及 3 个不同的上下文。
之后,您继续从 RepositoryTag
存储库中读取,此处:
var tagToAdd = _RepositoryTag.GetById(int.Parse(tag));
执行此操作时,对象 tagToAdd
会附加到 RepositoryTag
中的上下文。如果您在添加此 tagToAdd
的位置调试列表 BlogTags
,您将看到您有一个动态代理,这意味着该 objetc 已附加到上下文。
之后,您使用另一个上下文来填充存储库类别,此处:
var catToAdd = _RepositoryCategory.GetById(int.Parse(cat));
blog.BlogCategories.Add(catToAdd);
现在,您的 blog
对象引用了 2 个不同的上下文:一个用于加载标签 (RepositoryTag
),另一个用于加载博客类别 (RepositoryCategory
).
最后,您尝试插入 blog
usgin 第三个上下文:
_Repository.Insert(blog);
这将引发异常,因为 EF 不能像这样处理多个上下文。
要解决这个问题,只需在存储库之前实例化一个上下文,并将其传递给所有存储库,如下所示:
this.context = new DbContext(); // The context you need to use for all operations you are performing here.
this._Repository = new GenericRepository<Blog>(this.context);
this._RepositoryTag = new GenericRepository<BlogTag>(this.context);
this._RepositoryCategory = new GenericRepository<BlogCategory>(this.context);
现在,不要忘记您应该处理您的上下文。这就是为什么最推荐和最常用的方法是使用这样的代码:
using (var ctx = new DbContext()) {
var repo = new GenericRepository<Blog>(ctx);
var repoTag = new GenericRepository<BlogTag>(ctx);
var repoCategory = new GenericRepository<BlogCategory>(ctx);
<the rest of your code where you build the `blog` object>
ctx.SaveChanges();
}
我在通过通用存储库插入和更新时遇到问题,在通用插入或更新中没有问题,但在多对多关系中我在插入时遇到错误:
实体对象不能被 IEntityChangeTracker 的多个实例引用。
更新:
无法定义两个对象之间的关系,因为它们附加到不同的 ObjectContext 对象。
我的代码是
界面
public interface IGenericRepository<TEntity>:IDisposable
{
void Insert(TEntity entity);
void Update(TEntity entity);
}
通用 class
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
private ApplicationDbContext context=null;
private DbSet<TEntity> dbSet=null;
public GenericRepository()
{
this.context = new ApplicationDbContext();
this.dbSet = context.Set<TEntity>();
}
public GenericRepository(ApplicationDbContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
public virtual void Insert(TEntity entity)
{
Error is here---> this.context.Set<TEntity>().Add(entity);
// dbSet.Add(entity);
context.SaveChanges();
}
public virtual void Update(TEntity entity)
{
Error is here---> dbSet.Attach(entity);
context.Entry(entity).State = EntityState.Modified;
context.SaveChanges();
}
}
控制码为
private IGenericRepository<Blog> _Repository = null;
private IGenericRepository<BlogTag> _RepositoryTag = null;
private IGenericRepository<BlogCategory> _RepositoryCategory = null;
public BlogsController()
{
this._Repository = new GenericRepository<Blog>(new DbContext());
this._RepositoryTag = new GenericRepository<BlogTag>(new DbContext());
this._RepositoryCategory = new GenericRepository<BlogCategory>(new DbContext());
}
public async Task<ActionResult> Create([Bind(Include = "BlogID,BlogTitle,BlogContent,VisitCount,Preview")] Blog blog
,string[] SelectedTags,string[] SelectedCategories, HttpPostedFileBase files)
{
if (SelectedTags != null)
{
blog.BlogTags = new List<BlogTag>();
foreach (var tag in SelectedTags)
{
var tagToAdd = _RepositoryTag.GetById(int.Parse(tag));
blog.BlogTags.Add(tagToAdd);
}
}
if (SelectedCategories != null)
{
blog.BlogCategories = new List<BlogCategory>();
foreach (var cat in SelectedCategories)
{
var catToAdd = _RepositoryCategory.GetById(int.Parse(cat));
blog.BlogCategories.Add(catToAdd);
}
}
if (ModelState.IsValid)
{
blog.DateTimeInsert = DateTime.UtcNow;
blog.DateTimeModify = DateTime.UtcNow;
blog.ImagePath= files != null ? Path.GetFileName(files.FileName) : "";
blog.BlogContent = HttpUtility.HtmlEncode(blog.BlogContent);
_Repository.Insert(blog);
return RedirectToAction("Index");
}
ViewBag.BlogTags = new SelectList(_RepositoryTag.Get(), "BlogTagID", "TagName");
ViewBag.BlogCategories = new SelectList(_RepositoryCategory.Get(), "BlogCategoryID", "CategoriesName");
return View(blog);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "BlogID,BlogTitle,BlogContent,VisitCount,Preview")] Blog blog
, string[] SelectedTags, string[] SelectedCategories, HttpPostedFileBase files)
{
if (Request["BlogID"] == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
int id = int.Parse(Request["BlogID"].ToString());
var blogsToUpdate = _Repository.Query(i => i.BlogID == id, null).Include(t => t.BlogTags).Include(t => t.BlogCategories).Single();
if (TryUpdateModel(blogsToUpdate, "",
new string[] { "BlogID", "BlogTitle", "BlogContent", "VisitCount","Preview" }))
{
try
{
UpdateInstructorCourses(SelectedTags, SelectedCategories, blogsToUpdate);
blogsToUpdate.DateTimeModify = DateTime.UtcNow;
blogsToUpdate.DateTimeInsert = DateTime.UtcNow;
blogsToUpdate.BlogContent = HttpUtility.HtmlEncode(blogsToUpdate.BlogContent);
await _Repository.UpdateAsync(blogsToUpdate, d => d.BlogTitle, d => d.VisitCount, d => d.BlogContent, d => d.ImagePath);
return RedirectToAction("Index");
}
catch (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
}
AssignedDDLCHKBoxValues(blogsToUpdate);
return View(blogsToUpdate);
}
private void UpdateInstructorCourses(string[] SelectedTags, string[] SelectedCategories, Blog blogsToUpdate)
{
if (SelectedTags == null)
{
blogsToUpdate.BlogTags = new List<BlogTag>();
return;
}
if (SelectedCategories == null)
{
blogsToUpdate.BlogCategories = new List<BlogCategory>();
return;
}
var SelectedTagsHS = new HashSet<string>(SelectedTags);
var SelectedCategoriesHS = new HashSet<string>(SelectedCategories);
var blogTags = new HashSet<int>(blogsToUpdate.BlogTags.Select(c => c.BlogTagID));
foreach (var tag in _RepositoryTag.Get())
{
if (SelectedTagsHS.Contains(tag.BlogTagID.ToString()))
{
if (!blogTags.Contains(tag.BlogTagID))
{
blogsToUpdate.BlogTags.Add(tag);
}
}//if
else
{
if (blogTags.Contains(tag.BlogTagID))
{
blogsToUpdate.BlogTags.Remove(tag);
}
}//else
}//foreach tag
var blogcategories = new HashSet<int>
(blogsToUpdate.BlogCategories.Select(c => c.BlogCategoryID));
foreach (var Category in _RepositoryCategory.Get())
{
if (SelectedCategoriesHS.Contains(Category.BlogCategoryID.ToString()))
{
if (!blogcategories.Contains(Category.BlogCategoryID))
{
blogsToUpdate.BlogCategories.Add(Category);
}
}//if
else
{
if (blogcategories.Contains(Category.BlogCategoryID))
{
blogsToUpdate.BlogCategories.Remove(Category);
}
}//else
}//foreach skill
}
您的问题是您使用多个上下文来处理单个实体。 在您的控制器构造函数中,您有这些行:
this._Repository = new GenericRepository<Blog>(new DbContext());
this._RepositoryTag = new GenericRepository<BlogTag>(new DbContext());
this._RepositoryCategory = new GenericRepository<BlogCategory>(new DbContext());
在这里,您正在创建 3 个应该协同工作的存储库 9) 以及 3 个不同的上下文。
之后,您继续从 RepositoryTag
存储库中读取,此处:
var tagToAdd = _RepositoryTag.GetById(int.Parse(tag));
执行此操作时,对象 tagToAdd
会附加到 RepositoryTag
中的上下文。如果您在添加此 tagToAdd
的位置调试列表 BlogTags
,您将看到您有一个动态代理,这意味着该 objetc 已附加到上下文。
之后,您使用另一个上下文来填充存储库类别,此处:
var catToAdd = _RepositoryCategory.GetById(int.Parse(cat));
blog.BlogCategories.Add(catToAdd);
现在,您的 blog
对象引用了 2 个不同的上下文:一个用于加载标签 (RepositoryTag
),另一个用于加载博客类别 (RepositoryCategory
).
最后,您尝试插入 blog
usgin 第三个上下文:
_Repository.Insert(blog);
这将引发异常,因为 EF 不能像这样处理多个上下文。
要解决这个问题,只需在存储库之前实例化一个上下文,并将其传递给所有存储库,如下所示:
this.context = new DbContext(); // The context you need to use for all operations you are performing here.
this._Repository = new GenericRepository<Blog>(this.context);
this._RepositoryTag = new GenericRepository<BlogTag>(this.context);
this._RepositoryCategory = new GenericRepository<BlogCategory>(this.context);
现在,不要忘记您应该处理您的上下文。这就是为什么最推荐和最常用的方法是使用这样的代码:
using (var ctx = new DbContext()) {
var repo = new GenericRepository<Blog>(ctx);
var repoTag = new GenericRepository<BlogTag>(ctx);
var repoCategory = new GenericRepository<BlogCategory>(ctx);
<the rest of your code where you build the `blog` object>
ctx.SaveChanges();
}