Entity Framwork 在运行时获得连接字符串并触发自动迁移
介绍
此示例代码用于在运行时间指定您使用的上下文(连接、初始化器和配置)时使用实体框架 5。
背景
我花了两个星期收集信息如何运行自动迁移从代码没有包管理器控制台,以及如何做到这一点,当我想指定连接字符串在运行时间。此外,我不想使用任何。配置文件或其他静态配置。
使用代码
我使用不同的上下文因子进行测试、开发和生产。结合 Ninject 的 UnitOfWork 模式, 我可以使用正确的上下文来达到正确的目的。这里是我工厂的单位测试样本代码。
DbContext(在这种情况下)本身只包含DbSets和处理程序,以指定在LuentAPI中完成的映射。WPH_BusinessContextOnModelCreating
注意:如果出现数据丢失,迁移将抛出一个例外。还有一个样本如何设置Db新剂,但它被评论出来。
C#
public class TestDBContextFactory : IDbContextFactory<WPH_BusinessContext>
{
public WPH_BusinessContext Create()
{
//set connection string at runtime here
string connectionString = Properties.Settings.Default.TestDB;
var connectionInfo = new DbConnectionInfo(connectionString, "System.Data.SqlClient");
bool doInitialize = false;
//Set initializer here
//Database.SetInitializer<WPH_BusinessContext>(new DropCreateDatabaseAlways<WPH_BusinessContext>());
//doInitialize = true;
var contextInfo = new DbContextInfo(typeof(WPH_BusinessContext), connectionInfo);
var context = contextInfo.CreateInstance();
context.Configuration.LazyLoadingEnabled = true;
context.Configuration.ProxyCreationEnabled = true;
if (doInitialize)
{
context.Database.Initialize(false);
}
bool doMigration = false;
try
{
doMigration = !context.Database.CompatibleWithModel(true);
}
catch (NotSupportedException)
{
//if there are no metadata for migration
doMigration = true;
}
if (doMigration)
{
var migrationConfig = new DbMigrationsConfiguration<WPH_BusinessContext>();
migrationConfig.AutomaticMigrationDataLossAllowed = false;
migrationConfig.AutomaticMigrationsEnabled = true;
migrationConfig.TargetDatabase = connectionInfo;
var migrator = new DbMigrator(migrationConfig);
migrator.Update();
}
return context as WPH_BusinessContext;
}
}
实际上,上述代码是有Bug的,即在初始化未执行迁移时,会报连接不上数据库,经实践修改如下:
using System; using System.Configuration; using System.Data.Entity; using System.Data.Entity.ModelConfiguration.Conventions; using UniverseSecret.Core.Data; using UniverseSecret.Core.Data.Ef; using UniverseSecret.Identity.Domain; using UniverseSecret.Identity.Domain.Entities; namespace UniverseSecret.Erp.Ef { public class ErpDbContext : DbContext { #region 记录集,对应数据库表 /// <summary> /// Gets or sets the Permissions /// </summary> public DbSet<Permission> Permissions { get; set; } #endregion //private readonly string _connectionName; private bool ModifyTableName = Convert.ToBoolean(ConfigurationManager.AppSettings["ModifyTableName"]); /// <summary> /// The OnModelCreating /// </summary> /// <param name="modelBuilder">The modelBuilder<see cref="DbModelBuilder"/></param> protected override void OnModelCreating(DbModelBuilder modelBuilder) { //移除表名为复数 modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); //自动添加实现EntityTypeConfiguration的类 modelBuilder.Configurations.AddFromAssembly(EntityConfigLoader.Load()); //表名及字段大小写调整 if (ModifyTableName) { //表名调整 modelBuilder.Types().Configure(x => x.ToTable(EfStartup.SetTableName("", x.ClrType.Name))); //模式调整 modelBuilder.HasDefaultSchema(EfStartup.GetSchemaByConString(this.Database.Connection.ConnectionString)); //modelBuilder.HasDefaultSchema(""); //列名调整 modelBuilder.Properties().Configure(c => c.HasColumnName(c.ClrPropertyInfo.Name.ToUpper())); } base.OnModelCreating(modelBuilder); } /// <summary> /// The Set /// </summary> /// <typeparam name="TEntity"></typeparam> /// <returns>The <see cref="DbSet{TEntity}"/></returns> public new DbSet<TEntity> Set<TEntity>() where TEntity : Entity<Guid> { return base.Set<TEntity>(); } } }
using System; using System.Configuration; using System.Data.Entity.Infrastructure; using System.Data.Entity.Migrations; using UniverseSecret.Erp.Ef; namespace UniverseSecret.Erp.Server { /// <summary> /// 数据库工厂 /// </summary> public class DbContextFactory : IDbContextFactory<ErpDbContext> { public ErpDbContext Create() { //运行时在此处设置连接字符 var config = ConfigurationManager.ConnectionStrings["ErpConnection"]; string connectionString = config.ConnectionString; var connectionInfo = new DbConnectionInfo(connectionString, config.ProviderName); //是否初始化 bool doInitialize = Convert.ToBoolean(ConfigurationManager.AppSettings["DoInitializeDb"]); //在这设置初始化 //Database.SetInitializer<ErpDbContext>(new DropCreateDatabaseAlways<ErpDbContext>()); //doInitialize = true; //创建数据库实例 var contextInfo = new DbContextInfo(typeof(ErpDbContext), connectionInfo); var context = contextInfo.CreateInstance(); //允许懒加载 context.Configuration.LazyLoadingEnabled = true; //允许创建代理 context.Configuration.ProxyCreationEnabled = true; //如果初始化 if (doInitialize) { context.Database.Initialize(false); //执行数据库迁移 DoMigration(connectionInfo); } else { bool doMigration = false; try { //检查数据库与migration是否一致,以判断是否要做自动迁移 doMigration = !context.Database.CompatibleWithModel(true); } catch (NotSupportedException) { //if there are no metadata for migration doMigration = true; } if (doMigration) { DoMigration(connectionInfo); } } return context as ErpDbContext; } /// <summary> /// 执行数据库迁移 /// </summary> /// <param name="connectionInfo"></param> private void DoMigration(DbConnectionInfo connectionInfo) { var migrationConfig = new DbMigrationsConfiguration<ErpDbContext>(); //是否允许自动迁移时数据丢失 //当变更字段长度(变小)或改名时或改变数据类型时,可能导致数据丢失 migrationConfig.AutomaticMigrationDataLossAllowed = true; //是否允许自动迁移 migrationConfig.AutomaticMigrationsEnabled = true; //迁移的目标数据库 migrationConfig.TargetDatabase = connectionInfo; //执行迁移 var migrator = new DbMigrator(migrationConfig); migrator.Update(); } } }
using System.Reflection; namespace UniverseSecret.Identity.Domain { public class EntityConfigLoader { public static Assembly Load() { return Assembly.GetExecutingAssembly(); } } }
using System; using System.Configuration; using System.Linq; namespace UniverseSecret.Core.Data.Ef { /// <summary> /// EF预热 /// </summary> public static class EfStartup { /// <summary> /// 通过数据库连接名称获取模式名称(Oracle用) /// </summary> /// <param name="conName"></param> /// <returns></returns> public static string GetSchemaByConName(string conName) { if (string.IsNullOrEmpty(conName)) throw new ArgumentNullException(nameof(conName)); var c = ConfigurationManager.ConnectionStrings[conName]; if (c == null) throw new InvalidOperationException("Db Connection Not Found :" + conName); if (string.IsNullOrEmpty(c.ConnectionString)) throw new Exception($"ConnectionString {conName} is empty!"); //获取User Id var sp = c.ConnectionString.Split(';'); var u = sp.Where(x => !string.IsNullOrEmpty(x) && x.Contains("=")).FirstOrDefault(y => y.Trim().ToLower().StartsWith("user")); if (u == null) throw new Exception($"ConnectionString {conName} is invalid !"); sp = u.Split('='); return sp[1].Trim().ToUpper(); } /// <summary> /// 通过数据库连接字符获取模式名称(Oracle用) /// </summary> /// <param name="conString"></param> /// <returns></returns> public static string GetSchemaByConString(string conString) { if (string.IsNullOrEmpty(conString)) throw new ArgumentNullException(nameof(conString)); //获取User Id var sp = conString.Split(';'); var u = sp.Where(x => !string.IsNullOrEmpty(x) && x.Contains("=")).FirstOrDefault(y => y.Trim().ToLower().StartsWith("user")); if (u == null) throw new Exception($"ConnectionString is invalid !"); sp = u.Split('='); return sp[1].Trim().ToUpper(); } /// <summary> /// 调整表名称 /// </summary> /// <param name="preFix">表名前缀</param> /// <param name="tableName"></param> /// <returns></returns> public static string SetTableName(string preFix, string tableName) { var n2 = (preFix + tableName).ToUpper(); return n2; } } }
using Autofac; namespace UniverseSecret.Core.AutoFac { public interface IDependencyRegistrar { void Register(ContainerBuilder builder); int Order { get; } } }
using System; using System.Collections.Generic; using System.Reflection; namespace UniverseSecret.Core.AutoFac { public interface ITypeFinder { IList<Assembly> GetAssemblies(); IEnumerable<Type> FindDerivedTypes(Type baseType); } }
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace UniverseSecret.Core.AutoFac { public class TypeFinder : ITypeFinder { public IList<Assembly> GetAssemblies() { //由于注册文件可能分布在不同类库,为此我们获取所有程序集,而不是当前程序集 return AppDomain.CurrentDomain.GetAssemblies(); } /// <summary> /// 查找派生类型 /// </summary> /// <param name="baseType"></param> /// <returns></returns> public IEnumerable<Type> FindDerivedTypes(Type baseType) { var types = new List<Type>(); foreach (var assembly in GetAssemblies()) { var typeArray = assembly.GetTypes() .Where(type => !string.IsNullOrEmpty(type.Namespace)) .Where(type => type.GetInterface(baseType.Name) == baseType).ToArray(); if (typeArray.Length > 0) { types.AddRange(typeArray); } } return types; } } }
using System; using System.Collections.Generic; using System.Linq; using Autofac; using UniverseSecret.Core.AutoFac; namespace UniverseSecret.Erp.Server { public class ContainerManager { private static readonly TypeFinder _typeFinder = new TypeFinder(); public static IContainer Container = InitContainer(); private static IContainer InitContainer() { //autofac 容器 ContainerBuilder builder = new ContainerBuilder(); #region 反射 核心 //通过反射得到继承IDependencyRegistrar该接口的类成员 var types = _typeFinder.FindDerivedTypes(typeof(IDependencyRegistrar)); var drInstances = new List<IDependencyRegistrar>(); //创建实例 foreach (var drType in types) drInstances.Add((IDependencyRegistrar)Activator.CreateInstance(drType)); //sort drInstances = drInstances.AsQueryable().OrderBy(t => t.Order).ToList(); //执行Register方法 foreach (var dependencyRegistrar in drInstances) dependencyRegistrar.Register(builder); #endregion //then return builder.Build(); } } }
using Autofac; using UniverseSecret.Core.AutoFac; using UniverseSecret.Erp.Ef; namespace UniverseSecret.Erp.Server { public class DependencyRegistrar : IDependencyRegistrar { public void Register(ContainerBuilder builder) { #region 注册数据库上下文 builder.Register(c => new DbContextFactory().Create()).As<ErpDbContext>(); #endregion #region 注册仓储 //builder.RegisterType<SqlDbHelper>().As<DbHelper>() // .InstancePerRequest() // .WithParameter( // "connectionStringName", // "Sample.IdSvr"); #endregion #region 注册日志 //builder.RegisterType<NLogLogger>().As<ILogger>().InstancePerDependency(); #endregion #region 注册服务 ////按构造参数1注册 //builder.RegisterType<SugarCodeProvider>() // .UsingConstructor(typeof(AggregateInfo)) // .Named<ICodeProvider>("SugarCodeProvider"); #endregion } public int Order => 1; } }
//数据库初始化 ContainerManager.Container.Resolve<ErpDbContext>();
using System; using System.ComponentModel; using UniverseSecret.Core.Sugar; namespace UniverseSecret.Identity.Domain.Entities { public class Permission : SugarGuidEntity { public Guid ParentId { get; set; } public string Group { get; set; } /// <summary> /// 权限路径码,如:1609ABD8-0EA5-4C8B-9B19-45E6D9C648B5.E3CE9529-BA03-49BF-A0D2-7A9EE603524D /// </summary> public string Code { get; set; } /// <summary> /// 权限的Key,例如:System.Role.Create /// </summary> public string Key { get; set; } /// <summary> /// 显示的名称,可作菜单,例如“新增角色” /// </summary> public string DisplayName { get; set; } [DisplayName("圖示")] public string IconImage { get; set; } [DisplayName("描述")] public string Description { get; set; } /// <summary> /// 本级目录的下排列顺序 /// </summary> public int SortIndex { get; set; } /// <summary> /// 是否可见(作为菜单还是隐式的权限) /// </summary> public bool Visible { get; set; } /// <summary> /// 是否免鉴权 /// </summary> public bool Exemption { get; set; } /// <summary> /// 是否允许不登录也可用 /// </summary> public bool AllowAnonymous { get; set; } /// <summary> /// 连接外部的Url /// </summary> public string ExternalUrl { get; set; } public Permission() { } public Permission(Guid id, Guid parentId, string name, string group, string code, string key, string iconImage, string description, int sortIndex, bool visible, bool exemption, bool allowAnonymous, string externalUrl) { if (name == null) throw new ArgumentNullException(nameof(name)); Id = id; ParentId = parentId; DisplayName = name; Code = code; Group = group; Key = key; IconImage = iconImage; Description = description; SortIndex = sortIndex; Visible = visible; Exemption = exemption; AllowAnonymous = allowAnonymous; ExternalUrl = externalUrl; } } }
using System.Data.Entity.ModelConfiguration; using UniverseSecret.Identity.Domain.Entities; namespace UniverseSecret.Identity.Domain.Configurations.Systems { /// <summary> /// 权限表配置 /// </summary> public class PermissionConfig : EntityTypeConfiguration<Permission> { public PermissionConfig() { ToTable("Sys_Permission"); HasKey(o => o.Id); //主键 Property(o => o.DisplayName).IsRequired().HasMaxLength(50); //最大长度 Property(o => o.Code).IsRequired().HasMaxLength(512); //最大长度 Property(o => o.Key).IsRequired().HasMaxLength(127); Property(o => o.IconImage).HasMaxLength(255); Property(o => o.Description).HasMaxLength(255); //描述 Property(o => o.SortIndex).IsRequired(); //排序 Property(o => o.Exemption).IsRequired(); //免鉴权 Property(o => o.Description).HasMaxLength(255); Property(o => o.Group).IsRequired().HasMaxLength(50); Property(o => o.ExternalUrl).HasMaxLength(255); } } }
<appSettings> <add key="DoInitializeDb" value="true" /> <!--是否修改表名,如Oracle要转换成大写,并加上用户名--> <add key="ModifyTableName" value="false" /> </appSettings>
浙公网安备 33010602011771号