EF 带外键关系更新实体问题,求大神康康!!(Solved)

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

2020.09.04更新

使用GraphDiff更新集合,如果集合中某些元素对象也被更改如何通知数据库做出改变?

仅使用 map.AssociatedCollection(p => p.TargetPoints)是不够的,这只能改变TargetPoints的改变,即原来是五个TargetPoints,现在是三个,这可以通知到,而TargetPoint本身的改变是通知不到的,这种情况就需要我们手动通知:

model.TargetPoints.All(t =>
         {
             myDBContext.UpdateGraph(t,
map => map.AssociatedEntity(p => p.StandardData)
);

             return true;
         });

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的查找条件!

posted @ 2020-08-14 16:55  猝不及防  阅读(504)  评论(0编辑  收藏  举报