之前用的 sqlite3 作為本地資料庫, 可是它不能作為記憶體資料庫, 是基于檔案的, 在某些情況下沒有讀寫權限就直接掛壁了, 比如 WebGL 中會報錯 dlopen(), 然后給了一個鏈接, 看過去太復雜了沒有懂, 或者安卓里面 StreamingAssets 是壓縮包檔案, 也是沒法直接使用的......
而且 sqlite3 用起來很麻煩, dll 需要同時參考 Mono.Data 和 System.Data, 在Unity2017中需要手動扔一個 System.Data 進去, 要不然缺失參考, 而在 Unity2019中又不能扔進去, 會編譯沖突......
然后找到這個, 很簡單一個dll完事 :
它的讀取可以通過 path, byte[], Stream 等來實作, 能夠實作很多種需求了.
不過有點奇葩的是它的檔案命名方式, 比如我想要創建一個 abc.db 檔案, 這是不行的, 只能傳給它數字, 然后它自己生成 db{N}.box 這樣的 db 檔案, 或者傳給它一個檔案夾路徑, 它會自動生成檔案夾下 db1.box 檔案, 實在夠奇怪的, 不過生成出來的檔案, 可以通過改名, 然后讀取 bytes 的方式讀取......
反正是很神奇的腦回路, 我搞了半天才明白什么回事, 它也沒有檔案, 導致后面出現了一系列事故.
先來說說怎樣生成資料庫, 比如從 Excel 或是啥來源的資料, 要把它生成資料庫的流程很簡單, 就是先獲取 Table 的 Key, 然后每行作為對應的資料錄入資料庫就行了, 可是插入資料在 iboxDB 里面是個很奇葩的操作 :

AutoBox 是資料操作的入口, 它的插入只有泛型的 Insert<V> 來實作, 它的 API 設計是基于已存在的型別的, 比如一個資料庫你要保存一個類 :
public class Record { public string Id; public string Name; public string age; }
對于已經存在的型別, 它就很簡單 :
AutoBox autoBox = ...... var rec = new Record { Id = "aa", Name = "Andy" }; autoBox.Insert<Record>("hahaha", rec);
可是對于一個剛從 Excel 來的資料, 我們是沒有型別的, 那么怎樣才能創建一個型別給它?
這時候只能使用 Emit 了, 沒有型別就創建型別, 然后它沒有非泛型方法, 創建型別之后還需要從 Type 獲取泛型 Insert<V> 方法, 非常麻煩 :
/// <summary> /// Generate IL code for no exsists type /// </summary> /// <param name="typeName"></param> /// <param name="vars"></param> /// <returns></returns> public static System.Type DataBaseRawTypeILGenerator(string typeName, params string[] vars) { // 構建程式集 var asmName = new AssemblyName("DataBaseRawType"); var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave); // 構建模塊 ModuleBuilder mdlBldr = asmBuilder.DefineDynamicModule(asmName.Name, asmName.Name + ".dll"); // 構建類 var typeBldr = mdlBldr.DefineType(typeName, TypeAttributes.Public); // 創建field if(vars != null && vars.Length > 0) { foreach(var variance in vars) { FieldBuilder fbNumber = typeBldr.DefineField(variance, typeof(string), FieldAttributes.Public); } } var t = typeBldr.CreateType(); return t; }
通過創建型別, 傳入 { "Id", "Name", "age" }可以創建出一個跟 Record 一樣的擁有這些變數的型別, 然后需要根據它獲取 AutoBox 實體的 Insert<V> 泛型方法 :
public static MethodInfo GetGenericFunction(System.Type type, string genericFuncName, Type[] genericTypes, object[] paramaters, bool isStatic) { var flags = BindingFlags.Public | BindingFlags.NonPublic | (isStatic ? BindingFlags.Static : BindingFlags.Instance) | BindingFlags.InvokeMethod; var methods = type.GetMethods(flags); foreach(var method in methods) { if(method.IsGenericMethod && string.Equals(method.Name, genericFuncName, StringComparison.Ordinal)) { var arguments = method.GetGenericArguments(); // 檢查泛型類的數量是否對的上 if(arguments != null && arguments.Length == genericTypes.Length) { // 檢查傳入引數型別是否對的上, 如果考慮到可變引數, default value引數, 可空結構體引數等, 會很復雜 if(MethodParametersTypeEquals(method, paramaters)) { var genericMethod = method.MakeGenericMethod(genericTypes); if(genericMethod != null) { return genericMethod; } } } } } return null; } // 簡單的對比一下, 實際使用要考慮到可變引數( params object[] ), default value引數( bool isStatic = false ), 可空結構體引數( int? a = null )等 public static bool MethodParametersTypeEquals(MethodInfo method, object[] parameters) { var mehotdParamters = method.GetParameters(); int len_l = mehotdParamters != null ? mehotdParamters.Length : 0; int len_r = parameters != null ? parameters.Length : 0; return len_l == len_r; }
這兩個大招還是之前測驗 Lua 泛型的時候搞的, 沒想到會用到這里來, 然后就是依靠
System.Activator.CreateInstance(type);
來創建實體保存資料了, 它的設計基于簡單易用, 可是在這里就變得很復雜, 好在有 Emit 大法......
然后就能走通流程了, 讀取資料, 轉換資料, 保存資料到資料庫 :
private static void FillDataBase_iboxDB(string tableName, string[] variables, List<Dictionary<string, string>> valueRows, string key) { var type = DataBaseRawTypeILGenerator(tableName, variables); // 根據變數創建型別 var insertCall = GetGenericFunction(typeof(iBoxDB.LocalServer.AutoBox), "Insert", new System.Type[] { type }, new object[] { tableName, System.Activator.CreateInstance(type) }, false); // Insert<V> 方法 if(insertCall != null) { var db = new iBoxDB.LocalServer.DB(); var databaseAccess = db.Open(); foreach(var values in valueRows) { var data = https://www.cnblogs.com/tiancaiwrk/archive/2020/11/17/System.Activator.CreateInstance(type); // 創建實體 foreach(var valueKV in values) { SetField(data, valueKV.Key, valueKV.Value); // 反射修改變數 } insertCall.Invoke(databaseAccess, new object[] { tableName, data }); // 寫入資料庫 } db.Dispose(); } }
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/222252.html
標籤:其他
