初次使用EF6+MYSQL
这次的项目时间可拉得够长的,定制开发就是这样。客户真正用上了才能基本上不再改了。起先项目是php实现的,改造成桌面程序。用.net winform开发,像这种小项目肯定要用EF了。 以前一直用4.0,用DB First,把向导生成的模型扩展一下,另写一个分部类来实现bulkcopy等这种特殊需求。一直都是这样简单的用着,懒得花时间成本去搞高深的AOP/IOC/泛型工厂... 有时候就是越简单越好,当只会捉老鼠的猫就好。 这次依然还是使用的DB First。
经验总结:
1、EF6之CRUD
EF向导自动转换成的DbContext类:
public partial class POS_Entities : DbContext { public POS_Entities() : base("name=POS_Entities") { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { throw new UnintentionalCodeFirstException(); } public virtual DbSet<bm_brandclasses> bm_brandclasses { get; set; } public virtual DbSet<bm_brandsubclasses> bm_brandsubclasses { get; set; } public virtual DbSet<bm_membermng> bm_membermng { get; set; } public virtual DbSet<bm_memcard_privilege> bm_memcard_privilege { get; set; } public virtual DbSet<bm_memcardclasses> bm_memcardclasses { get; set; } public virtual DbSet<bm_pointmng> bm_pointmng { get; set; } public virtual DbSet<ls_busimng> ls_busimng { get; set; } public virtual DbSet<ls_cashierlist> ls_cashierlist { get; set; } public virtual DbSet<ls_cashierlist_detail> ls_cashierlist_detail { get; set; } public virtual DbSet<ls_cashiernum> ls_cashiernum { get; set; } public virtual DbSet<ls_commoditymng> ls_commoditymng { get; set; } public virtual DbSet<ls_couponmng> ls_couponmng { get; set; } public virtual DbSet<ls_couponmng_list> ls_couponmng_list { get; set; } public virtual DbSet<ls_handovermng> ls_handovermng { get; set; } public virtual DbSet<ls_promotionclasses> ls_promotionclasses { get; set; } public virtual DbSet<ls_promotionmng> ls_promotionmng { get; set; } public virtual DbSet<ls_promotionmng_subact> ls_promotionmng_subact { get; set; } public virtual DbSet<ls_promotionsubclasses> ls_promotionsubclasses { get; set; } public virtual DbSet<sa_manager> sa_manager { get; set; } public virtual DbSet<sa_num_auto> sa_num_auto { get; set; } public virtual DbSet<sa_role> sa_role { get; set; } public virtual DbSet<ls_leasemng> ls_leasemng { get; set; } }
我的扩展类:
public partial class POS_Entities { private static readonly BindingFlags bf = BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic; private static readonly Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); public string ConnectionString { get { var ecb = new EntityConnectionStringBuilder(config.ConnectionStrings.ConnectionStrings["POS_Entities"].ConnectionString); return ecb.ProviderConnectionString; } } /// <summary> /// 执行Sql语句 /// </summary> /// <param name="sql"></param> /// <returns></returns> public int ExecuteSql(string sql) { return this.Database.ExecuteSqlCommand(sql, new object[] { }); } /// <summary> /// 返回查询的第一行第一列的值 /// </summary> /// <param name="sqlText"></param> /// <returns></returns> public object ExecuteScalar(string sqlText) { var db = this.Database.Connection; try { if (db.State != ConnectionState.Open) db.Open(); var cmd = db.CreateCommand(); cmd.CommandText = sqlText; return cmd.ExecuteScalar(); } finally { db.Close(); } } /// <summary> /// 使用同一个事物批量提交SQL /// </summary> /// <param name="sqls"></param> public bool BatchExecuteSqlWithTrans(List<string> sqls) { var dbConn = this.Database.Connection; if (dbConn.State != ConnectionState.Open) dbConn.Open(); var cmd = dbConn.CreateCommand(); var st = dbConn.BeginTransaction(); try { HashSet<string> list = new HashSet<string>(); cmd.Transaction = st; sqls.ForEach(delegate (string sqlTxt) { cmd.CommandText = sqlTxt; cmd.ExecuteNonQuery(); }); st.Commit(); return true; } catch (Exception ex) { st.Rollback(); return false; throw new Exception(ex.Message); } finally { dbConn.Close(); } } #region MySql 批量插入 /// <summary> /// 批量插入 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="tbName">要插入的目标表名称</param> /// <param name="columeArr">要插入的列名数组</param> /// <param name="listModels">要插入的实体数组</param> /// <param name="strConnection">数据库连接字符串</param> /// <returns></returns> public int BatchInsert<T>(string tbName, string[] columeArr, IList<T> listModels) where T : class, new() { if (listModels == null || listModels.Count == 0) { throw new ArgumentException("没有需要批量插入的数据"); } int columes = columeArr.Length; StringBuilder sb = new StringBuilder(); sb.AppendFormat("INSERT INTO {0} ", tbName); AppendColumes(sb, columeArr); sb.Append(" VALUES "); var listParamKeys = new List<string>();//参数的键值 string paramKey = string.Empty; for (int i = 0; i < listModels.Count; i++) //构造参数 { sb.Append("("); for (int j = 0; j < columes; j++) { paramKey = string.Format("@v_{0}_{1}", columeArr[j], columes * i + j); //参数前必须加入@ sb.Append(paramKey); listParamKeys.Add(paramKey); if (j < columes - 1) { sb.Append(","); } } sb.Append("),"); } var listParamValues = new List<object>(); for (int i = 0; i < listModels.Count; i++) //构造参数值数组 { FastPrepareParamValue<T>(listModels[i], columeArr, listParamValues); } string sqlText = sb.ToString().Trim(',') + ";"; int affectNum = ExecuteNonQuery(ConnectionString, CommandType.Text, sqlText, PrepareParameters(listParamKeys.ToArray(), listParamValues.ToArray())); return affectNum; } private int ExecuteNonQuery(string connectionString, CommandType cmdType, string cmdText, params MySqlParameter[] commandParameters) { MySqlCommand cmd = new MySqlCommand(); using (MySqlConnection conn = new MySqlConnection(connectionString)) { PrepareCommand(conn, null, cmd, cmdType, cmdText, commandParameters); int val = cmd.ExecuteNonQuery(); cmd.Parameters.Clear(); return val; } } private MySqlParameter[] PrepareParameters(string[] paramKeys, object[] paramValues) { MySqlParameter[] clonedParms = new MySqlParameter[paramKeys.Length]; for (int i = 0; i < paramKeys.Length; i++) { clonedParms[i] = new MySqlParameter(paramKeys[i], paramValues[i]); } return clonedParms; } private void PrepareCommand(MySqlConnection conn, MySqlTransaction trans, MySqlCommand cmd, CommandType cmdType, string cmdText, MySqlParameter[] cmdParms) { if (conn.State != ConnectionState.Open) conn.Open(); cmd.Connection = conn; cmd.CommandText = cmdText; if (trans != null) cmd.Transaction = trans; cmd.CommandType = cmdType; if (cmdParms != null) { foreach (MySqlParameter parm in cmdParms) cmd.Parameters.Add(parm); } } private void AppendColumes(StringBuilder sb, string[] columeArr) { if (columeArr == null || columeArr.Length == 0) { throw new ArgumentException("插入列不能为空"); } sb.Append("("); for (int i = 0; i < columeArr.Length; i++) { sb.Append(columeArr[i]); if (i < columeArr.Length - 1) { sb.Append(","); } } sb.Append(")"); } private void FastPrepareParamValue<T>(T model, string[] columeArr, List<object> listPramValues) { object objValue = null; var objType = model.GetType(); var properties = objType.GetProperties(bf); foreach (var columeName in columeArr) { foreach (var propInfo in properties) { if (string.Compare(columeName, propInfo.Name, true) != 0) { continue; } try { objValue = propInfo.GetValue(model); } catch { objValue = null; } finally { listPramValues.Add(objValue); } break; } } } #endregion public DataSet Query(CommandType cmdType, string cmdText, params MySqlParameter[] commandParameters) { MySqlCommand cmd = new MySqlCommand(); MySqlConnection conn = new MySqlConnection(ConnectionString); try { PrepareCommand(conn, null, cmd, cmdType, cmdText, commandParameters); MySqlDataAdapter adapter = new MySqlDataAdapter(); adapter.SelectCommand = cmd; DataSet ds = new DataSet(); adapter.Fill(ds); cmd.Parameters.Clear(); conn.Close(); return ds; } catch (Exception e) { throw e; } } }
扩展类的目的是使用熟悉的原生ado.net的方式来避免趟EF的坑(未必有的)。
1)增
db.cashierlist.Add(bill); try { db.Entry<cashierlist>(bill).State = System.Data.Entity.EntityState.Added; db.SaveChanges(); result.Success = true; result.Message = "操作成功!"; result.Result = bill; } catch (Exception ex) { var exx = ex.InnerException == null ? ex : ex.InnerException; result.Message = exx.Message; }
这里好像就是需要写"db.Entry<T>(bill).State = System.Data.Entity.EntityState.Added",显式的标记为"System.Data.Entity.EntityState.Added",即新增状态。
2)改和删好像跟以前一样的没啥说的。
3)查
var dbExists = db.ls_cashierlist.SqlQuery("select * from ls_cashierlist where cl_sn='" + bill.cl_sn + "'").FirstOrDefaultAsync().Result;
toListAsync()/FirstOrDefaultAsync()/SingleAsync()等陌生方法给我带来了困惑。以前同步方式调用这些api习惯了,现在统统变成了支持异步的方法了。不深究,改成同步的方式就是取XXX方法的().Result就行了。
2、关于.net连接mysql
1)MYSQL某些字段日期时间类型如果是允许空值时且存了空值那么,MYSQL默认写的是“0000-00-00 00:00:00”这样子,这个其实在连接字符串里面有几个选项可以参考一下,就是"allowzerodatetime"和"convertzerodatetime".
2)中文乱码,需要指定字符集,比如:characterset=utf8
3、Http get或者post请求超时时间控制
由于winform客户端在发起Http get或者post请求时刚开始设置的超时时间是5秒,后来发现请求在服务器端的webapi中处理时间比较久时,客户端就直接什么都不返回,自动被终止了,两边都没有异常。自动被超时时间这个参数处理掉了。
还以为是那些个异步方法的使用有问题就是说webapi在返回值的时候没有等到异步方法返回就开始返回给客户端了。这是错误的,前面已经把异步改成同步了。
后来增加超时时间就能正常返回了。记录一下,我自己能看懂就好
还是老规矩,晒上几张图作为结束: