che*_*em7 6 c# asp.net entity-framework
我有一个asp.net核心项目,它需要能够在运行时支持插件,因此,我需要根据已插入的内容生成数据库表.插件分为不同的项目,他们有他们自己的DbContext类.在编译期间,仅在运行时才知道要使用的插件.
现在在EF Core我认为会有像"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; }
}
}
Run Code Online (Sandbox Code Playgroud)
这是"CoreContext"插件的另一段代码:
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; }
}
}
Run Code Online (Sandbox Code Playgroud)
我在Startup.cs中的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();
}
Run Code Online (Sandbox Code Playgroud)
如果我尝试将连接字符串更改为相同,迟早会收到一条错误消息,指出一个插件的表不存在.我试过"EnsureCreated",但这也行不通.
我遇到过同样的问题。几天前在 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
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后在最后Startup.Configure(),我解析一个IEntityFrameworkHelper实例并将其与 的实例一起使用来DbContext调用EnsureTables()。
一个问题是我仍然需要考虑脚本中不是CREATE TABLE语句的部分。例如,CREATE INDEX语句。
我要求他们给我们一个干净的解决方案,例如:CreateTable<TEntity>()向IRelationalDatabaseCreator. 但我并没有为此屏住呼吸……
编辑
我忘了发布 的代码GenerateCreateScript()。见下文:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
它基于此处找到的代码:EF Core Issue #2943
| 归档时间: |
|
| 查看次数: |
2027 次 |
| 最近记录: |