我正在使用MVC5,EntityFramework,Unity,UnitOfWork和通用存储库来开发我的项目体系结构之一,但我还没有使用过AutoMapper或其他类似于AutoMapper的东西。
每当我第一次执行更新时,它都能很好地工作,但是第二次以后,它给我以下错误。
附加类型为'EntityModel.tblCompanyMaster'的实体失败,因为相同类型的另一个实体已经具有相同的主键值。如果图形中的任何实体具有相互冲突的键值,则使用“附加”方法或将实体的状态设置为“不变”或“修改”时,可能会发生这种情况。这可能是因为某些实体是新实体,尚未收到数据库生成的键值。在这种情况下,请使用“添加”方法或“已添加”实体状态来跟踪图形,然后根据需要将非新实体的状态设置为“未更改”或“已修改”。
更新方法在此处[_dbSet.Attach(entity)]
发生错误 public virtual void UpdateEntity(TEntity entity, string[] NoUpdateProperty = null)
{
_dbSet.Attach(entity);
_dbContext.Entry<TEntity>(entity).State = EntityState.Modified;
.
.
}
这是我的代码:
1)团结
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
//container.RegisterType<DbContext, dbTestCMSEntities>();
container.RegisterSingleton<IUnitOfWork, UnitOfWork>();
container.RegisterSingleton(typeof(IDbHelper<>), typeof(DbHelper<>));
container.RegisterSingleton<ICompanyMasterBL, tblCompanyMasterBL>();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
}
2)工作单位
public interface IUnitOfWork
{
dbTestCMSEntities dbContext { get; }
void Save();
}
public class UnitOfWork : IUnitOfWork, IDisposable
{
public dbTestCMSEntities dbContext { get; }
private bool _disposed = false;
public UnitOfWork(dbTestCMSEntities context)
{
dbContext = context;
}
public void Save()
{
try
{
dbContext.SaveChanges();
}
catch (Exception ex)
{
throw ex;
}
}
protected virtual void Dispose(bool disposing)
{
if (!this._disposed)
{
if (disposing)
{
dbContext.Dispose();
}
}
this._disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
3)通用存储库
public interface IDbHelper<TEntity> where TEntity : class
{
bool Exists(object pkId);
TEntity GetFirst(Expression<Func<TEntity, bool>> condition, Expression<Func<TEntity, dynamic>> order = null);
IEnumerable<TEntity> GetMany(Expression<Func<TEntity, bool>> condition, Expression<Func<TEntity, string>> order = null);
IEnumerable<TEntity> GetPagedList(Expression<Func<TEntity, bool>> condition, Expression<Func<TEntity, Int64>> _order, int currentPageIndex, out int Total);
void InsertEntity(TEntity entity);
void InsertEntity(List<TEntity> entity);
void UpdateEntity(TEntity entity, string[] NoUpdateProperty = null);
void UpdateEntity(List<TEntity> entity, string[] NoUpdateProperty = null);
void Delete(object id);
void Delete(TEntity entity);
void DeleteEntity(Expression<Func<TEntity, bool>> condition);
}
public class DbHelper<TEntity> : IDbHelper<TEntity> where TEntity : class
{
protected readonly dbTestCMSEntities _dbContext;
protected DbSet<TEntity> _dbSet;
public DbHelper(IUnitOfWork unitOfWork)
{
_dbContext = unitOfWork.dbContext;
_dbSet = _dbContext.Set<TEntity>();
}
public bool Exists(object pkId)
{
return _dbSet.Find(pkId) != null;
}
public TEntity GetFirst(Expression<Func<TEntity, bool>> condition, Expression<Func<TEntity, dynamic>> order = null)
{
IQueryable<TEntity> lstResult = _dbSet.AsNoTracking().AsQueryable();
if (order != null)
lstResult = lstResult.AsNoTracking().OrderBy(order).AsQueryable();
if (condition != null)
lstResult = lstResult.AsNoTracking().Where(condition).AsQueryable();
TEntity entity = lstResult.AsNoTracking().FirstOrDefault();
return entity;
}
public virtual IEnumerable<TEntity> GetMany(Expression<Func<TEntity, bool>> condition, Expression<Func<TEntity, string>> order = null)
{
IQueryable<TEntity> lstResult = _dbSet.AsNoTracking().AsQueryable();
if (condition != null)
lstResult = lstResult.AsNoTracking().Where(condition).AsQueryable();
if (order != null)
lstResult = lstResult.AsNoTracking().OrderBy(order).AsQueryable();
return lstResult;
}
public virtual IEnumerable<TEntity> GetPagedList(Expression<Func<TEntity, bool>> condition, Expression<Func<TEntity, Int64>> _order, int currentPageIndex, out int Total)
{
IQueryable<TEntity> lstResult = _dbSet.AsNoTracking().AsQueryable();
if (condition != null)
lstResult = lstResult.AsNoTracking().Where(condition).AsQueryable();
Total = lstResult.Count();
lstResult = lstResult.AsNoTracking().OrderByDescending(_order).AsQueryable().Skip((currentPageIndex - 1) * StringUtility.ItemsPerPage).Take(StringUtility.ItemsPerPage);
return lstResult;
}
/// <summary>
/// Insert single record
/// </summary>
/// <param name="entity"></param>
public virtual void InsertEntity(TEntity entity)
{
_dbSet.Add(entity);
}
/// <summary>
/// Insert multiple records
/// </summary>
/// <param name="entity"></param>
public virtual void InsertEntity(List<TEntity> entity)
{
_dbSet.AddRange(entity);
}
/// <summary>
/// Update single entity
/// </summary>
/// <param name="entity"></param>
/// <param name="NoUpdateProperty"></param>
public virtual void UpdateEntity(TEntity entity, string[] NoUpdateProperty = null)
{
_dbSet.Attach(entity);
_dbContext.Entry<TEntity>(entity).State = EntityState.Modified;
if (NoUpdateProperty != null)
{
foreach (string item in NoUpdateProperty)
{
_dbContext.Entry<TEntity>(entity).Property(item).IsModified = false;
}
}
}
/// <summary>
/// Update multiple entity
/// </summary>
/// <param name="entity"></param>
/// <param name="NoUpdateProperty"></param>
public virtual void UpdateEntity(List<TEntity> entity, string[] NoUpdateProperty = null)
{
foreach (TEntity item in entity)
{
_dbSet.Attach(item);
_dbContext.Entry<TEntity>(item).State = EntityState.Modified;
if (NoUpdateProperty != null)
{
foreach (string item1 in NoUpdateProperty)
{
_dbContext.Entry<TEntity>(item).Property(item1).IsModified = false;
}
}
}
}
/// <summary>
/// Generic Delete method for the entities. Delete one record only.
/// </summary>
/// <param name="id"></param>
public virtual void Delete(object id)
{
TEntity entityToDelete = _dbSet.Find(id);
Delete(entityToDelete);
}
/// <summary>
/// Generic Delete method for the entities. Delete one record only.
/// </summary>
/// <param name="entityToDelete"></param>
public virtual void Delete(TEntity entity)
{
if (_dbContext.Entry(entity).State == EntityState.Detached)
{
_dbSet.Attach(entity);
}
_dbSet.Remove(entity);
}
/// <summary>
/// Delete one or many records based on given condition
/// </summary>
/// <param name="condition"></param>
public void DeleteEntity(Expression<Func<TEntity, bool>> condition)
{
_dbSet.RemoveRange(_dbSet.Where(condition));
}
}
我已经尝试了很多方法,但是并没有帮助我,我相信我在Unity和Unit of Work方面做错了,但是很难确定。预先感谢您的帮助。
答案 0 :(得分:0)
错误非常明显。以下是来自here的引用:
将给定实体附加到集合下的上下文。也就是说,将实体以未更改状态放置到上下文中,就像从数据库中读取它一样。
.....
.....
如果实体已经在上下文中处于“未更改”状态,则附加操作为空操作。
当您第一次致电_dbSet.Attach(entity);
时,与您分离的entity
成为DbSet
的一部分;即它被放置在上下文中。该实体还具有用于标识实体的唯一标识符(主键值)。
现在,当您第二次调用它时,标识符/密钥是相同的。一个标识符DbSet
中不能存在两个具有相同标识符的实体。
您应在致电Attach
之前检查该实体是否已存在。
在致电_dbSet.Find(key)
之前使用Attach
。如果已经存在,请不要调用Attach
。 This可能会有所帮助。
这不是问题的一部分,但是我想您更新记录的方式不正确。如果要更新记录,更好的方法是从数据库中获取(Find
/ SingleOrDefault
)=>修改要更改的属性=>刷新(调用{{1 }}) 变化。请参考this问题。
答案 1 :(得分:0)
每当我第一次执行更新时,它都能很好地工作,但是第二次以后,它给我以下错误。
可能您正在尝试将DbContext实例用于多个请求。您的DI容器似乎正在将事物注册为单例。 DbContext应该根据请求确定范围。
答案 2 :(得分:0)
下面的代码可以帮助我找到解决方案,只需在更新时在Attach()之前调用此方法即可
CREATE TRIGGER trig_update_sma
AFTER INSERT ON symbol_daily_ohlc
FOR EACH ROW
EXECUTE PROCEDURE update_sma8();