สร้างฐานข้อมูลอัตโนมัติใน Entity Framework Core


110

แอปพลิเคชันของฉันที่ถูกย้ายไปยัง. NET core จะใช้ EF Core ใหม่กับ SQLite ฉันต้องการสร้างฐานข้อมูลและโครงสร้างตารางโดยอัตโนมัติเมื่อมีการเรียกใช้แอปครั้งแรก ตามเอกสารหลักของ EF สิ่งนี้ทำได้โดยใช้คำสั่งด้วยตนเอง

dotnet ef migrations add MyFirstMigration

dotnet ef database update

อย่างไรก็ตามฉันไม่ต้องการให้ผู้ใช้ป้อนคำสั่งเหล่านี้และต้องการให้แอปสร้างและตั้งค่าฐานข้อมูลสำหรับการใช้งานครั้งแรก สำหรับ EF 6 มีฟังก์ชันเช่น

Database.SetInitializer(new CreateDatabaseIfNotExists<MyContext>());

แต่ใน EF Core สิ่งเหล่านี้ดูเหมือนจะไม่มีอยู่จริง ฉันไม่พบตัวอย่างหรือเอกสารใด ๆ เกี่ยวกับสิ่งที่เทียบเท่าสำหรับแกน EF และไม่มีการกล่าวถึงในรายการคุณสมบัติที่ขาดหายไปในเอกสารหลักของ EF ฉันมีการตั้งค่าคลาสโมเดลอยู่แล้วดังนั้นฉันจึงสามารถเขียนโค้ดเพื่อเริ่มต้นฐานข้อมูลตามโมเดลได้ แต่จะง่ายกว่าถ้าเฟรมเวิร์กทำสิ่งนี้โดยอัตโนมัติ ฉันไม่ต้องการสร้างโมเดลหรือย้ายข้อมูลโดยอัตโนมัติเพียงแค่สร้างโครงสร้างตารางในฐานข้อมูลใหม่

ฉันพลาดอะไรบางอย่างที่นี่หรือฟังก์ชันสร้างตารางอัตโนมัติหายไปในแกน EF หรือไม่

คำตอบ:


158

หากคุณสร้างการโอนย้ายคุณสามารถดำเนินการได้ในStartup.csดังต่อไปนี้

 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
 {
      using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
      {
            var context = serviceScope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
            context.Database.Migrate();
      }

      ...

สิ่งนี้จะสร้างฐานข้อมูลและตารางโดยใช้การย้ายข้อมูลที่เพิ่มเข้ามา

หากคุณไม่ได้ใช้ Entity Framework Migrations และเพียงแค่ต้องการโมเดล DbContext ของคุณที่สร้างขึ้นเหมือนกับที่อยู่ในคลาสบริบทของคุณในตอนแรกคุณสามารถใช้:

 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
 {
      using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
      {
            var context = serviceScope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
            context.Database.EnsureCreated();
      }

      ...

แทน.

หากคุณต้องการลบฐานข้อมูลก่อนที่จะสร้างฐานข้อมูลให้โทร:

            context.Database.EnsureDeleted();

ก่อนโทร EnsureCreated()

ดัดแปลงมาจาก: http://docs.identityserver.io/en/latest/quickstarts/7_entity_framework.html?highlight=entity


1
ฉันยังค่อนข้างใหม่สำหรับ EF ฉันสร้างคลาสที่กำหนดโครงสร้างข้อมูลโดยใช้โค้ด EF 6 ก่อน "สร้างจากฐานข้อมูล" จากสตูดิโอภาพโดยใช้ไฟล์ฐานข้อมูลปัจจุบัน จากนั้นฉันก็ตัด / วางสิ่งเหล่านี้ลงในโซลูชัน dotnet core VS ใหม่ - ดังนั้นฉันเดาว่าสิ่งเหล่านี้ไม่ใช่การโยกย้าย หมายความว่าฉันต้องสร้างไฟล์การย้ายข้อมูลก่อนจึงจะสามารถใช้โค้ดด้านบนได้หรือไม่
deandob

2
I`m ขอโทษฉันไม่ได้ทำผิดพลาดในคำตอบของฉันหากคุณไม่ได้ใช้การโยกย้าย, สามารถใช้คำสั่ง context.Database.EnsureCreated () / EnsureDeleted () รายละเอียดเพิ่มเติมในblogs.msdn.microsoft.com/dotnet/2016 / 09/29 / …
Ricardo Fontana

3
จะเกิดอะไรขึ้นถ้าคุณต้องการทั้งสองวิธี? เราเพิ่งย้ายแอปของเราไปที่ UWP และเริ่มใช้ EF Core ซึ่งหมายความว่าผู้ใช้บางคนต้องการฐานข้อมูลที่จะสร้างขึ้นใหม่ในขณะที่คนอื่น ๆ มีฐานข้อมูลอยู่แล้ว มีวิธีใดบ้างที่จะทำให้แน่ใจได้ว่าการย้ายข้อมูลครั้งแรกจะสร้างตารางเริ่มต้นเท่านั้นหากยังไม่มีอยู่ คำตอบของคุณดูเหมือนจะไม่ครอบคลุมสิ่งนี้หรือฉันขาดอะไรไป?
Lars Udengaard

35

คำตอบของฉันคล้ายกับคำตอบของ Ricardo มาก แต่ฉันรู้สึกว่าแนวทางของฉันตรงไปตรงมากว่าเล็กน้อยเพียงเพราะมีหลายอย่างที่เกิดขึ้นในusingหน้าที่ของเขาซึ่งฉันไม่แน่ใจด้วยซ้ำว่ามันทำงานอย่างไรในระดับล่าง

ดังนั้นสำหรับผู้ที่ต้องการโซลูชันที่เรียบง่ายและสะอาดซึ่งสร้างฐานข้อมูลสำหรับคุณที่คุณรู้ว่าเกิดอะไรขึ้นภายใต้ประทุนสิ่งนี้เหมาะสำหรับคุณ:

public Startup(IHostingEnvironment env)
{
    using (var client = new TargetsContext())
    {
        client.Database.EnsureCreated();
    }
}

นี่หมายความว่าภายในสิ่งDbContextที่คุณสร้างขึ้น (ในกรณีนี้เรียกว่าของฉันTargetsContext) คุณสามารถใช้อินสแตนซ์ของอินสแตนซ์DbContextเพื่อให้แน่ใจว่าตารางที่กำหนดไว้ในคลาสถูกสร้างขึ้นเมื่อเรียกใช้Startup.csในแอปพลิเคชันของคุณ


13
เช่นเดียวกับข้อมูลจากที่นี่ : EnsureCreatedข้ามการย้ายข้อมูลโดยสิ้นเชิงและสร้างสคีมาให้คุณเท่านั้นคุณไม่สามารถผสมผสานสิ่งนี้กับการย้ายข้อมูลได้ EnsureCreatedได้รับการออกแบบมาสำหรับการทดสอบหรือการสร้างต้นแบบอย่างรวดเร็วโดยที่คุณพอใจกับการทิ้งและสร้างฐานข้อมูลใหม่ทุกครั้ง หากคุณใช้การย้ายข้อมูลและต้องการให้ใช้โดยอัตโนมัติเมื่อเริ่มแอปคุณสามารถใช้context.Database.Migrate()แทนได้
Thomas Schneiter

นี่ตอบความสับสนของฉันว่าทำไมสตริงการเชื่อมต่อของฉันไม่ทำงาน ... :( ดังนั้นฐานข้อมูลไม่ได้สร้างขึ้นโดยอัตโนมัติที่คุณต้องระบุ Database.EnsureCreated () ซึ่งฉันทำในตัวสร้าง DbContext ของฉันขอบคุณมากช่วยฉันจากภาวะที่กลืนไม่เข้าคายไม่ออก 3 วัน X_X
pampi

แค่อยากจะทราบว่าฉันก็พยายามวางนี้ในแฟ้มเริ่มต้นของฉันและ VS2019 แจ้งผมว่าจะเลิกตอนนี้และทางเลือกที่แนะนำคือIHostingEnvironment Microsoft.AspNetCore.Hosting.IWebHostEnvironment
ctrl-z pls

18

หากคุณได้รับบริบทผ่านรายการพารามิเตอร์ของ Configure ใน Startup.cs คุณสามารถทำสิ่งนี้แทนได้:

public void Configure(IApplicationBuilder app, IHostingEnvironment env,  LoggerFactory loggerFactory,
    ApplicationDbContext context)
 {
      context.Database.Migrate();
      ...

1
IMHO นี่เป็นทางออกที่ดีที่สุด: คุณไม่จำเป็นต้องสนใจบริการใด ๆ หรือแม้แต่บริบท DB คุณก็สามารถใช้บริบทที่คุณได้รับจากการฉีดการพึ่งพา ดี!
Tobias

15

สำหรับ EF Core 2.0+ ฉันต้องใช้แนวทางที่แตกต่างออกไปเพราะพวกเขาเปลี่ยน API ตั้งแต่เดือนมีนาคม 2019 Microsoft ขอแนะนำให้คุณใส่รหัสการย้ายฐานข้อมูลในคลาสรายการแอปพลิเคชันของคุณ แต่อยู่นอกรหัสการสร้าง WebHost

public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateWebHostBuilder(args).Build();
        using (var serviceScope = host.Services.CreateScope())
        {
            var context = serviceScope.ServiceProvider.GetRequiredService<PersonContext>();
            context.Database.Migrate();
        }
        host.Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

โปรดทราบว่าคุณกำลังเรียกใช้การย้ายข้อมูลที่รันไทม์คุณจะต้องใช้การเชื่อมต่อฐานข้อมูลที่มีสิทธิ์เข้าถึงเพื่อเขียนสคีมาฐานข้อมูล เมื่อใช้วิธีนี้ขอแนะนำให้ใช้การเชื่อมต่อที่แตกต่างกันสำหรับการสืบค้นของคุณนอกเหนือจากการเรียกใช้การย้ายข้อมูล หากการโจมตีด้วยการฉีด SQL ทำให้เกิดขึ้นอย่างใดอย่างหนึ่งและคุณกำลังใช้สตริงการเชื่อมต่อที่มีสิทธิ์ DCL หรือ DDL สำหรับการค้นหาของคุณมันเป็นเวกเตอร์การโจมตี geeksforgeeks.org/sql-ddl-dql-dml-dcl-tcl-commands
Paul Stegler

8

หากคุณยังไม่ได้สร้างการย้ายข้อมูลมี 2 ตัวเลือก

1. สร้างฐานข้อมูลและตารางจากแอปพลิเคชันหลัก:

var context = services.GetRequiredService<YourRepository>();
context.Database.EnsureCreated();

2. สร้างตารางหากมีฐานข้อมูลอยู่แล้ว:

var context = services.GetRequiredService<YourRepository>();
context.Database.EnsureCreated();
RelationalDatabaseCreator databaseCreator =
(RelationalDatabaseCreator)context.Database.GetService<IDatabaseCreator>();
databaseCreator.CreateTables();

ขอบคุณคำตอบของ Bubi


3
บริการของคุณคืออะไร?
MichaelMao

เอกสารทั้งหมดที่ฉันกำลังอ่านแสดงคำเตือนเกี่ยวกับการย้ายถิ่นที่มีไขมันขนาดใหญ่ว่า "Don't use EnsureCreatedor EnsureDeletedor Database.Migratewill fail"
mwilson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.