【问题标题】:Error in CodeFirst Seed with migrations : Modifying a column with the 'Identity' pattern is not supported. Column: 'CreatedAt'.带有迁移的 CodeFirst Seed 中的错误:不支持使用“身份”模式修改列。列:'CreatedAt'。
【发布时间】:2015-02-26 07:23:56
【问题描述】:

我已在我的 Azure 移动服务项目中激活迁移。我在迁移的 Configuration.cs 类中填充了新的种子函数。如果表为空,则种子函数运行没有任何问题。当我的 AddorUpdate 尝试更新第一个对象时,我在内部异常中收到错误:“不支持使用 'Identity' 模式修改列。列:'CreatedAt'。表:'CodeFirstDatabaseSchema.Category'。”

我的部分代码如下:

context.categories.AddOrUpdate(
            new Category { Id="1", Code="GEN", Text="General"},
            new Category { Id="2", Code="POL", Text="Politics"},
            new Category { Id="3", Code="FAS", Text="Fashion"},
            new Category { Id="4", Code="PEO", Text="People"},
            new Category { Id="5", Code="TEC", Text="Technology"},
            new Category { Id="6", Code="SPO", Text="Sport"},
            new Category { Id="7", Code="LIV", Text="Living"}
        );

【问题讨论】:

    标签: c# entity-framework azure azure-mobile-services


    【解决方案1】:

    这是我对 Nikatlas 解决方案的通用实现。

    答案的简短版本:您不能使用空值修改 CreatedAt,因此您可以改用此函数:

        private void AddOrUpdatePreservingCreatedAt<T> (DbSet<T> set, T item) where T : EntityData
        {        
            var existing = set.Where(i => i.Id == item.Id).FirstOrDefault();
            if (existing != null)
            {
                item.CreatedAt = existing.CreatedAt;             
            }
            set.AddOrUpdate(i => i.Id, item);
        }
    

    这样称呼

    AddOrUpdatePreservingCreatedAt(context.YourItems, itemToBeUpdatedOrAdded);
    

    【讨论】:

    • 这是迄今为止最干净的创可贴修复!
    • 您的解决方案很好,但我将其修改为真正的 DbSet 扩展方法,并删除了“Where”,因为它是不必要的。 public static class DatabaseSetExtensions { public static void AddOrUpdatePreservingCreatedAt&lt;T&gt;(this DbSet&lt;T&gt; set, T item) where T : EntityData { var existing = set.FirstOrDefault(i =&gt; i.Id == item.Id); if (existing != null) { item.CreatedAt = existing.CreatedAt; } set.AddOrUpdate(i =&gt; i.Id, item); } }
    • 这是否意味着每次升级/迁移所有条目也会更新其UpdateAt?我想如果数据在迁移过程中确实发生了变化,这是明智的,但如果没有,那么不必要地丢失这些数据似乎是一种耻辱。我想你也可以添加item.UpdatedAt = existing.UpdatedAt,以避免这种情况。
    【解决方案2】:

    看来我已经找到了解决这个问题的方法。

    出现此错误的原因是AddOrUpdate 方法。 如这篇文章所述:http://thedatafarm.com/data-access/take-care-with-ef-4-3-addorupdate-method/

    更重要的是,如果找到匹配项,则更新将更新所有内容,并取消任何不在您的 AddOrUpdate 中的内容。

    这意味着在第一个种子之后,每当您的代码运行时,它都会尝试正确更新您的实体,但它会尝试在 CreatedAt 字段上传递 null 值。如果您查看 EntityData 类,则 CreateAt 字段具有以下属性:

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] //Here they mark this as IDENTITY        
    [Index(IsClustered = true)] // Cluster index. i really dont know why ?         
    [TableColumn(TableColumnType.CreatedAt)]         
    public DateTimeOffset? CreatedAt { get; set; }
    

    所以出现错误是因为您尝试修改CreatedAt 列。

    我的解决方案是创建一个列表,查找将 CreatedAt 设置为正确的值,然后添加或更新:

     // Create List       
     List<Permission> permissions = new List<Permission>(new Permission[]{             
       new Permission { Id = "ID1" , Name = "Send SMS"},           
       new Permission { Id = "ID2", Name = "Send Email"}            });
       // Iterate through list to set CreatedAt to correct value 
       foreach (Permission p in permissions){
          // Get the record from the db if it exists              
          var t = context.PermissionSet.Where(s => s.Id == p.Id).FirstOrDefault();
          if (t != null){
             p.CreatedAt = t.CreatedAt; //SET CreatedAt to correct Value if the record already exists                
          }
          context.PermissionSet.AddOrUpdate(a => a.Id, p); // NOW I CAN UPDATE WITH NO PROBLEM
       } 
    

    希望这会有所帮助。 :)

    【讨论】:

    • 工作就像一个魅力,感谢@Nikatlas!我还冒昧地在下面为您的解决方案发布了我的通用方法,希望您不介意:)
    【解决方案3】:

    如果您有一个名为 Id 的整数列,那么 Entity Framework 将假定它是主键并且它是数据库生成的 - 因此它在数据库中创建为 IDENTITY 列。

    您无法为 IDENTITY 列指定 ID,因此您可以通过删除 Id = 1Id = 2 等来停止这样做

    我对您遇到问题的列名为“CreatedAt”这一事实感到有些困惑。听起来应该是DateTime,也可能是数据库生成的,但肯定不应该是IDENTITY

    无论如何,您可能想要的用法是指定实体的自然键,以便 EF 可以识别任何已存在的记录。因此,如果CODE 是自然键,那么您应该这样编写种子:

    context.categories.AddOrUpdate(
       x => x.Code,//the natural key is Code    
       new Category { Code="GEN", Text="General"},
       new Category { Code="POL", Text="Politics"},
       new Category { Code="FAS", Text="Fashion"},
       new Category { Code="PEO", Text="People"},
       new Category { Code="TEC", Text="Technology"},
       new Category { Code="SPO", Text="Sport"},
       new Category { Code="LIV", Text="Living"}
    

    );

    参考: Take care with the AddOrUpdate method

    【讨论】:

    • CreatedAt 是 Azure 移动服务中的标识列。查看 EntityData 类(尽管我认为它不应该)
    • 是的,我忘了提到我正在使用 EntityData。 CreatedAt 是一个标识列。
    【解决方案4】:

    This question, its answers and comments 可能对您有所帮助,但帮助不大。

    您可以使用问题提供的解决方案在身份列上插入。但是您不能更新 Identity Column 的值。对身份列进行更新的唯一方法是将其标记为不是身份。可能是通过添加手动迁移。

    This SO question and its answers 也可能有帮助。

    Read here also on general MSSQL Server constraints on Updating Identity Column.

    【讨论】:

      【解决方案5】:

      同样的问题和解决方案 我们遇到了同样的问题,它是另一个表中的触发器,它影响了我们遇到问题的表。

      我们的发展细节 我们开发了一个连接到 Azure Web 服务的 Xamarin 应用程序。当我们使用来自 iMobileServices 的 PushAsync 方法时,它给了我们错误:不支持使用“身份”模式修改列。列:“CreatedAt”。

      这对我们来说很奇怪,因为有些表格的网络服务没有问题

      原因 触发器更新似乎与来自移动设备的 pushasync 冲突。

      我们禁用了触发器,将职责切换到前端,它工作正常。至少对我们来说。

      我们希望这个解决方案对某人有所帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-08-21
        • 2017-10-03
        • 1970-01-01
        • 2011-01-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多