/// <summary> /// Saves all generic lists and dictionaries of the object and all inlined classes. The object already has to have an id! /// </summary> /// <param name="o">Object of which to save lists and dictionaries</param> /// <param name="parent">Parent (not inlined) Type</param> /// <param name="id">Id of the object</param> /// <param name="embeddedPrefix">Prefix used to create unique mn table names for all inlined lists and dictionaries</param> private static void SaveEnumerations(object o, Type parent, string id, string embeddedPrefix = "") { if (o == null) { return; } // Save all scalar and inline types first, so we have the new entry id for our mn-table later foreach (var property in PersistentProperty.GetPersistentProperties(o.GetType(), embeddedPrefix, parent)) { if (property.IsInlinedType) { SaveEnumerations(property.Property.GetValue(o, null), parent == null ? o.GetType() : parent, id, property.ColumnName + "_"); } if (property.IsGenericList) { IList list = (IList)property.Property.GetValue(o, null); foreach (var item in list) { string newId = Save(item, null, new Dictionary <string, string>(), ""); using (var cmd = new SQLiteCommand(String.Format( "INSERT INTO {4} ({0}Id, {1}Id) VALUES ({2}, {3})", parent == null ? o.GetType().Name : parent.Name, property.ListType.Name, id, newId, property.RelationTableName ), DBManager.MPQMirror)) { cmd.ExecuteNonQuery(); } } } if (property.IsGenericDictionary) { IDictionary dictionary = (IDictionary)property.Property.GetValue(o, null); foreach (var item in dictionary.Keys) { string newId = Save(dictionary[item], null, new Dictionary <string, string>(), ""); using (var cmd = new SQLiteCommand(String.Format( "INSERT INTO {4} ({0}Id, {1}Id, Key) VALUES ({2}, {3}, {5})", parent == null ? o.GetType().Name : parent.Name, property.ListType.Name, id, newId, property.RelationTableName, item ), DBManager.MPQMirror)) { cmd.ExecuteNonQuery(); } } } } }
private static void CreateTableForType(Type type, Type parent, Dictionary <string, Type> values, string embeddedPrefix = "") { // Save all scalar and inline types first, so we have the new entry id for our mn-table later foreach (var property in PersistentProperty.GetPersistentProperties(type, embeddedPrefix, parent)) { if (property.IsSimpleType) { // save array of basic types if (property.Type.IsArray && property.ArrayCount != -1) { for (int i = 0; i < property.ArrayCount; i++) { values.Add(property.ColumnName + "_" + i, property.Property.PropertyType); } } else { values.Add(property.ColumnName, property.Type); } continue; } if (property.IsGenericList || property.IsGenericDictionary) { string query = ""; if (property.IsGenericList) { query = "CREATE TABLE {0} ({1}Id NUMERIC, {2}Id NUMERIC)"; } if (property.IsGenericDictionary) { query = "CREATE TABLE {0} ({1}Id NUMERIC, {2}Id NUMERIC, Key TEXT)"; } query = String.Format(query, property.RelationTableName, parent == null ? type.Name : parent.Name, property.ListType.Name); using (var cmd = new SQLiteCommand(query, DBManager.MPQMirror)) { cmd.ExecuteNonQuery(); } CreateTableForType(property.ListType, null, new Dictionary <string, Type>()); continue; } values.Add(property.ColumnName + "_", typeof(string)); CreateTableForType(property.Type, parent == null ? type : parent, values, property.ColumnName + "_"); } // Only create tables for parent classes if (parent == null && !tableExists(type.Name)) { if (type.Namespace == "System") { values.Add("Value", typeof(string)); } var columnDefinitions = values.Select(v => v.Key + " " + ((v.Value == typeof(String) || v.Value == typeof(byte[])) ? "TEXT" : "NUMERIC")).ToList <string>(); columnDefinitions.Add("Id INTEGER PRIMARY KEY"); var tableDefinition = String.Join(",", columnDefinitions.ToArray <string>()); using (var cmd = new SQLiteCommand(String.Format("CREATE TABLE {0} ({1})", type.Name, tableDefinition), DBManager.MPQMirror)) { cmd.ExecuteNonQuery(); } } }
/// <summary> /// Save object with all inlined classes to table /// </summary> /// <param name="o">Object to save</param> /// <param name="parent">Parent type (Type of outmost, not inlined object)</param> /// <param name="id">Id to use to save or null to create a new</param> /// <param name="values">Dictionary in wich inlined classes write their property values</param> /// <param name="embeddedPrefix">Prefix to use in inlined classes so all inlined properties have unique column names</param> /// <returns>Id used to save the object</returns> private static string SaveBasic(object o, Type parent, string id, Dictionary <string, string> values, string embeddedPrefix = "") { if (o == null) { return(""); } // save scalar types if (o.GetType().Namespace != "System" && !o.GetType().IsEnum) { // Save all scalar and inline types first, so we have the new entry id for our mn-table later foreach (var property in PersistentProperty.GetPersistentProperties(o.GetType(), embeddedPrefix, parent)) { if (property.IsSimpleType) { if (property.Type.IsArray) { if (property.ArrayCount == -1) { values.Add(property.ColumnName, "'" + BitConverter.ToString((byte[])property.Property.GetValue(o, null)) + "'"); } else { for (int i = 0; i < property.ArrayCount; i++) { values.Add(property.ColumnName + "_" + i, "'" + (property.Property.GetValue(o, null) as Array).GetValue(i).ToString() + "'"); } } } else { values.Add(property.ColumnName, "'" + property.Property.GetValue(o, null).ToString() + "'"); } } // save complex object as inlined class if (property.IsInlinedType) { values.Add(property.ColumnName + "_", "'" + (property.Property.GetValue(o, null) != null).ToString() + "'"); SaveBasic(property.Property.GetValue(o, null), parent == null ? o.GetType() : parent, id, values, property.ColumnName + "_"); } } } else { values.Add("Value", o.ToString()); } // No parent means this class is not inlined. Add a new entry with all saved values in the class table if (parent == null) { if (id != null) { values.Add("Id", id); } string cnames = String.Join(",", (new List <string>(values.Keys).ToArray())); string cvalues = String.Join(",", (new List <string>(values.Values).ToArray())); using (var cmd = new SQLiteCommand(String.Format("INSERT INTO {0} ({1}) VALUES ({2})", o.GetType().Name, cnames, cvalues), DBManager.MPQMirror)) { cmd.ExecuteNonQuery(); using (var last = new SQLiteCommand("SELECT last_insert_rowid()", DBManager.MPQMirror)) { id = last.ExecuteScalar().ToString(); } } } return(id); }
/// <summary> /// Loads properties of an object from the passed reader. For inlined properties, the parent reader is passed /// </summary> /// <param name="o">Object to be filled with data</param> /// <param name="parent">Parent type that has the id for inlined classes</param> /// <param name="reader">Reader from which to take the data</param> /// <param name="embeddedPrefix">Prefix for 'inlined' properties (complex types)</param> private static void Load(object o, Type parent, SQLiteDataReader reader, string embeddedPrefix = "") { foreach (var property in PersistentProperty.GetPersistentProperties(o.GetType(), embeddedPrefix, parent)) { string entryId = reader["Id"].ToString(); // Load generic lists by finding the mn-mapping table and loading every entry recursivly if (property.IsGenericList) { using (var cmd = new SQLiteCommand(String.Format(genericListsql, property.RelationTableName, property.ListType.Name, parent == null ? o.GetType().Name : parent.Name, entryId), DBManager.MPQMirror)) { var itemReader = cmd.ExecuteReader(); var list = Activator.CreateInstance(property.Type); if (itemReader.HasRows) { while (itemReader.Read()) { var item = Activator.CreateInstance(property.ListType); Load(item, null, itemReader); (list as IList).Add(item); } } property.Property.SetValue(o, list, null); } continue; } // Load generic dictionaires by finding the mn-mapping table and loading every entry recursivly if (property.IsGenericDictionary) { using (var cmd = new SQLiteCommand(String.Format(genericListsql, property.RelationTableName, property.ListType.Name, parent == null ? o.GetType().Name : parent.Name, entryId), DBManager.MPQMirror)) { var itemReader = cmd.ExecuteReader(); var dictionary = Activator.CreateInstance(property.Type); if (itemReader.HasRows) { while (itemReader.Read()) { var item = Activator.CreateInstance(property.ListType); Load(item, null, itemReader); (dictionary as IDictionary).Add(Convert.ChangeType(itemReader["Key"], property.Type.GetGenericArguments()[0]), item); } } property.Property.SetValue(o, dictionary, null); } continue; } // load scalar types if (property.Type.Namespace == "System") { // load array of scalar types. The column name of the i-th array entry is "columnName_i" if (property.Type.IsArray) { if (property.ArrayCount == -1) { byte[] blob = StringToByteArray(reader[property.ColumnName].ToString().Replace("-", "")); property.Property.SetValue(o, blob, null); } else { Array vals = (Array)Activator.CreateInstance(property.Type, property.ArrayCount); for (int i = 0; i < vals.Length; i++) { vals.SetValue(Convert.ChangeType(reader[property.ColumnName + "_" + i.ToString()], property.Type.GetElementType()), i); } property.Property.SetValue(o, vals, null); } } else { property.Property.SetValue(o, Convert.ChangeType(reader[property.ColumnName], property.Type), null); } continue; } // load enums if (property.Type.IsEnum) { property.Property.SetValue(o, Enum.Parse(property.Type, reader[property.ColumnName].ToString(), true), null); continue; } // if its none of the earlier types, its a inlined class. class properties if (Convert.ToBoolean(reader[property.ColumnName + "_"])) { var embedded = Activator.CreateInstance(property.Type); Load(embedded, o.GetType(), reader, property.ColumnName + "_"); property.Property.SetValue(o, embedded, null); } } }