【问题标题】:Entity Framework: Cancel a property change if no change in value实体框架:如果值没有变化,则取消属性更改
【发布时间】:2009-01-21 01:16:08
【问题描述】:

在实体对象上设置属性时,它会将值保存到数据库中,即使该值与以前完全相同。有没有办法防止这种情况发生?

例子:

如果我加载一个电影对象并且标题是“A”,如果我再次将标题设置为“A”并 SaveChanges(),我希望我不会在 SqlProfiler 中看到 UPDATE 语句,但我确实看到了。有没有办法阻止这一切?

【问题讨论】:

  • 我希望这会是一件快速而简单的事情。正确的方法看起来就像克雷格建议的那样——但这需要一些时间来加快速度。正如 Dave 和 Frans 在下面提到的那样,快速的方法是“照看”变更跟踪。

标签: entity-framework


【解决方案1】:

是的,您可以更改此设置。但是,在实体框架的当前版本中,这样做并非易事。将来会变得更容易。

您看到此行为的原因是实体模型的默认代码生成。这是一个有代表性的例子:

public global::System.Guid Id
{
    get
    {
        return this._Id;
    }
    set
    {
        // always!
        this.OnIdChanging(value);
        this.ReportPropertyChanging("Id");
        this._Id = global::System.Data.Objects.DataClasses
                               .StructuralObject.SetValidValue(value);
        this.ReportPropertyChanged("Id");
        this.OnIdChanged();
    }
}
private global::System.Guid _Id;
partial void OnIdChanging(global::System.Guid value);
partial void OnIdChanged();

此默认代码生成是合理的,因为实体框架不知道您打算如何使用这些值的语义。属性中的类型可能具有可比性,也可能不具有可比性,即使它们是可比的,框架也无法知道您打算如何在所有情况下使用引用相等与值相等。对于像小数这样的某些值类型,这是很清楚的,但在一般意义上它并不明显。

另一方面,您知道自己的代码,并且可以对其进行一些自定义。麻烦的是这是生成的代码,所以不能直接进去编辑。您需要接管代码生成,或者使其变得不必要。那么让我们看看这三个选项。

接管代码生成

这里的基本方法是创建一个 T4 模板,该模板执行后面的代码,并从 Entity Framework 生成默认代码。 Here is one example. 这种方法的一个优点是 the Entity Framework will be moving to T4 generation in the next version,因此您的模板可能会在未来的版本中运行良好。

消除代码生成

第二种方法是完全消除热电联产,do your change tracking support manually, via IPOCO。使用这种方法,您无需更改代码的生成方式,根本不进行任何代码生成,而是通过实现多个接口为实体框架提供更改跟踪支持。有关详细信息,请参阅链接的帖子。

等待

另一种选择是暂时使用实体框架,并等到下一个版本获得您想要的行为。 The next version of the Entity Framework will use T4 by default,所以自定义代码生成将非常容易。

【讨论】:

    【解决方案2】:

    根据MSDN

    对象的状态从 未更改为已修改 属性设置器被调用。出现这种情况 即使设置的值是 与当前值相同。之后 调用 AcceptAllChanges 方法时, 状态返回到未更改。经过 默认情况下,AcceptAllChanges 被调用 在 SaveChanges 操作期间。

    看起来您需要在更新之前检查 Entity 对象的属性值,以防止出现 UPDATE 语句。

    【讨论】:

    • o/r 映射器不应强迫您照看更改跟踪。如果 c.CompanyName 等于“Foo Inc”并且我再次将其设置为“Foo Inc”,则实体或上下文/会话应该理解它是相同的值并且不会发出更新语句。
    • 我并没有就这是否是一件好事发表任何声明,只是反刍他们的文档;)
    【解决方案3】:

    在一般级别上,如果您的实体正在实现 INotifyPropertyChanged,则您不希望在值相同的情况下触发 PropertyChanged 事件。所以每个属性看起来像这样:-

        public decimal Value
        {
            get
            {
                return _value;
            }
            set
            {
                if (_value != value)
                {
                    _value = value;
                    if (_propertyChanged != null) _propertyChanged(this, new PropertyChangedEventArgs("Value"));
                }
            }
    
        }
    

    希望这与实体框架有关。

    【讨论】:

      【解决方案4】:

      您可以做的一件事就是使用部分类文件自己包装属性,然后使用您的属性而不是第一个:

      public sealed partial class MyEFType {
         public string MyWrappedProperty {
            get {
               return MyProperty;
            }
            set {
               if (value == MyProperty)
                  return;
               MyProperty = value;
            }
         }
      }
      

      每个属性执行此操作不太实际,但如果您需要检测特定属性实际上已更改而不仅仅是被写入,则可以使用类似这样的方法工作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-11
        • 2018-01-20
        • 1970-01-01
        • 2011-07-24
        • 1970-01-01
        相关资源
        最近更新 更多