我正在研究迁移,以便清理我们的部署流程。将更改推向生产时所需的人工干预越少越好。
我遇到了迁移系统的3个主要障碍。如果我无法找到一个干净利落的方式,他们就会显示出来。
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的这些限制还是他们故意限制以防止我没有看到的其他类型的灾难?
答案 0 :(得分:27)
Sql("Insert ...")
的调用将插入直接放入Up()迁移中。请参阅此页面中间的注释:how to insert fixed data [DatabaseGenerated(DatabaseGeneratedOption.None)]
属性我认为这是Initializer and Seed methods
的一个很好的解释以下是如何使用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 :(得分:12)
作为第1项的可能解决方案,我实现了IDatabaseInitializer
策略,该策略仅运行每个挂起迁移的Seed方法,您需要在每个中实现自定义IMigrationSeed
接口在DbMigration
个类中,Seed
方法将在每个迁移类的Up
和Down
方法之后立即实施。
这有助于解决我的两个问题:
界面如下所示
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类的部分类,我发现这很有用,因为它允许我在我想重新播种相同的迁移时安全地删除迁移类
答案 2 :(得分:3)
您好我在此链接中找到了有关您的问题的非常有用的信息: 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的这些限制还是他们故意限制以防止我看不到其他类型的灾难?”
不是我知道的!
答案 3 :(得分:3)
好的,所以有点抨击我已经设法将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; }
}