关于导航属性的几点

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

记一次惨痛的EF报错 When attaching existing entities, ensure that only one entity instance with a given key value is attached

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() 方法。

echarts的数据模型生成 C# 用户组织架构图

用户上下级关系、层级关系全部循环到一个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; }
    }

 

Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded

程序报错,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多对多

报错的可能原因可能是EF的映射关系出了问题,如果Class之间有关联,需要手动设置好表关系
如:一个User有多个Device,一个Device可能属于多个User

public partial class User
{
  [Key]
  public int ID { get; set; }
  public string UserName { get; set; }
  public virtual ICollection UserDeivces { 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);
}