我有两张桌子,我想用种子填充它。
我在Ubuntu中使用ASP.NET Core 2.
如何填充两个表的数据,哪一个表与外键连接? 流量计有许多注释,注意属于flometer。 我想做这样的事情,但它应该存储在数据库中。
new Flowmeter {Make="Simple model name",SerialNum=45, Model="Lor Avon", Notes = new List<Note>()
{
new Note(){Value=45,CheckedAt=System.DateTime.Now},
new Note(){Value=98,CheckedAt=System.DateTime.Now}
}
}
答案 0 :(得分:40)
从实体框架核心2.1 开始,现在有一种新的种子数据播种方法。在DbContext
班级覆盖OnModelCreating
:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 1, Url = "http://sample.com" });
}
对于相关实体,请使用匿名类并指定相关实体的外键:
modelBuilder.Entity<Post>().HasData(
new {BlogId = 1, PostId = 1, Title = "First post", Content = "Test 1"},
new {BlogId = 1, PostId = 2, Title = "Second post", Content = "Test 2"});
请注意,在OnModelCreating方法和Update-Database中输入此数据后,您需要运行添加迁移以更新数据。
官方文档已updated。
答案 1 :(得分:13)
这是我的EF Core 2.0解决方案,改编自https://docs.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/#move-database-initialization-code
在program.cs中
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Seed().Run();
}
...
然后我的播种机课
public static class DatabaseSeedInitializer
{
public static IWebHost Seed(this IWebHost host)
{
using (var scope = host.Services.CreateScope())
{
var serviceProvider = scope.ServiceProvider;
try
{
Task.Run(async () =>
{
var dataseed = new DataInitializer();
await dataseed.InitializeDataAsync(serviceProvider);
}).Wait();
}
catch (Exception ex)
{
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred seeding the DB.");
}
}
return host;
}
}
答案 2 :(得分:6)
tl; dr :看看我的dwCheckApi project,了解我是如何实施的。
正如其他人所说,你可以从JSON或类似的东西中读取你的种子数据(如果你愿意的话,它可以是源代码控制的。)
我在项目中实现它的方法是在Startup类的Configure
方法中调用一个方法(仅在开发时):
if (env.IsDevelopment())
{
app.EnsureDatabaseIsSeeded(false);
}
调用以下内容:
public static int EnsureDatabaseIsSeeded(this IApplicationBuilder applicationBuilder,
bool autoMigrateDatabase)
{
// seed the database using an extension method
using (var serviceScope = applicationBuilder.ApplicationServices
.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var context = serviceScope.ServiceProvider.GetService<DwContext>();
if (autoMigrateDatabase)
{
context.Database.Migrate();
}
return context.EnsureSeedData();
}
}
我的DbContext类型为DwContext
,这是一个扩展EF Core DbContext
类型的类
EnsureSeedData
扩展方法如下所示:
public static int EnsureSeedData(this DwContext context)
{
var bookCount = default(int);
var characterCount = default(int);
var bookSeriesCount = default(int);
// Because each of the following seed method needs to do a save
// (the data they're importing is relational), we need to call
// SaveAsync within each method.
// So let's keep tabs on the counts as they come back
var dbSeeder = new DatabaseSeeder(context);
if (!context.Books.Any())
{
var pathToSeedData = Path.Combine(Directory.GetCurrentDirectory(), "SeedData", "BookSeedData.json");
bookCount = dbSeeder.SeedBookEntitiesFromJson(pathToSeedData).Result;
}
if (!context.BookCharacters.Any())
{
characterCount = dbSeeder.SeedBookCharacterEntriesFromJson().Result;
}
if (!context.BookSeries.Any())
{
bookSeriesCount = dbSeeder.SeedBookSeriesEntriesFromJson().Result;
}
return bookCount + characterCount + bookSeriesCount;
}
此应用程序旨在显示书籍,角色和系列之间的关系。这就是为什么有三个播种机。
其中一种播种方法如下:
public async Task<int> SeedBookEntitiesFromJson(string filePath)
{
if (string.IsNullOrWhiteSpace(filePath))
{
throw new ArgumentException($"Value of {filePath} must be supplied to {nameof(SeedBookEntitiesFromJson)}");
}
if (!File.Exists(filePath))
{
throw new ArgumentException($"The file { filePath} does not exist");
}
var dataSet = File.ReadAllText(filePath);
var seedData = JsonConvert.DeserializeObject<List<Book>>(dataSet);
// ensure that we only get the distinct books (based on their name)
var distinctSeedData = seedData.GroupBy(b => b.BookName).Select(b => b.First());
_context.Books.AddRange(distinctSeedData);
return await _context.SaveChangesAsync();
}
这里可能有一些代码不是很好,但它可能是你反弹的起点。
因为仅在开发环境中调用了播种器,所以您需要确保应用程序以这种方式启动(如果从命令行开始,您可以使用ASPNETCORE_ENVIRONMENT=Development dotnet run
来确保它在开发中启动)
这也意味着您需要一种不同的方法来为生产中的数据库播种。在dwCheckApi中,我有一个控制器,可以调用它来播种数据库(看看DatabaseController's SeedData method看看我是怎么做的。)
答案 3 :(得分:3)
与Microsoft文档中所写的一样,我不喜欢HasData方法,因为我无法以这种方式保持迁移的整洁,并且因为OnModelCreating()
中的DbContext
开始依赖于感觉有点差的数据错误并导致随机数据生成器出现问题。
对我来说,最有效,最方便的方法是为我的每个DbSet创建一个种子类,如下所示。 (使用Bogus库,就像呼吸一样简单)
using Bogus;
// namespace, class, etc.
// CategorySeeder seed method
public int Seed(AppDbContext context)
{
var faker = new Faker<Category>()
.RuleFor(r => r.IsGroup, () => true)
.RuleFor(r => r.Parent, () => null)
.RuleFor(r => r.UniversalTimeTicks, () => DateTime.Now.ToUniversalTime().Ticks)
.RuleFor(r => r.Title, f => "Folder: " + f.Random.Word());
var folders1 = faker.Generate(5);
faker.RuleFor(r => r.Parent, () => folders1.OrderBy(r => Guid.NewGuid()).First());
var folders2 = faker.Generate(10);
var folders3 = folders1.Concat(folders2).ToArray();
faker.RuleFor(r => r.Parent, () => folders3.OrderBy(r => Guid.NewGuid()).First());
faker.RuleFor(r => r.Title, f => f.Random.Word());
faker.RuleFor(r => r.IsGroup, () => false);
var elements = faker.Generate(20);
var allSeeds = elements.Concat(folders3).ToArray();
context.AddRange(allSeeds);
context.SaveChanges();
return allSeeds.Length;
}
// ProductSeeder Seed method
public int Seed(AppDbContext context)
{
var faker = new Faker<Product>()
.RuleFor(r => r.Sku, f => f.Random.AlphaNumeric(8))
.RuleFor(r => r.Title, f => f.Random.Word())
.RuleFor(r => r.Category, () => context.Categories.Where(c => !c.IsGroup).OrderBy(o => Guid.NewGuid()).First());
var prod = faker.Generate(50);
context.AddRange(prod);
context.SaveChanges();
return prod.Count;
}
然后创建仅在开发环境中工作的服务控制器。
public class DataGeneratorController : BaseController
{
public DataGeneratorController(IServiceProvider sp) : base(sp) { }
public IActionResult SeedData()
{
var lst = new List<string>();
if (!_dbContext.Categories.Any())
{
var count = new CategoryConfiguration().Seed(_dbContext);
lst.Add($"{count} Categories have been seeded.");
}
if (!_dbContext.Products.Any())
{
var count = new ProductConfiguration().Seed(_dbContext);
lst.Add($"{count} Products have been seeded.");
}
if (lst.Count == 0)
{
lst.Add("Nothing has been seeded.");
}
return Json(lst);
}
}
只要需要,就可以从Insomnia \ Postman呼叫它。
答案 4 :(得分:1)
如果仍然有人对该主题感兴趣,我们创建了一组工具(.net核心全局工具和库),可以简化数据播种的过程。
长话短说:您可以将当前数据库的内容保存到一些JSON或XML文件中,然后将几行代码添加到您的应用程序中,以加载这些文件并将其中保存的数据导入到您的数据库中。 该工具集完全免费,open-source。
详细的分步说明为published here。
答案 5 :(得分:0)
我在json中创建了种子,只需批量添加我的Asp.net核心启动
与https://garywoodfine.com/how-to-seed-your-ef-core-database/非常相似
尚未找到开箱即用的解决方案。
答案 6 :(得分:0)
我遇到了同样的问题,我通过以下方式修复了播种:
首先我将public static bool AllMigrationsApplied(this DbContext context)
从garywoodfine添加到我的模型中。
然后我实现了一个服务范围来播种db - &gt;见this blog
然后我根据this blog
上的教程使用NBuilder和Faker生成了一个public static void EnsureSeedData
代码来生成测试数据
我希望这可以帮助人们为他们的项目实现自动测试种子。目前我正忙着自己实现这个,当我有时间时,我会发布一些关于如何做的代码示例。
答案 7 :(得分:0)
创建种子数据静态类,如
public static class SeedData
{
public static void Initialize(IServiceProvider serviceProvider)
{
var context = serviceProvider.GetRequiredService<YourDbContext>();
context.Database.EnsureCreated();
if (!context.Items.Any())
{
context.Items.Add(entity: new Item() { Name = "Green Thunder" });
context.Items.Add(entity: new Item() { Name = "Berry Pomegranate" });
context.Items.Add(entity: new Item() { Name = "Betty Crocker" });
context.Items.Add(entity: new Item() { Name = "Pizza Crust Mix" });
context.SaveChanges();
}
if (!context.Shoppings.Any()) {
context.Shoppings.Add(entity:new Shopping() { Name="Defualt" });
}
}
}
更新您的program.cs代码以插入您的种子数据,如下所示
public class Program
{
public static void Main(string[] args)
{
//CreateWebHostBuilder(args).Build().Run();
var host = CreateWebHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<YourDbContext>();
context.Database.Migrate(); // apply all migrations
SeedData.Initialize(services); // Insert default data
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred seeding the DB.");
}
}
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
答案 8 :(得分:0)
我将Entity Framework 3与“内存数据库”上下文一起使用,并且能够通过执行以下操作为数据添加种子。
OnModelCreating
类中覆盖DbContext
。例如: public class NoteContext : DbContext
{
public DbSet<Note> Notes { get; set; }
public NoteContext(DbContextOptions<NoteContext> options)
: base(options)
{
}
/// <summary>
/// Seed data
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Note>().HasData(new[] {
new Note { NoteId = Guid.NewGuid(), User = "UserA", Message = "Message from UserA" },
new Note { NoteId = Guid.NewGuid(), User = "UserB", Message = "Message from UserB" }
});
}
}
context.Database.EnsureCreated()
。例如: [Route("api/[controller]")]
[ApiController]
public class NotesController : ControllerBase
{
private readonly NoteContext _context;
public NotesController(NoteContext context)
{
_context = context;
// Seed data
_context.Database.EnsureCreated();
}
...