![](/img/trans.png)
[英]Seeding large lookup table data with Entity Framework Code First migrations
[英]Entity Framework - Migrations - Code First - Seeding per Migration
我正在研究迁移,以便清理我们的部署过程。 将更改推向生产时所需的人工干预越少越好。
我在迁移系统中遇到了3个主要障碍。 如果我无法找到一个干净的方式,他们是show stoppers。
1.如何为每次迁移添加种子数据:
我执行命令“add-migration”,它使用Up和Down函数来构建一个新的迁移文件。 现在,我想通过Up和Down更改自动更改数据。 我不希望将种子数据添加到Configuration.Seed方法,因为它运行所有以各种重复问题结束的迁移。
2.如果无法满足上述要求,我该如何避免重复?
我有一个枚举,我循环,以将值添加到数据库。
foreach(var enumValue in Enum.GetValues(typeof(Access.Level)))
{
context.Access.AddOrUpdate(
new Access { AccessId = ((int)enumValue), Name = enumValue.ToString() }
);
}
context.SaveChanges();
即使我使用的是AddOrUpdate,我仍然会在数据库中获得重复项。 上面的代码将我带到了第三个也是最后一个问题:
3.如何为主键提供种子?
我用以上代码列举的是:
public class Access
{
public enum Level
{
None = 10,
Read = 20,
ReadWrite = 30
}
public int AccessId { get; set; }
public string Name { get; set; }
}
我指定了我想要的值作为我的主键,但实体框架似乎忽略了它。 他们最终仍然是1,2,3。 我怎么能得到10,20,30?
目前这些EF的局限性还是有意限制,以防止我没有看到的其他类型的灾难?
Sql("Insert ...")
调用Sql("Insert ...")
将插入直接放入Up()迁移中。 请参阅本页中间的注释: 如何插入固定数据 [DatabaseGenerated(DatabaseGeneratedOption.None)]
属性 我认为这是初始化器和种子方法的一个很好的解释
以下是如何使用AddOrUpdate方法的示例:
foreach(var enumValue in Enum.GetValues(typeof(Access.Level)))
{
context.Access.AddOrUpdate(
x => x.Name, //the natural key is "Name"
new Access { AccessId = ((int)enumValue), Name = enumValue.ToString() }
);
}
作为第1项的可能解决方案,我实现了IDatabaseInitializer
策略,该策略仅运行每个待处理迁移的Seed方法,您需要在每个DbMigration
类中实现自定义IMigrationSeed
接口,然后Seed
方法将是在每个迁移类的Up
和Down
方法之后Up
实现。
这有助于为我解决两个问题:
界面看起来像这样
public interface IMigrationSeed<TContext>
{
void Seed(TContext context);
}
下面是将调用此Seed
方法的新实现
public class CheckAndMigrateDatabaseToLatestVersion<TContext, TMigrationsConfiguration>
: IDatabaseInitializer<TContext>
where TContext : DbContext
where TMigrationsConfiguration : DbMigrationsConfiguration<TContext>, new()
{
public virtual void InitializeDatabase(TContext context)
{
var migratorBase = ((MigratorBase)new DbMigrator(Activator.CreateInstance<TMigrationsConfiguration>()));
var pendingMigrations = migratorBase.GetPendingMigrations().ToArray();
if (pendingMigrations.Any()) // Is there anything to migrate?
{
// Applying all migrations
migratorBase.Update();
// Here all migrations are applied
foreach (var pendingMigration in pendingMigrations)
{
var migrationName = pendingMigration.Substring(pendingMigration.IndexOf('_') + 1);
var t = typeof(TMigrationsConfiguration).Assembly.GetType(
typeof(TMigrationsConfiguration).Namespace + "." + migrationName);
if (t != null
&& t.GetInterfaces().Any(x => x.IsGenericType
&& x.GetGenericTypeDefinition() == typeof(IMigrationSeed<>)))
{
// Apply migration seed
var seedMigration = (IMigrationSeed<TContext>)Activator.CreateInstance(t);
seedMigration.Seed(context);
context.SaveChanges();
}
}
}
}
}
这里的好处是你有一个真正的EF上下文来操作种子数据,就像标准的EF种子实现一样。 但是,如果您决定删除先前迁移中的种子表,则可能会出现这种情况,您必须相应地重构现有的种子代码。
编辑:作为在Up和Down之后实现种子方法的替代方法,您可以创建相同Migration类的部分类,我发现这很有用,因为它允许我在我想重新播种时安全地删除迁移类同样的迁移。
您好我在此链接中找到了有关您的问题的非常有用的信息: Safari Books Online
“1.如何为每次迁移添加种子数据:”正如您在示例中所看到的,您需要为种子创建新的混淆。 迁移后必须调用此种子配置。
public sealed class Configuration : DbMigrationsConfiguration
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(SafariCodeFirst.SeminarContext context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
}
}
“2.如果上述情况不可能,我该如何避免重复?”
AddOrUpdate如果你在这里收到错误,必须帮助你准确地复制重复项,你可能会在调用堆栈后发生配置错误。 看看这个例子!
“3.我怎样才能为主键提供种子?”
这也是你的关键定义。 如果您的密钥DatabaseGenerated(DatabaseGeneratedOption.Identity)
比您不必提供它。 在其他一些senarios中,您需要创建一个新的,取决于密钥类型。
“目前EF的这些限制还是他们故意限制,以防止我看不到其他类型的灾难?”
不是我知道的!
好吧,所以有点抨击我已经设法将EF击败提交。 这是我做的:
1.我找不到特定迁移的数据。 这一切都必须进入常见的Configuration.Seed方法。
2.为了避免重复,我不得不做两件事情。 对于我的枚举,我写了以下种子代码:
foreach (var enumValue in Enum.GetValues(typeof(Access.Level)))
{
var id = (int)enumValue;
var val = enumValue.ToString();
if(!context.Access.Any(e => e.AccessId == id))
context.Access.Add(
new Access { AccessId = id, Name = val }
);
}
context.SaveChanges();
所以基本上,只检查它是否存在,如果不存在则添加
3.为了使上述功能正常工作,您需要能够插入主键值。 幸运的是,这个表将始终具有相同的静态数据,因此我可以取消激活自动增量。 为此,代码如下所示:
public class Access
{
public enum Level
{
None = 10,
Read = 20,
ReadWrite = 30
}
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int AccessId { get; set; }
public string Name { get; set; }
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.