var context = _context.Feedback.OrderByDescending(a=>a.AddTime);
[NotMapped]
Public User User{get;set;}
此时,_context.Feedback.Include(“User”),无法获取User
如果不加[NotMapped],以上代码获取到不为null的User,比如UserId=0,此时整条记录将不再显示。
asp.net core
var context = _context.Feedback.OrderByDescending(a=>a.AddTime);
[NotMapped]
Public User User{get;set;}
此时,_context.Feedback.Include(“User”),无法获取User
如果不加[NotMapped],以上代码获取到不为null的User,比如UserId=0,此时整条记录将不再显示。
asp.net core
InvalidOperationException: The instance of entity type ‘Category’ cannot be tracked because another instance with the same key value for {‘CategoryID’} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using ‘DbContextOptionsBuilder.EnableSensitiveDataLogging’ to see the conflicting key values.
翻译过来的内容大致是:实体 category不能被跟踪,因为有一个同样key categoryID已经被跟踪。
一、情况描述
一个新项目,用了ASP.net Core MVC开发,ORM使用EF,ASP.net Core 默认使用了依赖注入的概念。很简单的一个功能,就是一个层级的类别管理,在列表、添加页面都没有出现问题,唯独在更新的时候出现了问题,项目使用了两层的仓库结构,使用了默认的依赖注入,使用了unitOfwork,项目结构如下:
二、出现此问题的原因:大致理解为ef跟踪的内容已经不是从初始化上下文跟踪的内容
三、网上查到的解决办法,
1、使用AsNoTracking()不进行跟踪,但是这种方法不能对数据进行更新,只能查询,但是我这里需要更新。
2、更新之前重新查询一次数据然后更新,这个显然有点蛋疼。
四、错误排除方法:
1、是否造成多次实例化context,查询和更新的上下文不是一个,经过对context构造函数的日志输出,确认每次请求只实例化1个context。
2、调试程序,观察当前model的EntityState状态,发现是Detached,Detached的解释:对象存在,但未由对象服务跟踪。在创建实体之后、但将其添加到对象上下文之前,该实体处于此状态,我用了微软asp.net core mvc 示例同样调试状态发现也是Detached,所以这个可以排除。
3、由于用了两层的仓储(BLL,DAL),剥开每一层进行测试,跳过BLL使用DAL测试,跳过BLL,DAL,直接使用context,发现都没有问题,那么问题可能出在BLL层。对BLL的方法一个个测试,发现只要不执行GetTree()方法,问题就不会出现。
这个方法的目的是将类别树取出来,进行排序用于显示,第二个方法做一个递归,有一个全局变量_tree保存处理结果,通过Category._tree调用。
public List _tree = new List(); public void GetTree() { var list = _repository.GetList().ToList(); GetTree(list, 0, 0); } public void GetTree(List<Models.Category> list, int parentID, int level) { var tempList = list.Where(a => a.ParentID == parentID).OrderBy(a => a.Sort); if (tempList.Count() != 0) { foreach (var item in tempList) { item.Level = level; _tree.Add(item); GetTree(list, item.CategoryID, level + 1); } } }
发现两个问题
第一、一旦var list=_repository.GetList().ToList()就不能更新时候报错,我理解是_repository是引用类型,ToList之后,ef里的实体就会被Tolist,ef的跟踪状态发生了变化。
第二、不用Tolist,改成IQueryable,往下面走,foreach (var item in tempList){},这个方法改成一个空方法依然更新时候报错,我理解,在foreach的时候,IQueryable已经不在表达式了,已经是从数据库里取出了数据,ef的跟踪状态发生了变化,所以报错。
那么GetTree()是什么时候执行的呢,这又是一个错误,为了每次方便取出_tree,我把GetTree(),写到了控制器的构造函数里,造成不管是查询,更新都会先执行一次GetTree()。
五、总结
正常更新的时候是前台传递过来model,后台获取context,更新model到context,这个期间不要动context,不要tolist和foreach操作,虽然EntityState也是Detached,但是不能进行更新。
这个问题差不多一整天都在解决,多么痛的领悟。
2019-04-10
public JsonResult Edit(Models.Attr attr)
{
var dataModel = _attr.Find(attr.AttrId);
if (dataModel.AttrName != attr.AttrName)
{
var count= _attr.Count(a => a.AttrName == attr.AttrName);
if (count > 0)
{
var msg = new Models.PageMsg(false, "属性名重复");
return Json(msg);
}
}
_attr.Update(attr, true);
return Json(new Models.PageMsg(true, "操作成功", "/admin/attr/"));
}
上面代码同样的提示,改为以下代码
public JsonResult Edit(Models.Attr attr)
{
var dataModel = _attr.Find(attr.AttrId);
if (dataModel.AttrName != attr.AttrName)
{
var count= _attr.Count(a => a.AttrName == attr.AttrName);
if (count > 0)
{
var msg = new Models.PageMsg(false, "属性名重复");
return Json(msg);
}
}
dataModel.AttrName = attr.AttrName;
dataModel.AttrType = attr.AttrType;
dataModel.DefaultValue = attr.DefaultValue;
_attr.Update(dataModel, true);
return Json(new Models.PageMsg(true, "操作成功", "/admin/attr/"));
}
Detached:对象存在,但未由对象服务跟踪。在创建实体之后、但将其添加到对象上下文之前,该实体处于此状态;
Unchanged:自对象加载到上下文中后,或自上次调用 System.Data.Objects.ObjectContext.SaveChanges() 方法后,此对象尚未经过修改;
Added:对象已添加到对象上下文,但尚未调用 System.Data.Objects.ObjectContext.SaveChanges() 方法;
Deleted:使用 System.Data.Objects.ObjectContext.DeleteObject(System.Object) 方法从对象上下文中删除了对象;
Modified:对象已更改,但尚未调用 System.Data.Objects.ObjectContext.SaveChanges() 方法。
用户上下级关系、层级关系全部循环到一个Model里面去
public class UserController : ApiController { DAL.EF db = new DAL.EF(); //上级结果只有一个 tree[0],用一个tree[0]存储当前用户以及所有下级的关系 List<Models.User> tree = new List<Models.User>(); //用一个V存储所有下级的echarts模型 V_model V = new V_model(); public string GetTreeJson(int id) { var list = db.User_Repository.dbSet.SqlQuery(@"WITH TREE AS(SELECT * FROM [User] WHERE userid = " + id + " UNION ALL SELECT [User].* FROM [User], TREE WHERE [User].ParentID = TREE.userid )SELECT* FROM TREE ").ToList(); if (list.Count == 0) { return ""; } //初始值 var first = list[0]; V.name = first.TrueName; V.value = first.UserNo; V.children = new List<V_model>(); tree.Add(first); //开始递归 AddSub(list,first,V); //递归完成 return JsonConvert.SerializeObject(V) ;//输出echart 数据对象 //return JsonConvert.SerializeObject(list) ;//输出Models.User对象 } public void AddSub(List<Models.User> all, Models.User curUser,V_model vModel) { List<Models.User> sublist = all.Where(a => a.ParentID == curUser.UserID).ToList(); //得到子节点 curUser.SubUsers = sublist; foreach (var subItem in sublist) { var vv = new V_model { name = subItem.TrueName, value = subItem.UserNo, children = new List<V_model>() }; vModel.children.Add(vv); AddSub(all, subItem,vv); } } }
//Echarts数据对象模型 public class V_model { public string name { get; set; } public string value { get; set; } public List<V_model> children { get; set; } }
public partial class User { [Key] public int UserID { get; set; } ...... public int ParentID{get;set;} [NotMapped] public virtual List<Models.User> SubUsers { get; set; } }
EF报错,Count 必须具有非负值,一般的时候发生在ToList(),由于是linq的skip有问题, 跳过的元素比集合里的元素还要多,其他就自己找吧
程序报错,Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded,一般两种情况
第一、主键不存在
第二、在载入后模型被修改或者删除
我的问题恰好属于第一种,调试后发现Mode.ID为0,原因如下
@Html.HiddenFor(model => model.ID)没有写在Form里,服务器接收到的ID是空值,由于是int类型,自动转换为0
报错的可能原因可能是EF的映射关系出了问题,如果Class之间有关联,需要手动设置好表关系
如:一个User有多个Device,一个Device可能属于多个User
public partial class User { [Key] public int ID { get; set; } public string UserName { get; set; } public virtual ICollectionUserDeivces { get; set; } } public partial class Device { [Key] public int id { get; set; } public string deviceName { get; set; } public virtual ICollection UserDeivces { get; set; } } public class UserDeivce { [Key] public int ID { get; set; } public int UserID { get; set; } public int DeivceID { get; set; } public virtual Model.User User { set; get; } public virtual Model.Device Device { set; get; } } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity () .HasRequired(a => a.Device) .WithMany(a => a.UserDeivces) .HasForeignKey(a => a.DeviceID); modelBuilder.Entity () .HasRequired(a => a.User) .WithMany(a => a.UserDeivces) .HasForeignKey(a => a.UserID); }