WTM框架在开发过程中如何动态迁移表和创建表
官方迁移方法:https://wtmdoc.walkingtec.cn/#/Data/Migration
但是在实际开发过程中使用Add-Migration 方法迁移会发现,把系统内置的表也全部带出来了,自己需要手动删。
在参看官方的源码后发现作者也想到了这个问题(还在开发当中,可以查看EmptyContext),毕竟这不优雅,但是在稳定版本内没有合适的解决方式,故我借鉴了一下(抄袭)。
public partial class EmptyContext : DbContext, IDataContext { /// <summary> /// Commited /// </summary> public bool Commited { get; set; } /// <summary> /// IsFake /// </summary> public bool IsFake { get; set; } /// <summary> /// CSName /// </summary> public string CSName { get; set; } public DBTypeEnum DBType { get; set; } /// <summary> /// FrameworkContext /// </summary> public EmptyContext() { CSName = "default"; DBType = DBTypeEnum.SqlServer; } /// <summary> /// FrameworkContext /// </summary> /// <param name="cs"></param> public EmptyContext(string cs) { CSName = cs; } public EmptyContext(string cs, DBTypeEnum dbtype) { CSName = cs; DBType = dbtype; } public IDataContext CreateNew() { return (IDataContext)this.GetType().GetConstructor(new Type[] { typeof(string), typeof(DBTypeEnum) }).Invoke(new object[] { CSName, DBType }); } public IDataContext ReCreate() { return (IDataContext)this.GetType().GetConstructor(new Type[] { typeof(string), typeof(DBTypeEnum) }).Invoke(new object[] { CSName, DBType }); } /// <summary> /// 将一个实体设为填加状态 /// </summary> /// <param name="entity">实体</param> public void AddEntity<T>(T entity) where T : TopBasePoco { this.Entry(entity).State = EntityState.Added; } /// <summary> /// 将一个实体设为修改状态 /// </summary> /// <param name="entity">实体</param> public void UpdateEntity<T>(T entity) where T : TopBasePoco { this.Entry(entity).State = EntityState.Modified; } /// <summary> /// 将一个实体的某个字段设为修改状态,用于只更新个别字段的情况 /// </summary> /// <typeparam name="T">实体类</typeparam> /// <param name="entity">实体</param> /// <param name="fieldExp">要设定为修改状态的字段</param> public void UpdateProperty<T>(T entity, Expression<Func<T, object>> fieldExp) where T : TopBasePoco { var set = this.Set<T>(); if (set.Local.AsQueryable().CheckID(entity.GetID()).FirstOrDefault() == null) { set.Attach(entity); } this.Entry(entity).Property(fieldExp).IsModified = true; } /// <summary> /// UpdateProperty /// </summary> /// <typeparam name="T"></typeparam> /// <param name="entity"></param> /// <param name="fieldName"></param> public void UpdateProperty<T>(T entity, string fieldName) where T : TopBasePoco { var set = this.Set<T>(); if (set.Local.AsQueryable().CheckID(entity.GetID()).FirstOrDefault() == null) { set.Attach(entity); } this.Entry(entity).Property(fieldName).IsModified = true; } /// <summary> /// 将一个实体设定为删除状态 /// </summary> /// <param name="entity">实体</param> public void DeleteEntity<T>(T entity) where T : TopBasePoco { var set = this.Set<T>(); if (set.Local.AsQueryable().CheckID(entity.GetID()).FirstOrDefault() == null) { set.Attach(entity); } set.Remove(entity); } /// <summary> /// CascadeDelete /// </summary> /// <typeparam name="T"></typeparam> /// <param name="entity"></param> public void CascadeDelete<T>(T entity) where T : TopBasePoco, ITreeData<T> { if (entity != null && entity.ID != Guid.Empty) { var set = this.Set<T>(); var entities = set.Where(x => x.ParentId == entity.ID).ToList(); if (entities.Count > 0) { foreach (var item in entities) { CascadeDelete(item); } } DeleteEntity(entity); } } /// <summary> /// GetCoreType /// </summary> /// <param name="t"></param> /// <returns></returns> public Type GetCoreType(Type t) { if (t != null && t.IsNullable()) { if (!t.GetTypeInfo().IsValueType) { return t; } else { if ("DateTime".Equals(t.GenericTypeArguments[0].Name)) { return typeof(string); } return Nullable.GetUnderlyingType(t); } } else { if ("DateTime".Equals(t.Name)) { return typeof(string); } return t; } } /// <summary> /// OnModelCreating /// </summary> /// <param name="modelBuilder"></param> protected override void OnModelCreating(ModelBuilder modelBuilder) { } /// <summary> /// OnConfiguring /// </summary> /// <param name="optionsBuilder"></param> protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { switch (DBType) { case DBTypeEnum.SqlServer: try { var Configs = GlobalServices.GetRequiredService<Configs>(); if (Configs.IsOldSqlServer == true) { optionsBuilder.UseSqlServer(CSName, op => op.UseRowNumberForPaging()); } else { optionsBuilder.UseSqlServer(CSName); } } catch { optionsBuilder.UseSqlServer(CSName, op => op.UseRowNumberForPaging()); } break; case DBTypeEnum.MySql: optionsBuilder.UseMySql(CSName); break; case DBTypeEnum.PgSql: optionsBuilder.UseNpgsql(CSName); break; case DBTypeEnum.Memory: optionsBuilder.UseInMemoryDatabase(CSName); break; case DBTypeEnum.SQLite: optionsBuilder.UseSqlite(CSName); break; case DBTypeEnum.Oracle: optionsBuilder.UseOracle(CSName); break; default: break; } try { var Configs = GlobalServices.GetRequiredService<Configs>();//如果是debug模式,将EF生成的sql语句输出到debug输出 if (Configs.IsQuickDebug) { optionsBuilder.UseLoggerFactory(LoggerFactory); } } catch { } base.OnConfiguring(optionsBuilder); } public static readonly LoggerFactory LoggerFactory = new LoggerFactory(new[] { new DebugLoggerProvider() }); /// <summary> /// 数据初始化 /// </summary> /// <param name="allModules"></param> /// <param name="IsSpa"></param> /// <returns>返回true表示需要进行初始化数据操作,返回false即数据库已经存在或不需要初始化数据</returns> public async virtual Task<bool> DataInit(object allModules, bool IsSpa) { bool rv = await Database.EnsureCreatedAsync(); return rv; } #region 执行存储过程返回datatable /// <summary> /// 执行存储过程,返回datatable结果集 /// </summary> /// <param name="command">存储过程名称</param> /// <param name="paras">存储过程参数</param> /// <returns></returns> public DataTable RunSP(string command, params object[] paras) { return Run(command, CommandType.StoredProcedure, paras); } #endregion public IEnumerable<TElement> RunSP<TElement>(string command, params object[] paras) { return Run<TElement>(command, CommandType.StoredProcedure, paras); } #region 执行Sql语句,返回datatable public DataTable RunSQL(string sql, params object[] paras) { return Run(sql, CommandType.Text, paras); } #endregion public IEnumerable<TElement> RunSQL<TElement>(string sql, params object[] paras) { return Run<TElement>(sql, CommandType.Text, paras); } #region 执行存储过程或Sql语句返回DataTable /// <summary> /// 执行存储过程或Sql语句返回DataTable /// </summary> /// <param name="sql">存储过程名称或Sql语句</param> /// <param name="commandType">命令类型</param> /// <param name="paras">参数</param> /// <returns></returns> public DataTable Run(string sql, CommandType commandType, params object[] paras) { DataTable table = new DataTable(); switch (this.DBType) { case DBTypeEnum.SqlServer: SqlConnection con = this.Database.GetDbConnection() as SqlConnection; SqlDataAdapter adapter = new SqlDataAdapter(); using (SqlCommand cmd = new SqlCommand(sql, con)) { adapter.SelectCommand = cmd; cmd.CommandTimeout = 2400; cmd.CommandType = commandType; if (paras != null) { foreach (var param in paras) cmd.Parameters.Add(param); } adapter.Fill(table); adapter.SelectCommand.Parameters.Clear(); } break; case DBTypeEnum.MySql: MySqlConnection mySqlCon = this.Database.GetDbConnection() as MySqlConnection; using (MySqlCommand cmd = new MySqlCommand(sql, mySqlCon)) { if (mySqlCon.State == ConnectionState.Closed) { mySqlCon.Open(); } cmd.CommandTimeout = 2400; cmd.CommandType = commandType; if (paras != null) { foreach (var param in paras) cmd.Parameters.Add(param); } MySqlDataReader dr = cmd.ExecuteReader(); table.Load(dr); dr.Close(); mySqlCon.Close(); } break; case DBTypeEnum.PgSql: Npgsql.NpgsqlConnection npgCon = this.Database.GetDbConnection() as Npgsql.NpgsqlConnection; using (Npgsql.NpgsqlCommand cmd = new Npgsql.NpgsqlCommand(sql, npgCon)) { if (npgCon.State == ConnectionState.Closed) { npgCon.Open(); } cmd.CommandTimeout = 2400; cmd.CommandType = commandType; if (paras != null) { foreach (var param in paras) cmd.Parameters.Add(param); } Npgsql.NpgsqlDataReader dr = cmd.ExecuteReader(); table.Load(dr); dr.Close(); npgCon.Close(); } break; case DBTypeEnum.SQLite: case DBTypeEnum.Oracle: var connection = this.Database.GetDbConnection(); var isClosed = connection.State == ConnectionState.Closed; if (isClosed) { connection.Open(); } using (var command = connection.CreateCommand()) { command.CommandText = sql; command.CommandTimeout = 2400; command.CommandType = commandType; if (paras != null) { foreach (var param in paras) command.Parameters.Add(param); } using (var reader = command.ExecuteReader()) { table.Load(reader); } } if (isClosed) { connection.Close(); } break; } return table; } #endregion public IEnumerable<TElement> Run<TElement>(string sql, CommandType commandType, params object[] paras) { IEnumerable<TElement> entityList = new List<TElement>(); DataTable dt = Run(sql, commandType, paras); entityList = EntityHelper.GetEntityList<TElement>(dt); return entityList; } public object CreateCommandParameter(string name, object value, ParameterDirection dir) { object rv = null; switch (this.DBType) { case DBTypeEnum.SqlServer: rv = new SqlParameter(name, value) { Direction = dir }; break; case DBTypeEnum.MySql: rv = new MySqlParameter(name, value) { Direction = dir }; break; case DBTypeEnum.PgSql: rv = new NpgsqlParameter(name, value) { Direction = dir }; break; case DBTypeEnum.SQLite: rv = new SqliteParameter(name, value) { Direction = dir }; break; case DBTypeEnum.Oracle: rv = new OracleParameter(name, value) { Direction = dir }; break; } return rv; } }
使用的时候
public class DataContext : FrameworkContext { public DataContext(string cs, DBTypeEnum dbtype) : base(cs, dbtype) { } } public class MEContext : EmptyContext { public DbSet<School> Schools { get; set; } public MEContext(string cs, DBTypeEnum dbtype) : base(cs, dbtype) { } } /// <summary> /// 为EF的Migration准备的辅助类,填写完整连接字符串和数据库类型 /// 就可以使用Add-Migration和Update-Database了 /// </summary> public class DataContextFactory : IDesignTimeDbContextFactory<MEContext> { public MEContext CreateDbContext(string[] args) { var dc= new MEContext("连接字符串", DBTypeEnum.MySql); return dc; } }
然后敲指令
Add-Migration Initia-IgnoreChanges -Context MEContext //先生成迁移的代码
Update-Database//然后迁移
因缘际会的相遇,自当有非同寻常的结局
QQ交流群:110826636