【问题标题】:How to handle EF 4.3.1 setting Modified a Rowversion Row如何处理 EF 4.3.1 设置修改了 Rowversion 行
【发布时间】:2012-04-27 15:36:42
【问题描述】:

假设我们有:

public class Driver
{
    public int driverID { get; set; }
    public byte[] stamp { get; set; }
    public string name { get; set; }
    public string prename { get; set; }
}

简而言之,我正面临这种情况。

 ...
 var myDriver = myCustomDBContext.Drivers.AsNoTracking()
                                         .Where(d => d.driverID == driverID)
                                         .SingleOrDefault();
 ...
 myDriver.name = "John";
 myDriver.prename = "Lennon";
 ...
 myCustomDBContext.Drivers.Attach(myDriver);
 myCustomDBContext.Entry(myDriver).State = EntityState.Modified;
 myCustomDBContext.SaveChanges();
 ...

结果是

 The column cannot be modified because it is an identity, rowversion or 
     a system column. [Column name = stamp]

是否有任何方法可以强制对分离的实体进行更新,或者此 rowversion 列的解决方法不设置为已修改。

【问题讨论】:

    标签: c# .net entity-framework entity-framework-4.3


    【解决方案1】:

    看起来您没有在模型中将 stamp 属性指定为 rowversion,它只是一个二进制字段。您可以使用 Fluent API 指定它:

    modelBuilder.Entity<Driver>().Property(d => d.stamp)
        .IsRowVersion()
        .IsConcurrencyToken(false);
    

    上面的代码适用于您不希望将stamp 属性作为并发令牌的情况。 (默认情况下,rowversion 是一个并发令牌,因此您必须明确禁用它。)如果您想将它作为并发令牌,那么您可以使用 Fluent API...

    modelBuilder.Entity<Driver>().Property(d => d.stamp)
        .IsRowVersion();
    

    ...或带有数据注释:

    [Timestamp]
    public byte[] stamp { get; set; }
    

    这应该会阻止 EF 为这个属性写一个 UPDATE。

    编辑

    如果您使用数据库优先策略,[Timestamp] 属性将不起作用。此属性仅适用于 Code-First 开发。

    当您使用 Database-First 时,连接字符串包含一个元数据部分,引用 EDMX 文件中定义的 EDM:

    connectionString="metadata=res://*/Model1.csdl
                              |res://*/Model1.ssdl
                              |res://*/Model1.msl;
                              ...
                              ..."
    

    如果实体框架在连接字符串中找到此部分,它不会在模型属性上使用数据注释,也不会处理 Fluent API 中的任何代码(根本不调用OnModelCreating)。相反,它从嵌入和编译的 EDMX 文件加载映射定义。

    这意味着,如果您想将stamp 属性定义为并发令牌,您必须在 EDMX 文件中执行此操作。在 XML 中它看起来像这样:

    在 SSDL 部分:

    <Property Name="stamp" Type="timestamp" Nullable="false"
              StoreGeneratedPattern="Computed" />
    

    在 CSDL 部分:

    <Property Name="stamp" Type="Binary" Nullable="false" MaxLength="8"
              FixedLength="true"
              annotation:StoreGeneratedPattern="Computed"
              ConcurrencyMode="Fixed" />
    

    您也可以在 Visual Studio 的模型设计器中定义:在设计器图面的实体中标记stamp 属性,转到“属性”窗口并将“并发模式”设置为“固定”(同时设置"StoreGeneratedPattern" 到 "Computed")。

    您还可以从连接字符串中删除元数据部分。但这实际上意味着您从数据库优先切换到代码优先开发。然后所有属性和 Fluent API 都将被尊重,但不再是 EDMX 中的任何定义。

    【讨论】:

    • 将属性标记为 [Timestamp] ist solving anything. The problem relies within the DBContext.Entry(entity).State = EntityState.Modified;. It marks the stamp property as Modified and during DBContext.SaveChanges()` 它构建了 动态-sql 更新数据库中的列 [stamp]。这是 SQLCE 的一个猜测原因,我没有分析器来备份它。
    • @CiobanuIon:对于 SQL Server,这是不正确的。 EF 不会发送更新标记为[Timestamp] 的属性列的 SQL,而是将其值包含在 WHERE 子句中以进行乐观并发检查。你有没有测试过它不起作用?您使用的是哪个版本的 SQLCE?
    • m using SQLCE4.0.0.0. Now that I tested if concurrency is working, its 不是。对于通常的非分离实体,更新正在发生,[stamp] 值正在增加,但没有发生并发。这是出乎意料的。 Im having a database first case. Generated POCOclases from EF4.x DbContext Generator and decorated them with DataAnnotations. Really unsure where to look now. On the previous matter, my guess was if I cant 将已修改设置为整个实体我仅将某些属性设置为已修改,并且更新有效。但是对每个属性都进行硬编码并不是一个合适的解决方案。
    • @CiobanuIon:你有和以前一样的错误信息还是另一个错误?
    • @CiobanuIon:请参阅上面我的答案中的编辑部分。当您使用数据库优先时,数据注释用于定义 EF 模型元数据。当您将它们用于其他目的时,它们确实起作用,例如验证、绑定到 UI 等。这些组件可能会处理属性,但 EF 不会 - 除非您使用代码优先。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-06-30
    • 1970-01-01
    • 1970-01-01
    • 2013-12-15
    • 2020-01-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多