从更新时调用SaveChanges()被调用排除属性 [英] Exclude property from updating when SaveChanges() is called

查看:503
本文介绍了从更新时调用SaveChanges()被调用排除属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

似乎有两种方法来更新使用连接的方法断开连接的实体框架的实体。

方法一,您只需设置断开实体的状态修改:

  myDbContext.Dogs.Attach(狗);
myDbContext.Entry(狗).STATE = EntityState.Modified;
myDbContext.SaveChanges();

这将保存狗的对象的所有领域。但是,假设你是从一个MVC的Web页面,您只允许Dog.Name的编辑这样做,并包含在页面上唯一的狗属性名称。那么可以做方法二:

  myDbContext.Dogs.Attach(狗);
myDbContext.Entry(狗).Property(O = GT; o.Name).CurrentValue = dog.Name;
myDbContext.Entry(狗).Property(O = GT; o.Name).IsModified = TRUE;
myDbContext.SaveChanges();

方法二可以得到相当冗长的时候有很多的属性来更新。这促使我尝试方法三,设置将IsModified =上我不想改变的属性假的。这不起作用,抛出运行时错误设置将IsModified为false,修改后的财产不被支持

  myDbContext.Dogs.Attach(狗);
myDbContext.Entry(狗).STATE = EntityState.Modified;
myDbContext.Entry(狗).Property(O = GT; o.Owner).IsModified = FALSE;
myDbContext.SaveChanges();

我更preFER使用方法一无处不在,但也有在我的asp.net的MVC视图不包含狗类的每个标量属性许多实例。

我的问题是:


  1. 有我可以在POCO类会告诉我再也不想财产最多更新实体框架使用的属性?例如,[NeverUpdate]。我知道的[NotMapped]属性的,但是这不是我所需要的。

  2. 如果做不到这一点,有没有什么办法,我可以使用方法一以上(myDbContext.Entry(狗).STATE = EntityState.Modified;
    ),并排除我不想要更新的字段?

P.S。我知道的另一种方式,不使用附加,只是从数据库中获取一个新的对象,更新所需的性能,并保存。这就是我在做什么,但我很好奇,如果有使用的方式连接,从而避免了额外的旅行到数据库,但这样做的方式,不那么冗长如上法两种。通过获取一个新的对象,我的意思是:

 狗dbDog = myDbContext.Dogs.FirstOrDefault(D => d.ID = dog.ID);
dbDog.Name = dog.Name;
myDbContext.SaveChanges();


解决方案

下面的<击>可能工作工作

  myDbContext.Dogs.Attach(狗);
myDbContext.Entry(狗).STATE = EntityState.Modified;VAR的ObjectContext =((IObjectContextAdapter)myDbContext).ObjectContext;
的foreach(在objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified)。凡(实体=&GT VAR进入; entity.Entity.GetType()== typeof运算(狗)))
{
    //你需要给外键属性名
    //而不是导航属性名
    entry.RejectPropertyChanges(OWNERID);}myDbContext.SaveChanges();


如果你想要做它在一个单一的线,使用下面的扩展方法:

 公共静态无效DontUpdateProperty&LT; TEntity&GT;(这方面的DbContext,弦乐propertyName的)
{
    变种的ObjectContext =((IObjectContextAdapter)上下文).ObjectContext;
    的foreach(在objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified)。凡(实体=&GT VAR进入; entity.Entity.GetType()== typeof运算(TEntity)已))
    {
        entry.RejectPropertyChanges(propertyName的);
    }
}

和使用它像这样

  //在修改了一些波苏斯
myDbContext.DontUpdateProperty&LT;狗狗和GT(OWNERID);
myDbContext.SaveChanges();

正如你所看到的,你可以修改此解决方案,以满足您的需求,例如,使用的String []属性而不是字符串propertyName的作为参数。<​​/ P>


建议的方法

有一个更好的解决办法是如你所说([NeverUpdate])使用一个属性。为了使它工作,你需要使用SavingChanges事件(检查我的<一个href=\"https://heli$c$cr.word$p$pss.com/2013/07/20/execute-$c$c-before-save-and-after-fetch-in-entity-framework/\"相对=nofollow称号=博客>博客):

 无效ObjectContext_SavingChanges(对象发件人,System.Data.Objects.SavingChangesEventArgs E)
{
    ObjectContext的背景=发件人为ObjectContext的;
    如果(上下文!= NULL)
    {
        的foreach(在context.ObjectStateManager.GetObjectStateEntries ObjectStateEntry条目(EntityState.Modified))
        {
            VAR类型= ty​​peof运算(entry.Entity);
            变种属性= type.GetProperties();
            的foreach(在性能VAR属性)
            {
                VAR属性= property.GetCustomAttributes(typeof运算(NeverUpdateAttribute),FALSE);
                如果(attributes.Length大于0)
                    entry.RejectPropertyChanges(property.Name);
            }
        }
    }
}
//检查如何创建自定义属性Microsoft文档:
// http://msdn.microsoft.com/en-us/library/sw480ze8(v=vs.80).aspx
公共类NeverUpdateAttribute:SystemAttribute
{}//在你的POCO
公共类犬
{
    [NeverUpdate]
    公众诠释OWNERID {搞定;组; }
}

<击>警告:我没有编译这个code。我不在家:/


报警2:我刚才看了<一个href=\"http://msdn.microsoft.com/en-us/library/system.data.objects.objectstateentry.rejectpropertychanges.aspx\"相对=nofollow称号=MSDN文档> MSDN文档和它说:


  

ObjectStateEntry.RejectPropertyChanges法


  
  

拒绝,因为对财产提出给定名称的任何变化
  物业上次加载,,保存或改变被接受。
  该物业的原单值存储和属性将不
  改性再被标记。


<击>
我不知道它的行为将是附加修饰实体的情况下。我会尝试这个明天。


警告3 :我现在已经尝试过。此解决方案。被拒绝与 RejectPropertyChanges属性()方法不更新的持久性单元(数据库)。

,如果是通过调用更新连接实体附加(),在目前的情况下仍然脏后的SaveChanges()。假定下面行中的数据库中存在

 
ID:1
名称:最高
OWNERID:1

考虑以下code:

  VAR myDog =新狗();
myDog.ID = 1;
myDog.Name =阿喀琉斯;
myDog.OwnerID = 2;myDbContext.Dogs.Attach(myDog);
myDbContext.Entry(myDog).STATE = EntityState.Modified;
myDbContext.SaveChanges();

数据库调用SaveChanges()后的当前状态:

 狗:
ID:1
名称:阿基里斯
OWNERID:1

myDbContext的调用SaveChanges后的当前状态():

  VAR OWNERID = myDog.OwnerID; //它是2
VAR状态= myDbContext.Entry(myDog).STATE; //这是不变

所以,你应该怎么办呢?调用SaveChanges()后,请取出它:

 狗myDog =新狗();
//设置属性
...
myDbContext.Dogs.Attach(myDog);
myDbContext.Entry(myDog).STATE = EntityState.Modified;
myDbContext.SaveChanges();
myDbContext.Entry(myDog).STATE = EntityState.Detached;

There appears to be two ways to update a disconnected Entity Framework entity using the "attach" method.

Method One is to simply set the disconnected entity's state as modified:

myDbContext.Dogs.Attach(dog);
myDbContext.Entry(dog).State = EntityState.Modified;
myDbContext.SaveChanges();

This will save all fields on the "dog" object. But say you are doing this from an mvc web page where you only allow editing of Dog.Name, and the only Dog property contained on the page is Name. Then one could do Method Two:

myDbContext.Dogs.Attach(dog);
myDbContext.Entry(dog).Property(o => o.Name).CurrentValue = dog.Name;
myDbContext.Entry(dog).Property(o => o.Name).IsModified = true;
myDbContext.SaveChanges();

Method Two could get quite verbose when there are a lot of properties to update. This prompted me to attempt Method Three, setting IsModified = false on the properties I don't want to change. This does not work, throwing the runtime error "Setting IsModified to false for a modified property is not supported":

myDbContext.Dogs.Attach(dog);
myDbContext.Entry(dog).State = EntityState.Modified;
myDbContext.Entry(dog).Property(o => o.Owner).IsModified = false;
myDbContext.SaveChanges();

I'd much prefer to use Method One everywhere, but there are many instances where my asp.net mvc view does not contain every scalar property of the Dog class.

My questions are:

  1. Are there any attributes I could use on the POCO class that would tell Entity Framework that I never want the property to up updated? Eg, [NeverUpdate]. I am aware of the [NotMapped] attribute, but that is not what I need.
  2. Failing that, is there any way I can use Method One above (myDbContext.Entry(dog).State = EntityState.Modified; ) and exclude fields that I don't want updated?

P.S. I am aware of another way, to not use "attach" and simply fetch a fresh object from the database, update the desired properties, and save. That is what I am doing, but I'm curious if there is a way to use "attach," thus avoiding that extra trip to the database, but do it in a way that is not so verbose as Method Two above. By "fetch a fresh object" I mean:

Dog dbDog = myDbContext.Dogs.FirstOrDefault(d => d.ID = dog.ID);
dbDog.Name = dog.Name;
myDbContext.SaveChanges();

解决方案

The following may work works.

myDbContext.Dogs.Attach(dog);
myDbContext.Entry(dog).State = EntityState.Modified;

var objectContext = ((IObjectContextAdapter) myDbContext).ObjectContext;
foreach (var entry in objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified).Where(entity => entity.Entity.GetType() == typeof(Dogs)))
{
    // You need to give Foreign Key Property name
    // instead of Navigation Property name
    entry.RejectPropertyChanges("OwnerID"); 

}

myDbContext.SaveChanges();


If you want to do it in a single line, use the following extension method:

public static void DontUpdateProperty<TEntity>(this DbContext context, string propertyName)
{
    var objectContext = ((IObjectContextAdapter) context).ObjectContext;
    foreach (var entry in objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified).Where(entity => entity.Entity.GetType() == typeof(TEntity)))
    {
        entry.RejectPropertyChanges(propertyName); 
    }
}

And use it like this

// After you modify some POCOs
myDbContext.DontUpdateProperty<Dogs>("OwnerID");
myDbContext.SaveChanges();

As you can see, you can modify this solution to fit your needs, e.g. use string[] properties instead of string propertyName as the argument.


Suggested Approach

A better solution would be to use an Attribute as you suggested ([NeverUpdate]). To make it work, you need to use SavingChanges event (check my blog):

void ObjectContext_SavingChanges(object sender, System.Data.Objects.SavingChangesEventArgs e)
{
    ObjectContext context = sender as ObjectContext;
    if(context != null)
    {
        foreach(ObjectStateEntry entry in context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified))
        {
            var type = typeof(entry.Entity);
            var properties = type.GetProperties();
            foreach( var property in properties )
            {
                var attributes = property.GetCustomAttributes(typeof(NeverUpdateAttribute), false);
                if(attributes.Length > 0)
                    entry.RejectPropertyChanges(property.Name);
            }
        }
    }
}
// Check Microsoft documentation on how to create custom attributes:
// http://msdn.microsoft.com/en-us/library/sw480ze8(v=vs.80).aspx
public class NeverUpdateAttribute: SystemAttribute
{

}

//In your POCO
public class Dogs
{
    [NeverUpdate]
    public int OwnerID { get; set; }
}

Warning: I did not compile this code. I'm not at home :/


Warning 2: I have just read the MSDN documentation and it says:

ObjectStateEntry.RejectPropertyChanges Method

Rejects any changes made to the property with the given name since the property was last loaded, attached, saved, or changes were accepted. The orginal value of the property is stored and the property will no longer be marked as modified.

I am not sure what its behavior would be in the case of attaching a modified entity. I will try this tomorrow.


Warning 3: I have tried it now. This solution works. Property that is rejected with RejectPropertyChanges() method are not updated in the persistence unit (database).

HOWEVER, if the entity that is updated is attached by calling Attach(), the current context remains dirty after SaveChanges(). Assume that the following row exists in the database:

Dogs
ID: 1
Name: Max
OwnerID: 1

Consider the following code:

var myDog = new Dogs();
myDog.ID = 1;
myDog.Name = Achilles;
myDog.OwnerID = 2;

myDbContext.Dogs.Attach(myDog);
myDbContext.Entry(myDog).State = EntityState.Modified;
myDbContext.SaveChanges();

The current state of database after SaveChanges():

Dogs:
ID: 1
Name: Achilles
OwnerID: 1

The current state of myDbContext after SaveChanges():

var ownerId = myDog.OwnerID;  // it is 2
var status = myDbContext.Entry(myDog).State; // it is Unchanged

So what you should do? Detach it after SaveChanges():

Dogs myDog = new Dogs();
//Set properties
...
myDbContext.Dogs.Attach(myDog);
myDbContext.Entry(myDog).State = EntityState.Modified;
myDbContext.SaveChanges();
myDbContext.Entry(myDog).State = EntityState.Detached;

这篇关于从更新时调用SaveChanges()被调用排除属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆