2016-12-08 9 views
4

私は実行時にプラグインをサポートする必要があるasp.netコアプロジェクトを持っており、プラグインされたものに基づいてデータベーステーブルを生成する必要がありますプラグインはそれぞれ別々のプロジェクトに分割され、独自のDbContextクラスを持っています。使用するプラグインは、実行時にのみコンパイル時にはわかりません。EFコア - ランタイム中にデータベースに新しいテーブルを追加

EFコアでは、既存のデータベースにテーブルを追加するだけの "UpdateDatabase"のようなメソッドがあると思っていましたが、間違っていました。これを達成する方法はありますか?私はプラグインごとに別々のデータベースを生成することができましたが、それは私が心に留めていたものではありません。一つのデータベースにすべてのテーブルが必要でした。ここで

は "HRContext" プラグインのコードです:ここで

using System; 
using System.Collections.Generic; 
using System.Text; 
using Microsoft.EntityFrameworkCore; 
using Plugins.HR.Models.Entities; 

namespace Plugins.HR.Contexts 
{ 
    public class HrContext : DbContext 
    { 
     public HrContext() 
     { 
     } 
     public HrContext(DbContextOptions<HrContext> contextOptions) : base(contextOptions) 
     { 
     } 

     protected override void OnModelCreating(ModelBuilder modelBuilder) 
     { 
      modelBuilder.HasDefaultSchema("HR"); 
      base.OnModelCreating(modelBuilder); 
     } 

     public DbSet<Address> Address { get; set; } 
     public DbSet<Attendance> Attendance { get; set; } 
     public DbSet<Department> Departments { get; set; } 
     public DbSet<Employee> Employees { get; set; } 
     public DbSet<JobTitle> JobTitles { get; set; } 
    } 
} 

は "CoreContext" プラグインのコードの別の部分があります:Startup.csで

using System; 
using System.Collections.Generic; 
using System.Text; 
using Core.Data.Models; 
using Microsoft.EntityFrameworkCore; 
namespace Core.Data.Contexts 
{ 
    public class CoreContext : DbContext 
    { 
     public CoreContext() 
     { 

     } 
     public CoreContext(DbContextOptions<CoreContext> contextOptions) : base(contextOptions) 
     { 

     } 

     protected override void OnModelCreating(ModelBuilder modelBuilder) 
     { 
      modelBuilder.HasDefaultSchema("Core"); 
      base.OnModelCreating(modelBuilder); 

     } 
     public DbSet<Test> Tests { get; set; } 
    } 
} 

マイConfigureServices方法:

public void ConfigureServices(IServiceCollection services) 
{ 
    services.AddDbContext<CoreContext>(options => options.UseSqlServer("Data source = localhost; initial catalog = Company.Core; integrated security = true;")) 
    .AddDbContext<HrContext>(options => options.UseSqlServer("Data source = localhost; initial catalog = Company.HR; integrated security = true;")); 

    // Add framework services. 
    services.AddMvc(); 
} 

接続文字列を同じに変更しようとすると、遅かれ早かれ1つのプラグインのテーブルが存在しないことを示すエラーです。私は "EnsureCreated"を試みましたが、それもうまくいきませんでした。

+0

私もプラグインを使用してプロジェクトを作成し、実行時にテーブルを作成する必要がありますよ - EFの移行にオプションではありません。あなたは解決策を見つけましたか? – Matt

+0

@Matt悲しいことに、私はちょうど私のプロジェクトのための異なったプラグインのための異なったデータベースと解決しなければならなかった。 – chelem7

+0

私は解決策を見つけました。私は以下の答えとしてそれを投稿しました。がんばろう – Matt

答えて

0

私は同じ問題がありました。ここでは、数日前にはGitHub上の私の解決策を参照してください:EF Core Issue #9238

必要なものは、次のようなものです:

// Using an interface, so that we can swap out the implementation to support PG or MySQL, etc if we wish... 
public interface IEntityFrameworkHelper 
{ 
    void EnsureTables<TContext>(TContext context) 
     where TContext : DbContext; 
} 

// Default implementation (SQL Server) 
public class SqlEntityFrameworkHelper : IEntityFrameworkHelper 
{ 
    public void EnsureTables<TContext>(TContext context) 
     where TContext : DbContext 
    { 
     string script = context.Database.GenerateCreateScript(); // See issue #2943 for this extension method 
     if (!string.IsNullOrEmpty(script)) 
     { 
      try 
      { 
       var connection = context.Database.GetDbConnection(); 

       bool isConnectionClosed = connection.State == ConnectionState.Closed; 

       if (isConnectionClosed) 
       { 
        connection.Open(); 
       } 

       var existingTableNames = new List<string>(); 
       using (var command = connection.CreateCommand()) 
       { 
        command.CommandText = "SELECT table_name from INFORMATION_SCHEMA.TABLES WHERE table_type = 'base table'"; 

        using (var reader = command.ExecuteReader()) 
        { 
         while (reader.Read()) 
         { 
          existingTableNames.Add(reader.GetString(0).ToLowerInvariant()); 
         } 
        } 
       } 

       var split = script.Split(new[] { "CREATE TABLE " }, StringSplitOptions.RemoveEmptyEntries); 
       foreach (string sql in split) 
       { 
        var tableName = sql.Substring(0, sql.IndexOf("(", StringComparison.OrdinalIgnoreCase)); 
        tableName = tableName.Split('.').Last(); 
        tableName = tableName.Trim().TrimStart('[').TrimEnd(']').ToLowerInvariant(); 

        if (existingTableNames.Contains(tableName)) 
        { 
         continue; 
        } 

        try 
        { 
         using (var createCommand = connection.CreateCommand()) 
         { 
          createCommand.CommandText = "CREATE TABLE " + sql.Substring(0, sql.LastIndexOf(";")); 
          createCommand.ExecuteNonQuery(); 
         } 
        } 
        catch (Exception) 
        { 
         // Ignore 
        } 
       } 

       if (isConnectionClosed) 
       { 
        connection.Close(); 
       } 
      } 
      catch (Exception) 
      { 
       // Ignore 
      } 
     } 
    } 
} 

その後Startup.Configure()の終わりに、私はIEntityFrameworkHelperインスタンスを解決してそれを使用しますDbContextのインスタンスを呼び出してEnsureTables()を呼び出します。

1つの問題は、ではないスクリプトの部分をまだ考慮する必要があります。CREATE TABLE文。たとえば、CREATE INDEXステートメントです。

私は、例えばCreateTable<TEntity>()メソッドをIRelationalDatabaseCreatorに追加するなど、きれいなソリューションを要求しました。私はGenerateCreateScript()ためのコードをポストするのを忘れているけれども...

EDIT

のための私の息を保持していません。以下を参照してください:

using System.Text; 
using Microsoft.EntityFrameworkCore.Infrastructure; 
using Microsoft.EntityFrameworkCore.Metadata; 
using Microsoft.EntityFrameworkCore.Migrations; 
using Microsoft.EntityFrameworkCore.Storage; 

public static class DatabaseFacadeExtensions 
{ 
    public static string GenerateCreateScript(this DatabaseFacade database) 
    { 
     var model = database.GetService<IModel>(); 
     var migrationsModelDiffer = database.GetService<IMigrationsModelDiffer>(); 
     var migrationsSqlGenerator = database.GetService<IMigrationsSqlGenerator>(); 
     var sqlGenerationHelper = database.GetService<ISqlGenerationHelper>(); 

     var operations = migrationsModelDiffer.GetDifferences(null, model); 
     var commands = migrationsSqlGenerator.Generate(operations, model); 

     var stringBuilder = new StringBuilder(); 
     foreach (var command in commands) 
     { 
      stringBuilder 
       .Append(command.CommandText) 
       .AppendLine(sqlGenerationHelper.BatchTerminator); 
     } 

     return stringBuilder.ToString(); 
    } 
} 

それは、ここで見つけたコードに基づいています:EF Core Issue #2943

関連する問題