初次使用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在返回值的時候沒有等到異步方法返回就開始返回給客戶端了。這是錯誤的,前面已經把異步改成同步了。
后來增加超時時間就能正常返回了。記錄一下,我自己能看懂就好
還是老規矩,曬上幾張圖作為結束: