【问题标题】:Identity Insert just for initial Data with EF Code-First Migrations使用 EF 代码优先迁移仅为初始数据插入身份
【发布时间】:2019-02-21 04:04:03
【问题描述】:

我目前正在尝试将一些本地数据引入使用 EF Code-First 迁移的数据库。这些数据已经具有唯一 ID,因此最初应该按原样插入第一组数据。

现在我正在考虑首先设置“DatabaseGeneratedOption.None”, 将数据集添加到数据库中,计算最后使用的 ID,使用 T-SQL 命令“dbcc checkident (tablename, reseed, [NEW AUTO ID])”重新设置种子 然后在将 GeneratedOption 重置为“DatabaseGeneratedOption.Identity”时添加另一个迁移。

这是正确的方法和/或什至可能,还是我走错了路?

任何线索将不胜感激
最好的问候

编辑 - 迈克尔斯回答

尝试实现 Michals 答案,但 ID 仍然由数据库“生成”,并且我的显式值被忽略。也许这是我插入值的方式:

MyDbContext context = // GetContext;
DbSet<SomeClass> dbSet = context.SomeClass;
context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT " + someClassTableName + " ON");

foreach(SomeClass sc in toAdd)
{
    // sc already contains all data including its unique ID
    sbSet.Add(sc);
}

context.SaveChanges();
context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT " + someClassTableName + " OFF");

解决方案 /w Michals 答案

嗯, 我尽我最大的努力以不同的方式实现 Michals 的答案,最后得出了一个(非常奇怪的)解决方案。

首先,我有我的 DBContext 类和一个名为 DBContextEditor 的派生类。所做的只是在我更改所有“身份”属性的地方覆盖 OnModelCreating。

public class MyContextEditor : MyContext
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<SomeClass>()
        .Property(e => e.ID)
        .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
    }
}

将此课程与其他帖子(以下来源)中的一些其他技术结合使用,最终给了我想要的结果。
最后我得到了这样的东西:

MyContext context = new MyContextEditor();
DbSet<SomeClass> dataSet = context.SomeClass;

// this.Clear(dataSet); Just for testing to reset Identity Auto ID and remove all Data
// this.Reset(dataSet);

context.Database.Connection.Open();
// Add data to dataSet...
// dataSet.Add(toAdd);
// Apply Michals answer and save
context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT " + tableName + " ON");
context.SaveChanges();
context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT " + tableName + " OFF");

// Close Connection
context.Database.Connection.Close();

我还尝试实现对 MyContext 类的覆盖,该类以错误结尾说“烘焙模型不同...”,这也将 ID 属性更改为不再是“身份”。

最后,这种方法让我能够控制添加身份数据,同时保持生成身份值的可能性。此外,任何进一步的迁移都不会更改模型,也不会删除“身份”标志,而 [DatabaseGeneratedOption] 确实删除了它。 也可以并建议将其存储在 using 中,如下所示:

using( var transaction = context.Database.BeginTransaction'))

伪代码模型仅供参考

class SomeClass
{
    [Key]
    public int ID { get; set; }

    public string Title { get; set; }
    // More Properties
}

相关帖子和来源

【问题讨论】:

    标签: c# database entity-framework code-first identity-insert


    【解决方案1】:

    我个人的做法如下:

    1. SET IDENTITY_INSERT sometableWithIdentity ON
    2. 播种所有数据
    3. SET IDENTITY_INSERT sometableWithIdentity OFF

    在播种数据时,我将所有实体附加到上下文中,添加为哪个预先存在的 Id。

    唯一的问题是有时是否存在外键的循环引用。为了解决问题,我删除了外键的一侧,添加所有实体,然后将外键放回去。

    例如如果我有以下情况:

    public class Customer 
    {
        public List<Address> Addresses { get; set; }
    
        public long? DefaultAddressId { get; set; }
        public Address DefaultAddress { get; set; }
    }
    
    public class Customer 
    {
        public long Id { get; set; }
    
        public long CustomerId { get; set; }
        public Customer Customer { get; set; }
    }
    

    我会做如下的事情:

    1. 取消插入Customer.DefaultAddressId
    2. Customer + Addresses 附加为EntityState.New
    3. Context.SaveChanges() 保存到数据库
    4. Customer.DefaultAddressId 重新附加到第 2 步中跟踪的客户
    5. Context.SaveChanges()保存默认地址Id的

    如果可能,另一个选项是在种子期间关闭参照完整性。但是当我们使用多个数据库(本地开发的 Sqlite,生产的 PostegreSql,有时还有 MariaDb)时,我们无法成功将其关闭 + 重新打开 + 强制检查是否存在引用完整性/

    【讨论】:

    • 实现了您的答案,但无法正常工作。检查我的编辑以获取更多信息
    猜你喜欢
    • 1970-01-01
    • 2016-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-18
    • 2012-09-15
    • 1970-01-01
    • 2013-11-05
    相关资源
    最近更新 更多