zoukankan      html  css  js  c++  java
  • EF 带外键关系更新实体问题,求大神康康!!(Solved)

    EF 带外键关系更新实体问题

    2020.08.17更新

    在国外论坛发现了这个问题,可以通过使用GraphDiff解决:

    问题链接:https://stackoverflow.com/questions/9606866/cannot-get-relationship-to-update-for-navigation-properties-in-entity-framework/37493263#37493263

    基本上发生这种情况是因为EntryState.Modified仅查找标量属性(原始类型)

    Install-Package RefactorThis.GraphDiff
    
    using (var context = new Context())
    {
        var customer = new Customer()
        {
            Id = 12503,
            Name = "Jhon Doe",
            City = new City() { Id = 8, Name = "abc" }
        };
    
        context.UpdateGraph(customer, map => map.AssociatedEntity(p => p.City));
        context.Configuration.AutoDetectChangesEnabled = true;
    
        context.SaveChanges();
    }
    

    使用EF遇到了个问题,记录一下:

    场景类型如下,这一个表有两个其他表实例:

     public class Sample : Model
        {
         ....
             public SampleReference Reference{get;set;}
        	 public SampleBase Base{get;set;}
     	}
    

    数据上下文设置:

      public MyDBContext()
                : base("name=Sqlite")
            {
                this.Configuration.ProxyCreationEnabled = true;
                this.Configuration.LazyLoadingEnabled = false;
            }
    

    查询:

     public List<M> GetAll(Expression<Func<M, bool>> predicate, bool isTrack, params Expression<Func<M, dynamic>>[] expressions)
            {
                using (MyDBContext myDBContext = new MyDBContext())
                {
                    IQueryable<M> qt = myDBContext.Set<M>();
                    if (expressions != null && expressions.Any())
                        foreach (var exp in expressions)
                            qt = qt.Include(exp);
                    if (!isTrack) qt = qt.AsNoTracking();
                    if (predicate != null) qt = qt.Where(predicate);
                    return qt.ToList();
                }
            }
    

    执行查询:

     var list = _modelHelper.GetAll(t => t.Sample.Base, t => t.Spectrogram, t => t.TestingInfomation);
    

    OK ,查询到一些实例,我对他们进行了一些修改,现在我想要Update他们:

    (再次说明一下我对实例怎么修改了,修改后多个实例Sample有着共同的实例Reference)

    最开始用的是EF提供的AddorUpdate扩展方法:

     myDBContext.Set<Sample>().AddOrUpdate(modelList.ToArray());
    

    报错重复主键,经查询此方法很不智能,是暴力的,你的所有表实例要不都得是新的,要不都得是modify状态(all modifed有待商榷)

    之后采用改变实体state方法

     myDBContext.Entry(model).State = EntityState.Modified;
    

    问题来了,Sample Entity只能改变Sample表的状态,对于之前include的Sample.SampleBase的改变没有提交效果。

    最终我采用了以下方法,很傻很笨:

     public static void AddorUpdate(this Sample model, DbContext myDBContext)
            {
                model.Base?.AddorUpdate(myDBContext);
                model.Reference?.AddorUpdate(myDBContext);
    
                if (myDBContext.Set<Sample>().AsNoTracking().FirstOrDefault(t => t.ID == model.ID) != null)
                {
                    myDBContext.Entry(model).State = EntityState.Modified;
                }
                else
                {
                    myDBContext.Entry(model).State = EntityState.Added;
                }
            }
    
      public static void AddorUpdate(this SampleBase model, DbContext myDBContext)
            {
                model.Customer?.AddorUpdate(myDBContext);
                if (myDBContext.Set<SampleBase>().AsNoTracking().FirstOrDefault(t => t.ID == model.ID) != null)
                {
                    myDBContext.Entry(model).State = EntityState.Modified;
                }
                else
                {
                    myDBContext.Entry(model).State = EntityState.Added;
                }
            }
    
      public static void AddorUpdate(this SampleReference model, DbContext myDBContext)
            {
                if (myDBContext.Set<SampleReference>().AsNoTracking().FirstOrDefault(t => t.ID == model.ID) != null)
                {
                    myDBContext.Entry(model).State = EntityState.Modified;
                }
                else
                {
                    myDBContext.Entry(model).State = EntityState.Added;
                }
            }
    

    显示的去一个一个调,代码写死。

    很奇怪,我记得原来用好像都可以自己去追踪更改,不用这么费劲啊,我是哪里做错了呢?


    更令人气愤的是,这种写法还跟顺序有关,下面这种写法就不行,要先处理关联表,再处理主表

    public static void AddorUpdate(this Sample model, DbContext myDBContext)
            {
                if (myDBContext.Set<Sample>().AsNoTracking().FirstOrDefault(t => t.ID == model.ID) != null)
                {
                    myDBContext.Entry(model).State = EntityState.Modified;
                }
                else
                {
                    myDBContext.Entry(model).State = EntityState.Added;
                }
                    
        		model.Base?.AddorUpdate(myDBContext);
                model.Reference?.AddorUpdate(myDBContext);
    
            }
    

    此时的Sql,没保存,类似于这样:

    UPDATE [Sample] xxxxxxxxxxxxx
    WHERE ([ID] = @p0) AND SampleBases_ID IS NULL AND Reference_ID IS NULL;
    
    -- @p0: '20200814151039694' (Type = String)
    
    -- @p1: '310946eb64ec4515a10515f6481aaf90' (Type = String)
    
    -- @p2: 'b90f94bc13f448798530bf2f20a9e837' (Type = String)
    
    -- @p3: '0' (Type = Double)
    
    -- @p4: '9fc574f6518b4e3893c47371c64d9ec2' (Type = String)
    
    -- Executing at 2020/8/14 15:11:10 +08:00
    
    -- Completed in 0 ms with result: 1
    

    AND SampleBases_ID IS NULL AND Reference_ID IS NULL!!!!!!!!

    是按照顺序执行的sql,你不先写这两个关联标的modified,他认为是null的查找条件!

  • 相关阅读:
    ios NSString format 保留小数点 float double
    IOS中延时执行的几种方式的比较和汇总
    ioss使用xcode常用快捷键
    iphone 6plus 下app里的状态栏和界面会被放大的问题//以及设置APP闪屏页/APP图标流程
    iostbleView刷新后显示指定cell
    iOS-打包成ipa的4种方法
    iosttableViewCell右侧的箭头,圆形等
    Linux学习之CentOS(二十)------vi/vim 按键说明
    gzip
    bzip2
  • 原文地址:https://www.cnblogs.com/swobble/p/13503161.html
Copyright © 2011-2022 走看看