我正在学习ASP.NET,并且在我的项目中使用EF Core。在运行迁移时,我想播种一些测试数据。这是我的AppDbContext
类和模型类:
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions options) : base(options)
{
}
public DbSet<Game> Games { get; set; }
public DbSet<Studio> Studios { get; set; }
public DbSet<Person> People { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
var converter = new EnumToNumberConverter<Genre, byte>();
builder.Entity<Game>().Property(item => item.Genre).HasConversion(converter);
}
}
public class Person
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(30)]
public string Name { get; set; }
[Required]
[StringLength(30)]
public string Surname { get; set; }
}
public class Game
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[Required]
[Range(0.0, 1000.0)]
public double Price { get; set; }
public Genre Genre { get; set; }
[Required]
public Studio Studio { get; set; }
}
public class Studio
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
public IEnumerable<Game> Games { get; set; }
[Required]
public decimal Budget { get; set; }
public IEnumerable<Person> Employees { get; set; }
}
您可能会注意到Game和Studio实体之间存在一些复杂的关系。游戏不仅具有创建游戏的工作室,而且工作室具有由其开发的游戏列表。
如果必须编写OnModelCreating
方法,我将首先创建一些测试人员,然后使用它们来填充Employees
类中的Studio
字段。当我必须创建游戏时,会出现问题。 Studio还不存在,如果我想先创建工作室,那么游戏就是一个缺少的元素。
我是否必须手动创建一些游戏,忽略Studio参考,然后实例化Studio,然后手动将对象链接在一起,或者有更好的解决方案吗?最好的问候。
答案 0 :(得分:1)
也许是因为人们只读标题,但每个人都跳到如何在Entity Framework中播种。接下来,有人提出了AddOrUpdate
,它在EF核心中不存在。
与EF6相比,EF核心中的播种已发生了巨大变化。在EF6的Seed
方法中,可以保存对象图,即包含引用和集合的对象。但是,AddOrUpdate
方法存在两个问题(在这里我不会将它们拼写出来),使得它的行为很难遵循,甚至在您不了解它们的情况下也可以正确执行。
作为对此的(过度)反应,EF团队决定不再让EF确定是否应添加或更新实体。播种应添加一个不存在的实体,否则不执行任何操作。永不更新。这些考虑导致EF核心中的播种机制大大简化了:
IDENTITY INSERT ON
一起插入(在Sql Server中)。InvalidOperationException:无法添加实体类型“ Studio”的种子实体,因为它已设置了导航“ Games”。要播种关系,您需要将相关实体种子添加到“游戏”并指定外键值{'StudioId'}。
这意味着您必须将StudioId
添加到Game
。 StudioId
和GenreId
至Person
。不支持独立关联(无Studio
的{{1}}引用)。 (我认为这是一个意义深远的体系结构决定。)
这样做,您的种子代码经过简化,看起来像:
StudioId
循环引用var games = new[]
{
new Game{ Id = 1, Name = "Game1", StudioId = 1 },
new Game{ Id = 2, Name = "Game2", StudioId = 1 },
};
var studio = new Studio
{
Id = 1,
Name = "Studio1",
};
modelBuilder.Entity<Studio>().HasData(studio);
modelBuilder.Entity<Game>().HasData(games);
⟷Studio
在这里无关紧要,因为它仅表示一个外键。
但是,不可能对两个外键进行循环引用。假设Game
具有Studio
所引用的Director
类型的Person
属性,并且主管也是雇员:
DirectorId
现在有一个鸡和蛋的问题:两个实体只有在先插入另一个实体后才能插入。
InvalidOperationException:无法保存更改,因为在要保存的数据中检测到循环依赖性。
我认为这是此设计决策的另一个深远影响。在EF6中,即使var director = new Person { Id = 1, Name = "Director1", StudioId = 1 }; // Employee of Studio1
var studio = new Studio
{
Id = 1,
Name = "Studio1",
DirectorId = 1 // Director is "Director1"
};
瘫痪了,至少也可以两次调用AddOrUpdate
来适应这种情况。
考虑到所有这些,播种并不适合迁移,我对数据播种的立场是:要么不支持它(我不介意),要么很好地支持它。
答案 1 :(得分:-1)
replacePersistentStore:…
在PM插入中: 1.启用迁移 2.添加迁移初始 3.更新数据库
答案 2 :(得分:-1)
您应该使用ModelBuilder播种数据:
modelBuilder.Entity<Post>().HasData(
new Post() { BlogId = 1, PostId = 1, Title = "First post", Content = "Test 1" });
答案 3 :(得分:-1)
如果您不喜欢使用“ ef core modelbuilder”或“ ef core migrations”,则可以在应用程序层中创建默认记录。您不需要模型构建器或迁移工具。像这样。
Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
SeedData.Initialize(app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope().ServiceProvider);
app.UseDeveloperExceptionPage();
}
// your code...
}
SeedData.cs
internal class SeedData
{
static AppDbContext _dataContext;
internal static void Initialize(IServiceProvider serviceProvider)
{
_dataContext = (AppDbContext)serviceProvider.GetService(typeof(AppDbContext));
var ensureCreated = _dataContext.Database.EnsureCreated();
if (ensureCreated)
{
CreatePersonData();
CreateGameData();
CreateStudioData();
try
{
_dataContext.SaveChanges();
}
catch (Exception ex)
{
throw ex;
}
}
}
private static void CreatePersonData() { /* your code... */ }
private static void CreateGameData() { /* your code... */ }
private static void CreateStudioData() { /* your code... */ }
}
答案 4 :(得分:-1)
我将创建一个扩展方法来播种您的数据:
public static class DataContextExtensions
{
public static void EnsureSeedDataForContext(this AppDbContext context)
{
// first, clear the database. This ensures we can always start fresh. This is optional !
context.Games.RemoveRange(context.Games);
context.SaveChanges();
// init seed data
var games = new List<Games>()
{
new Games()
{
Id = new Guid("25320c5e-f58a-4b1f-b63a-8ee07a840bdf"),
Name = "Stephen King",
Price= 14.99
Genre = new Genre(),
Studio = new Studio(),
}
}
//can seed other context data here
context.Games.AddRange(games);
context.SaveChanges();
}
}
然后在configure方法中的startup.cs文件中,可以调用扩展方法,然后执行迁移命令:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, AppDBContext appDbContext)
{
appDbContext.EnsureSeedDataForContext();
app.UseHttpCacheHeaders();
app.UseMvc();
}
对我来说,在存根数据时它并不复杂且不易操作。但是上面的例子也很棒!希望这会有所帮助。