問題描述
如何將json對象數據保存到數據庫對應的表中。
分析問題
json數據內容不固定,json數據格式是固定的(name:value),數據庫表結構是固定的,在json數據格式 與 數據庫表結構之間建立一套對應規則,寫一套邏輯解析這套對應規則。
1、json對象對應的數據庫表需要確定。
2、json對象的節點對應數據庫表的列需要確定。
3、json對象的value類型需要確定下來,才能對value值做相應的操作。
4、數據庫表的主鍵列比較特殊,一般是自增長列,需要確定。
5、數據庫記錄一般是假刪除,有一個刪除狀態列,需要確定。
6、json對象的value的類型是json時,此列為外鍵,確定主表,獲取主表主鍵。
7、json對象的value的類型是array是,json此節點的數據為從表的數據,確定從表,對從表操作。
解決問題實例
一、自定義特性
定義了兩個特性,
EntityAttribute特性用於實體,PropertyAttribute特性用於屬性
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Standard.Frame.BEntity { /// <summary> /// 實體特性 /// </summary> [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] public class EntityAttribute : Attribute { public EntityAttribute(String entityName, String tableName) { EntityName = entityName; TableName = tableName; } /// <summary> /// 實體名稱 /// </summary> public string EntityName { get; set; } /// <summary> /// 表名稱 /// </summary> public string TableName { get; set; } } /// <summary> /// 屬性特性 /// </summary> [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public class PropertyAttribute : Attribute { public PropertyAttribute() { } /// <summary> /// json節點名稱 /// </summary> public string FieldName { get; set; } /// <summary> /// 列名稱 /// </summary> public string ColumnName { get; set; } /// <summary> /// 節點類型 /// </summary> public string NodeType { get; set; } /// <summary> /// 是否是主鍵 /// </summary> public string IsPK { get; set; } /// <summary> /// 是否刪除 /// </summary> public string IsDeletedFlag { get; set; } /// <summary> /// 主實體 /// </summary> public string MainEntity { get; set; } } }
二、在實體、屬性上添加特性
這里特性的作用:
1、建立json對象與數據庫表的對應關系。
2、
建立json節點的name與數據庫表列的對應關系。
3、指定json節點value的類型,類型主要有三類:字符串、json對象、數組。
4、標示出主鍵列。
5、標示出刪除標示列。
6、標示出主實體列,類似於關系表的外鍵關系。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Standard.Frame.Config; namespace Standard.Frame.BEntity { /// <summary> /// 項目信息 /// </summary> [Entity("PlanProject", "XMGL_20")] public class PlanProject:BaseBEntity { /// <summary> /// 項目ID /// </summary> [Property(FieldName = "planProjectID", ColumnName = "XMGL_20_COL_10", NodeType = NodeType.String, IsPK = BooleanType.True)] public string PlanProjectID { get; set; }
/// <summary> /// 項目名稱 /// </summary> [Property(FieldName = "planProjectName", ColumnName = "XMGL_20_COL_70", NodeType = NodeType.String)] public string PlanProjectName { get; set; }
/// <summary> /// 是否已刪除 /// </summary> [Property(FieldName = NodeOperation.Operation, ColumnName = "XMGL_20_COL_200", NodeType = NodeType.String, IsDeletedFlag = BooleanType.True)] public string IsDeleted { get; set; } /// <summary> /// 創建時間 /// </summary> [Property(FieldName = "createTime", ColumnName = "XMGL_20_COL_210", NodeType = NodeType.String)] public string CreateTime { get; set; } /// <summary> /// 創建人 /// </summary> [Property(FieldName = "creatorID", ColumnName = "XMGL_20_COL_220", NodeType = NodeType.Json, MainEntity = "User")] public string CreatorID { get; set; } /// <summary> /// 項目組成員 /// </summary> [Property(FieldName = "projectMember", NodeType = NodeType.Array, MainEntity = "ProjectMember")] public string ProjectMember { get; set; } } /// <summary> /// 項目組成員信息 /// </summary> [Entity("ProjectMember", "XMGL_220")] public class ProjectMember : BaseBEntity { /// <summary> /// 人員ID /// </summary> [Property(FieldName = "projectMemberID", ColumnName = "XMGL_220_COL_10", NodeType = NodeType.String, IsPK = BooleanType.True)] public string ProjectMemberID { get; set; } /// <summary> /// 姓名 /// </summary> [Property(FieldName = "projectMemberName", ColumnName = "XMGL_220_COL_20", NodeType = NodeType.String)] public string ProjectMemberName { get; set; } /// <summary> /// 性別 /// </summary> [Property(FieldName = "sex", ColumnName = "XMGL_220_COL_30", NodeType = NodeType.String)] public string Sex { get; set; }
/// <summary> /// 備注 /// </summary> [Property(FieldName = "notes", ColumnName = "XMGL_220_COL_110", NodeType = NodeType.String)] public string Notes { get; set; } /// <summary> /// 是否已刪除 /// </summary> [Property(FieldName = NodeOperation.Operation, ColumnName = "XMGL_220_COL_200", NodeType = NodeType.String, IsDeletedFlag = BooleanType.True)] public string IsDeleted { get; set; } /// <summary> /// 創建時間 /// </summary> [Property(FieldName = "createTime", ColumnName = "XMGL_220_COL_210", NodeType = NodeType.String)] public string CreateTime { get; set; } /// <summary> /// 創建人 /// </summary> [Property(FieldName = "creatorID", ColumnName = "XMGL_220_COL_220", NodeType = NodeType.Json, MainEntity = "User")] public string CreatorID { get; set; } /// <summary> /// 項目 /// </summary> [Property(FieldName = "planProject", ColumnName = "XMGL_20_COL_10", NodeType = NodeType.Json, MainEntity = "PlanProject")] public string PlanProject { get; set; } } }
三、反射機制,分析實體特性、屬性特性,將json對象數據保存到數據庫。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using Standard.Frame.BEntity; using Standard.Frame.Utility; using Standard.Frame.Config; using Standard.Frame.DBAccess; using System.Reflection; using Standard.Frame.Utility; namespace Standard.Frame.BLRuler { public class JsonToTable { public static void Save(object inputJsonObj) { try { SaveJsonObj(inputJsonObj, "PlanProject", null); } catch (Exception e) { LogUtility.Write(DateTime.Now.ToString() + " : " + e.Message); } } /// <summary> /// 保存輸入的json對象 /// </summary> /// <param name="inputJsonObj"></param> /// <param name="entityName"></param> private static void SaveJsonObj(object inputJsonObj, string entityName, Tuple<string, object> tupleFk) { Dictionary<string, object> dicReq = CommonFacade.ParseJsonObjToDic(inputJsonObj); Type type = CommonFacade.GetEntityType(entityName); string tableName = CommonFacade.GetTableNameByType(type); System.Reflection.PropertyInfo[] properties = type.GetProperties(); //先保存主表數據(string類型的節點) object recordID = SaveStringNode(dicReq, tableName, properties, tupleFk); //保存從表數據(array類型的節點) SaveArrayNode(dicReq, properties, entityName, recordID); } /// <summary> /// 保存json對象中的字符串節點 /// </summary> /// <param name="dicReq"></param> /// <param name="tableName"></param> /// <param name="properties"></param> /// <param name="tupleFk"></param> /// <returns></returns> private static object SaveStringNode(Dictionary<string, object> dicReq, string tableName, System.Reflection.PropertyInfo[] properties,Tuple<string, object> tupleFk) { PreSqlAccess sqlAccess = new PreSqlAccess(); Dictionary<string, object> dicTable = new Dictionary<string, object>(); Tuple<string, object> tuplePk = ParseStringNode(dicReq, properties, dicTable); if (tupleFk != null) { dicTable.Remove(tupleFk.Item1); dicTable.Add(tupleFk.Item1, tupleFk.Item2); } object recordID = sqlAccess.InsertOrUpdateDataSync(tableName, dicTable, tuplePk);//保存到數據庫 return recordID; } /// <summary> /// 保存從表數據(數據類型為array的節點) /// </summary> /// <param name="dicReq"></param> /// <param name="properties"></param> /// <param name="recordID"></param> private static void SaveArrayNode(Dictionary<string, object> dicReq, System.Reflection.PropertyInfo[] properties, string mainEntityName, object recordID) { System.Attribute[] attrs = null; foreach (System.Reflection.PropertyInfo p in properties) { attrs = System.Attribute.GetCustomAttributes(p); foreach (System.Attribute attr in attrs) { if (attr is PropertyAttribute) { PropertyAttribute propertyAttr = (PropertyAttribute)attr; if (dicReq.ContainsKey(propertyAttr.FieldName) && propertyAttr.NodeType == NodeType.Array) { Tuple<string, object> tupleFk = GetTupleFKID(propertyAttr.MainEntity, mainEntityName, recordID); List<object> listObj = CommonFacade.ParseJsonObjToList(dicReq[propertyAttr.FieldName]); listObj.ForEach((inputJsonObj) => { SaveJsonObj(inputJsonObj, propertyAttr.MainEntity, tupleFk); }); } } } } } /// <summary>/// 解析輸入json對象中的字符串節點 /// </summary> /// <param name="dicReq"></param> /// <param name="properties"></param> /// <returns></returns> private static Tuple<string, object> ParseStringNode(Dictionary<string, object> dicReq, System.Reflection.PropertyInfo[] properties, Dictionary<string, object> dicTable) { Tuple<string, object> tuplePk = null; System.Attribute[] attrs = null; foreach (System.Reflection.PropertyInfo p in properties) { attrs = System.Attribute.GetCustomAttributes(p); foreach (System.Attribute attr in attrs) { if (attr is PropertyAttribute) { PropertyAttribute propertyAttr = (PropertyAttribute)attr; if (dicReq.ContainsKey(propertyAttr.FieldName)) { if (propertyAttr.IsPK == BooleanType.True) {//記錄主鍵,備用 tuplePk = new Tuple<string, object>(propertyAttr.ColumnName, dicReq[propertyAttr.FieldName]); break; } if (propertyAttr.IsDeletedFlag == BooleanType.True) {//"是否刪除" 這個標示列需要特殊處理 if (dicReq.ContainsKey(NodeOperation.Operation) && Convert.ToString(dicReq[propertyAttr.FieldName]) == NodeOperationType.Deleted) dicTable.Add(propertyAttr.ColumnName, 1); break; } if (propertyAttr.NodeType == NodeType.String) dicTable.Add(propertyAttr.ColumnName, dicReq[propertyAttr.FieldName]); if (propertyAttr.NodeType == NodeType.Json) dicTable.Add(propertyAttr.ColumnName,CommonFacade.GetPKID(dicReq[propertyAttr.FieldName], propertyAttr.MainEntity)); } } } } return tuplePk; } /// <summary>/// 將主表ID匹配到從表對應的外鍵 /// </summary> /// <param name="inputJsonObj"></param> /// <param name="entityName"></param> /// <param name="mainEntityName"></param> /// <param name="recordID"></param> /// <returns></returns> private static Tuple<string, object> GetTupleFKID(string entityName, string mainEntityName, object recordID) { Tuple<string, object> tupleFK = null; Type type = CommonFacade.GetEntityType(entityName); System.Reflection.PropertyInfo[] properties = type.GetProperties(); System.Attribute[] attrs = null; foreach (System.Reflection.PropertyInfo p in properties) { attrs = System.Attribute.GetCustomAttributes(p); foreach (System.Attribute attr in attrs) { if (attr is PropertyAttribute) { PropertyAttribute propertyAttr = (PropertyAttribute)attr; if (propertyAttr.NodeType == NodeType.Json && propertyAttr.MainEntity == mainEntityName) { tupleFK = new Tuple<string, object>(propertyAttr.ColumnName,recordID); goto outer; } } } } outer: return tupleFK; } } }
四、操作數據庫
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Data; using System.Data.SqlClient; using Standard.Frame.Config; namespace Standard.Frame.DBAccess { public class PreSqlAccess { /// <summary>/// 同步插入記錄 /// </summary> /// <param name="tablename"></param> /// <param name="dicTable"></param> /// <returns></returns> private object InsertRecordSync(string tablename, Dictionary<string, object> dicTable) { StringBuilder columns = new StringBuilder(); StringBuilder datas = new StringBuilder(); List<IDataParameter> parameters = new List<IDataParameter>(); foreach (var item in dicTable) { columns.Append(item.Key).Append(","); datas.Append("@" + item.Key).Append(","); parameters.Add(new SqlParameter("@" + item.Key, item.Value)); } StringBuilder sqlCommand = new StringBuilder(); sqlCommand.Append(" insert into "); sqlCommand.Append(tablename).Append(" ( ").Append(columns.ToString().Trim(new char[] { ',' })).Append(" ) "); sqlCommand.Append(" values ( ").Append(datas.ToString().Trim(new char[] { ',' })).Append(" ) "); SqlAccess sqlAccess = new SqlAccess(); return sqlAccess.InsertRecordSync(tablename, sqlCommand.ToString(), CommandType.Text, parameters.ToArray(), null); } /// <summary>/// 同步更新記錄 /// </summary> /// <param name="tablename"></param> /// <param name="dicTable"></param> /// <param name="tuplePk"></param> /// <returns></returns> private object UpdateRecordSync(string tablename, Dictionary<string, object> dicTable, Tuple<string, object> tuplePk) { StringBuilder columnValues = new StringBuilder(); List<IDataParameter> parameters = new List<IDataParameter>(); foreach (var item in dicTable) { columnValues.Append(item.Key).Append("=").Append("@" + item.Key).Append(","); parameters.Add(new SqlParameter("@" + item.Key, item.Value)); } StringBuilder sqlCommand = new StringBuilder(); sqlCommand.Append(" update ").Append(tablename); sqlCommand.Append(" set "); sqlCommand.Append(columnValues.ToString().Trim(new char[] { ',' })); sqlCommand.Append(" where ").Append(tuplePk.Item1).Append("=").Append(tuplePk.Item2); SqlAccess sqlAccess = new SqlAccess(); return sqlAccess.ExecuteNonQuerySync(sqlCommand.ToString(), CommandType.Text, parameters.ToArray(), null); } /// <summary> /// 插入或者更新記錄 返回記錄ID /// </summary> /// <param name="tablename"></param> /// <param name="dicTable"></param> /// <param name="tuplePk"></param> /// <returns></returns> public object InsertOrUpdateDataSync(string tablename, Dictionary<string, object> dicTable, Tuple<string, object> tuplePk = null) { object recordID = default(object); if (dicTable.ContainsKey(NodeOperation.Operation)) { switch (Convert.ToString(dicTable[NodeOperation.Operation])) { case NodeOperationType.Added: dicTable.Remove(NodeOperation.Operation); recordID=InsertRecordSync(tablename, dicTable); break; case NodeOperationType.Changed: case NodeOperationType.Deleted: dicTable.Remove(NodeOperation.Operation); recordID = tuplePk.Item2; UpdateRecordSync(tablename, dicTable, tuplePk); break; default: ; break; } } return recordID; } } }