文章目錄
C# - Property 與 Value 的 Dictionary 轉為 Object
之前筆記 C# - 將 Object 的 Property 與 Value 轉換為 Dictionary 紀錄到將 C# object 的 property name 與 value 透過 dictionary 的資料型態存放,當時主要是為了配合 InfluxDB 的 insert 而做的筆記 ,想不到時隔兩個月,竟然想要透過 property - value 的 dictionary 來轉回 C# object,有趣的是來源不是 InfluxDB 而是 Redis
基本環境說明
- macOS Catalina 10.15.1
- .NET Core SDK 3.1.100
測試用程式碼
model
public class User { public int UserId { get; set; } public string Name { get; set; } public string Email { get; set; } }
假資料
User[] _user = new User[] { new User { UserId = 1, Name = "yowko", Email = "[email protected]" }, new User { UserId = 3, Name = "test", Email = "[email protected]" }, new User { UserId = 5, Name = "poc", Email = "[email protected]" }, };
將 object 轉為 dictionary
之前筆記 C# - 將 Object 的 Property 與 Value 轉換為 Dictionary 中的程式只能轉換 reference type,這邊小改一下,讓 value type 也可以順利轉換 (但有 boxing 的效能損耗)
public static class PocoToDictionary { private static readonly MethodInfo AddToDictionaryMethod = typeof(IDictionary<string, object>).GetMethod("Add"); private static readonly ConcurrentDictionary<Type, Func<object, IDictionary<string, object>>> Converters = new ConcurrentDictionary<Type, Func<object, IDictionary<string, object>>>(); private static readonly ConstructorInfo DictionaryConstructor = typeof(Dictionary<string, object>) .GetConstructors().FirstOrDefault(c => c.IsPublic && !c.GetParameters().Any()); public static IDictionary<string, object> ExpressionToDictionary(this object obj) => obj == null ? null : Converters.GetOrAdd(obj.GetType(), o => { var outputType = typeof(IDictionary<string, object>); var inputType = obj.GetType(); var inputExpression = Expression.Parameter(typeof(object), "input"); var typedInputExpression = Expression.Convert(inputExpression, inputType); var outputVariable = Expression.Variable(outputType, "output"); var returnTarget = Expression.Label(outputType); var body = new List<Expression> { Expression.Assign(outputVariable, Expression.New(DictionaryConstructor)) }; body.AddRange( from prop in inputType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy) where prop.CanRead && (prop.PropertyType.IsPrimitive || prop.PropertyType == typeof(string)) let getExpression = Expression.Property(typedInputExpression, prop.GetMethod) select Expression.Call(outputVariable, AddToDictionaryMethod, Expression.Constant(prop.Name), Expression.Convert(getExpression, typeof(object)))); body.Add(Expression.Return(returnTarget, outputVariable)); body.Add(Expression.Label(returnTarget, Expression.Constant(null, outputType))); var lambdaExpression = Expression.Lambda<Func<object, IDictionary<string, object>>>( Expression.Block(new[] {outputVariable}, body), inputExpression); return lambdaExpression.Compile(); })(obj); }
將各個 object 轉為 Dictionary list
public static List<IDictionary<string, object>> ByPocoToDictionary() { var result = new List<IDictionary<string, object>>(); var h = 0; foreach (var item in _user) { var dict = item.ExpressionToDictionary(); h = $"{h}{dict.GetHashCode()}".GetHashCode(); result.Add(dict); } return result; }
將 Dictionary 轉為 object
轉換程式
private static T DictionaryToObject<T>(IDictionary<String, Object> dictionary) where T : class, new() { // 取得 T 所有 property var myPropertyInfo = typeof(T).GetProperties(); // 將 property 的 name 轉為小寫當 key,value 為原始大小寫,讓傳入的資料無論大小寫皆可轉換 var properties = myPropertyInfo .Select(a => new KeyValuePair<string, string>(a.Name.ToLowerInvariant(), a.Name)) .ToDictionary(a => a.Key, a => a.Value); // 建立 T 實體 var instance = Activator.CreateInstance<T>(); //處理所有欄位 foreach (var (key, value) in dictionary) { var name = key.ToLowerInvariant(); //欄位名稱不存在就換下一個 if (!properties.TryGetValue(name, out var property)) continue; var prop = typeof(T).GetProperty(property); //依據不同型別來做轉換,只意思寫 int 與 string,請自行擴充 switch (prop.PropertyType) { case { } intType when intType == typeof(int): prop.SetValue(instance, Convert.ToInt32(value), null); break; case { } stringType when stringType == typeof(string): prop.SetValue(instance, Convert.ToString(value), null); break; } } return instance; }
實際使用
// 取得 users 的 dictionary list var result = ByPocoToDictionary(); // 將 dictionary 再轉回 user var outputResult = result.Select(DictionaryToObject<User>);
心得
其實上次紀錄 C# - 將 Object 的 Property 與 Value 轉換為 Dictionary 時,我就覺得有些荒謬了,覺得這樣的需求沒什麼道理,使用情境很是侷限,想不到這次竟然還需要來個逆向操作,不過所幸有上次的經驗,讓這次的問題可以快速被解決,雖然可能會因為上次的既定印象反而限制了思考的方向而造成用了效能不佳的方法,但終究是多了解了一個可以解決問題的方法,不論好壞還是學到了
參考資訊
文章作者 Yowko Tsai
上次更新 2020-12-11
授權合約
本部落格 (Yowko's Notes) 所有的文章內容(包含圖片),任何轉載行為,必須通知並獲本部落格作者 (Yowko Tsai) 的同意始得轉載,且轉載皆須註明出處與作者。
Yowko's Notes 由 Yowko Tsai 製作,以創用CC 姓名標示-非商業性-相同方式分享 3.0 台灣 授權條款 釋出。