[英]How to seed in Entity Framework Core 2?
我有兩張桌子,我想用種子填充它們。
我在 Ubuntu 中使用 ASP.NET Core 2。
如何填充通過外鍵連接到另一個表的兩個表的數據?
流量計有很多注釋,注釋屬於流量計。
我想做這樣的事情,但它應該存儲在數據庫中:
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 }
}
}
從Entity Framework Core 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 中輸入此數據后,您需要運行添加遷移以更新數據。
官方文檔已更新。
這是我針對 EF Core 2.0 的解決方案,改編自https://learn.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/#move-database-initialization-code
在程序.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;
}
}
tl;dr :查看我的dwCheckApi 項目,看看我是如何實現它的。
正如其他人所說,您可以從 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 的 SeedData 方法以了解我是如何做到的)。
我不喜歡 Microsoft 文檔中所寫的 HasData 方法,因為我無法通過這種方式保持遷移干凈,因為我的DbContext
中的OnModelCreating()
開始依賴感覺有點不對的數據,並導致隨機數據生成器出現問題。
對我來說,最有效和舒適的方法是為我的每個 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 調用它。
創建種子數據靜態類,如
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>();
}
我在 json 中創建了我的種子,然后將它們批量添加到我的 Asp.net 核心 Startup
非常類似於https://garywoodfine.com/how-to-seed-your-ef-core-database/
尚未找到開箱即用的解決方案。
我遇到了同樣的問題,並通過以下方式修復了播種:
首先,我將 garywoodfine 中的public static bool AllMigrationsApplied(this DbContext context)
添加到我的模型中。
然后我實現了一個服務范圍來為數據庫播種 -> 請參閱此博客
然后我創建了一個public static void EnsureSeedData
,其中包含使用 NBuilder 和 Faker 生成測試數據的代碼,遵循本博客上的教程
我希望這將幫助人們為他們的項目實施自動化測試種子。 目前我正忙於自己實現這個,當我有時間時,我會發布一些代碼示例來說明如何做到這一點。
我將 Entity Framework 3 與“內存數據庫”上下文一起使用,並且能夠通過執行以下操作來播種數據。
DbContext
類中覆蓋OnModelCreating
。 例如: 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();
}
...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.