忽略上下文更新

时间:2016-09-26 21:10:58

标签: c# .net entity-framework null entity-framework-5

我有一个大型模型,已经通过反序列化进行了部分更新。因为它只是部分更新,所以当我将它传递给我的实体框架更新时,我想忽略任何空值。最终EntityState.Modified已设置,但我遇到的问题是所有字段都已更新。这意味着任何null都会在数据库中消隐 是否可以通过设置更改此默认行为或覆盖方法以检查null?似乎由于上下文期待完整的模型,我不能简单地设置几个值 我通过仅映射我需要修改的内容并发生相同的行为来验证这一点。

3 个答案:

答案 0 :(得分:1)

您可以实现类似这样的功能。

在这种情况下,我使用具有反射功能的通用存储库来遍历属性并在更新方法中排除空值。

public virtual TEntity Update(TEntity entity)
{
    dbSet.Attach(entity);
    dbContext.Entry(entity).State = EntityState.Modified;

    var entry = dbContext.Entry(entity);

    Type type = typeof(TEntity);
    PropertyInfo[] properties = type.GetProperties();
    foreach (PropertyInfo property in properties)
    {
        if (property.GetValue(entity, null) == null)
        {
            entry.Property(property.Name).IsModified = false;
        }
    }

    dbContext.SaveChanges();
    return entity;
}

答案 1 :(得分:0)

这是一个我必须解决的有点乏味的问题。

由于缺乏更直接的解决方案,最终我决定我不想尝试解决下游问题,就像EF的SaveChanges-and-the-likes'被调用一样(即,不需要“挂钩”) EF这么晚了,但是上游/早些时候要高得多 -

就是说,在我获得令人满意的反序列化之后立即这样做,这对于改变模型是有意义的,基于每个实体的实例(在我的用例中,没有可更新的属性表示关系,但只是独立的E / R用语中的属性 - YMMV)

所以,我选择了一个“填充”助手,按照以下方式:

    static void Populate(object from, object to)
    {
        var sourceType = from.GetType();
        foreach (PropertyInfo target in to.GetType().GetProperties())
        {
            // Is the property at the target object writable and *not* marked
            // as `[NotMapped]'?
            var isUpdatable =
                target.CanWrite &&
                (target.GetCustomAttribute<NotMappedAttribute>(true) == null);
            if (isUpdatable)
            {
                // If so, just find the corresp. property with the same name at the source object
                // (caller is assumed responsible to guarantee that there is one, and of the same type, here)
                var source = sourceType.GetProperty(target.Name);

                var @default = sourceType.IsValueType ? Activator.CreateInstance(sourceType) : null;
                var equality = (IEqualityComparer)typeof(EqualityComparer<>).MakeGenericType(sourceType).GetProperty("Default", BindingFlags.Public | BindingFlags.Static).GetValue(null);
                var value = source.GetValue(from);

                // Test for <property value> != default(<property type>)
                // (as we don't want to lose information on the target because of a "null" (or "default(...)") coming from the source)
                if (!equality.Equals(value, @default))
                {
                    target.SetValue(to, value, null);
                }
            }
        }
    }

其中“from”是刚刚被任何反序列化代码部分填充的新实体实例,其中“to”是生活在DbContext中的实际目标实体(无论是否为EF代理);

并且NotMappedAttribute是EF常用的地方。

您通常会在反序列化(&amp; /或DTO映射)到“from”实例完成后的某个时间调用Populate,但无论如何在SaveChanges()调用DbContext之前调用所有“对于“实体 - 显然,我们假设有一个可行的1对1映射”,从“......”到“,Populate的调用者知道/可以找出。

注意我仍然不知道是否有更优雅(更直接)的方式来做到这一点,而无需借助反思 - 所以,那里,FWIW。

说明

1)根据来电者的假设,上述代码可以(或应该)以各种方式更具防御性;

2)人们可能想要缓存那些IEqualityComparer(&amp; /或PropertyInfo),因为可能出现的任何(好的)原因 - 在我的情况下,我没有必要;

3)最后,我的理解是,AutoMapper等第三方图书馆也是专为此类任务而设计的,如果你能负担得起额外的依赖

“HTH,

答案 2 :(得分:0)

 public IHttpActionResult PutProduct(int id, Product product)
    {
        NorthwindEntities db = new NorthwindEntities();
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        db.Products.Attach(product);

        // Only the fields you want to update, will change.
        db.Entry(product).Property(p => p.ProductName).IsModified = true;
        db.Entry(product).Property(p => p.UnitPrice).IsModified = true;
        db.Entry(product).Property(p => p.UnitsInStock).IsModified = true;

        //  only if if the value is not null, the field will change.
        db.Entry(product).Property(p => p.UnitsOnOrder).IsModified = 
        product.UnitsOnOrder != null;

        db.SaveChanges();


        return Ok(product);
    }