/// <summary> /// Obtains the object from the database and fetches all the properties annotated with /// any subclass of <c>RelationshipAttribute</c>. If the object with the specified primary key doesn't /// exist in the database, an exception will be raised. /// </summary> /// <returns>The object with all the children loaded</returns> /// <param name="conn">SQLite Net connection object</param> /// <param name="pk">Primary key for the object to search in the database</param> /// <param name="recursive">If set to <c>true</c> all the relationships with /// <c>CascadeOperation.CascadeRead</c> will be loaded recusively.</param> /// <typeparam name="T">Entity type where the object should be fetched from</typeparam> public static T GetWithChildren <T>(this SQLiteConnection conn, object pk, bool recursive = false) where T : new() { var element = conn.Get <T>(pk); conn.GetChildren(element, recursive); return(element); }
private static void GetChildRecursive(this SQLiteConnection conn, object element, PropertyInfo relationshipProperty, bool recursive, ObjectCache objectCache) { var relationshipAttribute = relationshipProperty.GetAttribute <RelationshipAttribute>(); if (relationshipAttribute is OneToOneAttribute) { conn.GetOneToOneChild(element, relationshipProperty, recursive, objectCache); } else if (relationshipAttribute is OneToManyAttribute) { conn.GetOneToManyChildren(element, relationshipProperty, recursive, objectCache); } else if (relationshipAttribute is ManyToOneAttribute) { conn.GetManyToOneChild(element, relationshipProperty, recursive, objectCache); } else if (relationshipAttribute is ManyToManyAttribute) { conn.GetManyToManyChildren(element, relationshipProperty, recursive, objectCache); } else if (relationshipAttribute is TextBlobAttribute) { TextBlobOperations.GetTextBlobChild(element, relationshipProperty); } }
public static void InsertWithChildren <T>(this SQLiteConnection conn, T element) { RefreshForeignKeys(ref element); conn.Insert(element); conn.UpdateInverseForeignKeys(element); }
public static void UpdateWithChildren <T>(this SQLiteConnection conn, T element) { // Update the current element RefreshForeignKeys(ref element); conn.Update(element); // Update inverse foreign keys conn.UpdateInverseForeignKeys(element); }
/// <summary> /// The behavior is the same that <c>GetWithChildren</c> but it returns null if the object doesn't /// exist in the database instead of throwing an exception /// Obtains the object from the database and fetch all the properties annotated with /// any subclass of <c>RelationshipAttribute</c>. If the object with the specified primary key doesn't /// exist in the database, it will return null /// </summary> /// <returns>The object with all the children loaded or null if it doesn't exist</returns> /// <param name="conn">SQLite Net connection object</param> /// <param name="pk">Primary key for the object to search in the database</param> /// <param name="recursive">If set to <c>true</c> all the relationships with /// <c>CascadeOperation.CascadeRead</c> will be loaded recusively.</param> /// <typeparam name="T">Entity type where the object should be fetched from</typeparam> public static T FindWithChildren <T>(this SQLiteConnection conn, object pk, bool recursive = false) where T : new() { var element = conn.Find <T>(pk); if (!EqualityComparer <T> .Default.Equals(element, default(T))) { conn.GetChildren(element, recursive); } return(element); }
private static object GetManyToOneChild <T>(this SQLiteConnection conn, T element, PropertyInfo relationshipProperty, bool recursive, ObjectCache objectCache) { var type = element.GetType(); EnclosedType enclosedType; var entityType = relationshipProperty.GetEntityType(out enclosedType); Assert(enclosedType == EnclosedType.None, type, relationshipProperty, "ManyToOne relationship cannot be of type List or Array"); var otherEntityPrimaryKeyProperty = entityType.GetPrimaryKey(); Assert(otherEntityPrimaryKeyProperty != null, type, relationshipProperty, "ManyToOne relationship destination must have Primary Key"); var currentEntityForeignKeyProperty = type.GetForeignKeyProperty(relationshipProperty); Assert(currentEntityForeignKeyProperty != null, type, relationshipProperty, "ManyToOne relationship origin must have Foreign Key"); var tableMapping = conn.GetMapping(entityType); Assert(tableMapping != null, type, relationshipProperty, "There's no mapping table for OneToMany relationship destination"); object value = null; var isLoadedFromCache = false; var foreignKeyValue = currentEntityForeignKeyProperty.GetValue(element, null); if (foreignKeyValue != null) { // Try to load from cache when possible if (recursive) { value = GetObjectFromCache(entityType, foreignKeyValue, objectCache); } if (value == null) { value = conn.Find(foreignKeyValue, tableMapping); } else { isLoadedFromCache = true; } } relationshipProperty.SetValue(element, value, null); if (value != null && !isLoadedFromCache && recursive) { SaveObjectToCache(value, otherEntityPrimaryKeyProperty.GetValue(value, null), objectCache); conn.GetChildrenRecursive(value, true, recursive, objectCache); } return(value); }
private static void GetChildrenRecursive(this SQLiteConnection conn, object element, bool onlyCascadeChildren, bool recursive, ObjectCache objectCache = null) { objectCache = objectCache ?? new ObjectCache(); foreach (var relationshipProperty in element.GetType().GetRelationshipProperties()) { var relationshipAttribute = relationshipProperty.GetAttribute <RelationshipAttribute>(); if (!onlyCascadeChildren || relationshipAttribute.IsCascadeRead) { conn.GetChildRecursive(element, relationshipProperty, recursive, objectCache); } } }
/// <summary> /// Fetches all the entities of the specified type with the filter and fetches all the relationship /// properties of all the returned elements. /// </summary> /// <returns>List of all the elements of the type T that matches the filter with the children already loaded</returns> /// <param name="conn">SQLite Net connection object</param> /// <param name="filter">Filter that will be passed to the <c>Where</c> clause when fetching /// objects from the database. No relationship properties are allowed in this filter as they /// are loaded afterwards</param> /// <param name="recursive">If set to <c>true</c> all the relationships with /// <c>CascadeOperation.CascadeRead</c> will be loaded recusively.</param> /// <typeparam name="T">Entity type where the object should be fetched from</typeparam> public static List <T> GetAllWithChildren <T>(this SQLiteConnection conn, Expression <Func <T, bool> > filter = null, bool recursive = false) where T : new() { var elements = conn.Table <T>(); if (filter != null) { elements = elements.Where(filter); } var list = elements.ToList(); foreach (T element in list) { conn.GetChildren(element, recursive); } return(list); }
private static void UpdateInverseForeignKeys <T>(this SQLiteConnection conn, T element) { foreach (var relationshipProperty in typeof(T).GetRelationshipProperties()) { var relationshipAttribute = relationshipProperty.GetAttribute <RelationshipAttribute>(); if (relationshipAttribute is OneToManyAttribute) { conn.UpdateOneToManyInverseForeignKey(element, relationshipProperty); } else if (relationshipAttribute is OneToOneAttribute) { conn.UpdateOneToOneInverseForeignKey(element, relationshipProperty); } else if (relationshipAttribute is ManyToManyAttribute) { conn.UpdateManyToManyForeignKeys(element, relationshipProperty); } } }
private static void UpdateOneToOneInverseForeignKey <T>(this SQLiteConnection conn, T element, PropertyInfo relationshipProperty) { var type = typeof(T); EnclosedType enclosedType; var entityType = relationshipProperty.GetEntityType(out enclosedType); var originPrimaryKeyProperty = type.GetPrimaryKey(); var inversePrimaryKeyProperty = entityType.GetPrimaryKey(); var inverseForeignKeyProperty = type.GetForeignKeyProperty(relationshipProperty, inverse: true); Debug.Assert(enclosedType == EnclosedType.None, "OneToOne relationships cannot be List or Array of entities"); var inverseProperty = type.GetInverseProperty(relationshipProperty); if (inverseProperty != null) { EnclosedType inverseEnclosedType; var inverseEntityType = inverseProperty.GetEntityType(out inverseEnclosedType); Debug.Assert(inverseEnclosedType == EnclosedType.None, "OneToOne inverse relationship shouldn't be List or Array"); Debug.Assert(inverseEntityType == type, "OneToOne inverse relationship is not the expected type"); } object keyValue = null; if (originPrimaryKeyProperty != null && inverseForeignKeyProperty != null) { keyValue = originPrimaryKeyProperty.GetValue(element, null); } object childKey = null; var child = relationshipProperty.GetValue(element, null); if (child != null) { if (inverseForeignKeyProperty != null && keyValue != null) { inverseForeignKeyProperty.SetValue(child, keyValue, null); } if (inverseProperty != null) { inverseProperty.SetValue(child, element, null); } if (inversePrimaryKeyProperty != null) { childKey = inversePrimaryKeyProperty.GetValue(child, null); } } // Objects already updated, now change the database if (inverseForeignKeyProperty != null && inversePrimaryKeyProperty != null) { string tableName = entityType.GetTableName(); string inverseForeignKeyName = inverseForeignKeyProperty.GetColumnName(); string inversePrimaryKeyName = inversePrimaryKeyProperty.GetColumnName(); var query = String.Format("update {0} set {1} = ? where {2} == ?", tableName, inverseForeignKeyName, inversePrimaryKeyName); conn.Execute(query, keyValue, childKey); // Delete previous relationships var deleteQuery = String.Format("update {0} set {1} = NULL where {1} == ? and {2} not in (?)", tableName, inverseForeignKeyName, inversePrimaryKeyName); conn.Execute(deleteQuery, keyValue, childKey); } }
/// <summary> /// Fetches a specific property of the current object and keeps fetching recursively if the /// <c>recursive</c> flag has been set. /// </summary> /// <param name="conn">SQLite Net connection object</param> /// <param name="element">Element used to load all the relationship properties</param> /// <param name="relationshipProperty">Name of the property to fetch from the database</param> /// <param name="recursive">If set to <c>true</c> all the relationships with /// <c>CascadeOperation.CascadeRead</c> will be loaded recusively.</param> /// <typeparam name="T">Entity type where the object should be fetched from</typeparam> public static void GetChild <T>(this SQLiteConnection conn, T element, string relationshipProperty, bool recursive = false) { conn.GetChild(element, element.GetType().GetRuntimeProperty(relationshipProperty), recursive); }
/// <summary> /// Fetches a specific property of the current object and keeps fetching recursively if the /// <c>recursive</c> flag has been set. /// </summary> /// <param name="conn">SQLite Net connection object</param> /// <param name="element">Element used to load all the relationship properties</param> /// <param name="propertyExpression">Expression that returns the property to be loaded from the database. /// This variant is useful to avoid spelling mistakes and make the code refactor-safe.</param> /// <param name="recursive">If set to <c>true</c> all the relationships with /// <c>CascadeOperation.CascadeRead</c> will be loaded recusively.</param> /// <typeparam name="T">Entity type where the object should be fetched from</typeparam> public static void GetChild <T>(this SQLiteConnection conn, T element, Expression <Func <T, object> > propertyExpression, bool recursive = false) { conn.GetChild(element, ReflectionExtensions.GetProperty(propertyExpression), recursive); }
/// <summary> /// Fetches a specific property of the current object and keeps fetching recursively if the /// <c>recursive</c> flag has been set. /// </summary> /// <param name="conn">SQLite Net connection object</param> /// <param name="element">Element used to load all the relationship properties</param> /// <param name="relationshipProperty">Property to load from the database</param> /// <param name="recursive">If set to <c>true</c> all the relationships with /// <c>CascadeOperation.CascadeRead</c> will be loaded recusively.</param> /// <typeparam name="T">Entity type where the object should be fetched from</typeparam> public static void GetChild <T>(this SQLiteConnection conn, T element, PropertyInfo relationshipProperty, bool recursive = false) { conn.GetChildRecursive(element, relationshipProperty, recursive, new ObjectCache()); }
private static IEnumerable GetManyToManyChildren <T>(this SQLiteConnection conn, T element, PropertyInfo relationshipProperty, bool recursive, ObjectCache objectCache) { var type = element.GetType(); EnclosedType enclosedType; var entityType = relationshipProperty.GetEntityType(out enclosedType); var currentEntityPrimaryKeyProperty = type.GetPrimaryKey(); var otherEntityPrimaryKeyProperty = entityType.GetPrimaryKey(); var manyToManyMetaInfo = type.GetManyToManyMetaInfo(relationshipProperty); var currentEntityForeignKeyProperty = manyToManyMetaInfo.OriginProperty; var otherEntityForeignKeyProperty = manyToManyMetaInfo.DestinationProperty; var intermediateType = manyToManyMetaInfo.IntermediateType; var tableMapping = conn.GetMapping(entityType); Assert(enclosedType != EnclosedType.None, type, relationshipProperty, "ManyToMany relationship must be a List or Array"); Assert(currentEntityPrimaryKeyProperty != null, type, relationshipProperty, "ManyToMany relationship origin must have Primary Key"); Assert(otherEntityPrimaryKeyProperty != null, type, relationshipProperty, "ManyToMany relationship destination must have Primary Key"); Assert(intermediateType != null, type, relationshipProperty, "ManyToMany relationship intermediate type cannot be null"); Assert(currentEntityForeignKeyProperty != null, type, relationshipProperty, "ManyToMany relationship origin must have a foreign key defined in the intermediate type"); Assert(otherEntityForeignKeyProperty != null, type, relationshipProperty, "ManyToMany relationship destination must have a foreign key defined in the intermediate type"); Assert(tableMapping != null, type, relationshipProperty, "There's no mapping table defined for ManyToMany relationship origin"); IList cascadeElements = new List <object>(); IList values = null; var primaryKeyValue = currentEntityPrimaryKeyProperty.GetValue(element, null); if (primaryKeyValue != null) { // Obtain the relationship keys var keysQuery = string.Format("select {0} from {1} where {2} = ?", otherEntityForeignKeyProperty.GetColumnName(), intermediateType.GetTableName(), currentEntityForeignKeyProperty.GetColumnName()); var query = string.Format("select * from {0} where {1} in ({2})", entityType.GetTableName(), otherEntityPrimaryKeyProperty.GetColumnName(), keysQuery); var queryResults = conn.Query(tableMapping, query, primaryKeyValue); Array array = null; // Create a generic list of the expected type if (enclosedType == EnclosedType.List) { values = (IList)Activator.CreateInstance(typeof(List <>).MakeGenericType(entityType)); } else { values = array = Array.CreateInstance(entityType, queryResults.Count); } int i = 0; foreach (var result in queryResults) { // Replace obtained value with a cached one whenever possible bool loadedFromCache = false; var value = recursive ? ReplaceWithCacheObjectIfPossible(result, otherEntityPrimaryKeyProperty, objectCache, out loadedFromCache) : result; if (array != null) { array.SetValue(value, i); } else { values.Add(value); } if (!loadedFromCache) { cascadeElements.Add(result); } i++; } } relationshipProperty.SetValue(element, values, null); if (recursive) { foreach (var child in cascadeElements) { conn.GetChildrenRecursive(child, true, recursive, objectCache); } } return(values); }
/// <summary> /// Fetches all the properties annotated with any subclass of <c>RelationshipAttribute</c> of the current /// object and keeps fetching recursively if the <c>recursive</c> flag has been set. /// </summary> /// <param name="conn">SQLite Net connection object</param> /// <param name="element">Element used to load all the relationship properties</param> /// <param name="recursive">If set to <c>true</c> all the relationships with /// <c>CascadeOperation.CascadeRead</c> will be loaded recusively.</param> /// <typeparam name="T">Entity type where the object should be fetched from</typeparam> public static void GetChildren <T>(this SQLiteConnection conn, T element, bool recursive = false) { GetChildrenRecursive(conn, element, false, recursive); }
private static void UpdateManyToManyForeignKeys <T>(this SQLiteConnection conn, T element, PropertyInfo relationshipProperty) { var type = typeof(T); EnclosedType enclosedType; var entityType = relationshipProperty.GetEntityType(out enclosedType); var currentEntityPrimaryKeyProperty = type.GetPrimaryKey(); var otherEntityPrimaryKeyProperty = entityType.GetPrimaryKey(); var manyToManyMetaInfo = type.GetManyToManyMetaInfo(relationshipProperty); var currentEntityForeignKeyProperty = manyToManyMetaInfo.OriginProperty; var otherEntityForeignKeyProperty = manyToManyMetaInfo.DestinationProperty; var intermediateType = manyToManyMetaInfo.IntermediateType; Debug.Assert(enclosedType != EnclosedType.None, "ManyToMany relationship must be a List or Array"); Debug.Assert(currentEntityPrimaryKeyProperty != null, "ManyToMany relationship origin must have Primary Key"); Debug.Assert(otherEntityPrimaryKeyProperty != null, "ManyToMany relationship destination must have Primary Key"); Debug.Assert(intermediateType != null, "ManyToMany relationship intermediate type cannot be null"); Debug.Assert(currentEntityForeignKeyProperty != null, "ManyToMany relationship origin must have a foreign key defined in the intermediate type"); Debug.Assert(otherEntityForeignKeyProperty != null, "ManyToMany relationship destination must have a foreign key defined in the intermediate type"); var primaryKey = currentEntityPrimaryKeyProperty.GetValue(element, null); // Obtain the list of children keys var childList = (IEnumerable)relationshipProperty.GetValue(element, null); var childKeyList = (from object child in childList ?? new List <object>() select otherEntityPrimaryKeyProperty.GetValue(child, null)).ToList(); var childrenKeysParams = String.Join(",", childKeyList.Select(x => "?")); // Check for already existing relationships var currentChildrenQuery = string.Format("select {0} from {1} where {2} == ? and {0} in ({3})", otherEntityForeignKeyProperty.Name, intermediateType.Name, currentEntityForeignKeyProperty.Name, childrenKeysParams); var sqlParams = new List <object> { primaryKey }; sqlParams.AddRange(childKeyList); var currentChildKeyList = from object child in conn.Query(conn.GetMapping(intermediateType), currentChildrenQuery, sqlParams.ToArray()) select otherEntityForeignKeyProperty.GetValue(child, null); // Insert missing relationships in the intermediate table var missingChildKeyList = childKeyList.Where(o => !currentChildKeyList.Contains(o)).ToList(); var missingIntermediateObjects = new List <object>(missingChildKeyList.Count); foreach (var missingChildKey in missingChildKeyList) { var intermediateObject = Activator.CreateInstance(intermediateType); currentEntityForeignKeyProperty.SetValue(intermediateObject, primaryKey, null); otherEntityForeignKeyProperty.SetValue(intermediateObject, missingChildKey, null); missingIntermediateObjects.Add(intermediateObject); } conn.InsertAll(missingIntermediateObjects); // Delete any other pending relationship var deleteQuery = string.Format("delete from {0} where {1} == ? and {2} not in ({3})", intermediateType.Name, currentEntityForeignKeyProperty.Name, otherEntityForeignKeyProperty.Name, childrenKeysParams); conn.Execute(deleteQuery, sqlParams.ToArray()); }
private static object GetOneToOneChild <T>(this SQLiteConnection conn, T element, PropertyInfo relationshipProperty, bool recursive, ObjectCache objectCache) { var type = element.GetType(); EnclosedType enclosedType; var entityType = relationshipProperty.GetEntityType(out enclosedType); Assert(enclosedType == EnclosedType.None, type, relationshipProperty, "OneToOne relationship cannot be of type List or Array"); var currentEntityPrimaryKeyProperty = type.GetPrimaryKey(); var otherEntityPrimaryKeyProperty = entityType.GetPrimaryKey(); Assert(currentEntityPrimaryKeyProperty != null || otherEntityPrimaryKeyProperty != null, type, relationshipProperty, "At least one entity in a OneToOne relationship must have Primary Key"); var currentEntityForeignKeyProperty = type.GetForeignKeyProperty(relationshipProperty); var otherEntityForeignKeyProperty = type.GetForeignKeyProperty(relationshipProperty, inverse: true); Assert(currentEntityForeignKeyProperty != null || otherEntityForeignKeyProperty != null, type, relationshipProperty, "At least one entity in a OneToOne relationship must have Foreign Key"); var hasForeignKey = otherEntityPrimaryKeyProperty != null && currentEntityForeignKeyProperty != null; var hasInverseForeignKey = currentEntityPrimaryKeyProperty != null && otherEntityForeignKeyProperty != null; Assert(hasForeignKey || hasInverseForeignKey, type, relationshipProperty, "Missing either ForeignKey or PrimaryKey for a complete OneToOne relationship"); var tableMapping = conn.GetMapping(entityType); Assert(tableMapping != null, type, relationshipProperty, "There's no mapping table for OneToOne relationship"); var inverseProperty = type.GetInverseProperty(relationshipProperty); object value = null; var isLoadedFromCache = false; if (hasForeignKey) { var foreignKeyValue = currentEntityForeignKeyProperty.GetValue(element, null); if (foreignKeyValue != null) { // Try to load from cache when possible if (recursive) { value = GetObjectFromCache(entityType, foreignKeyValue, objectCache); } if (value == null) { value = conn.Find(foreignKeyValue, tableMapping); } else { isLoadedFromCache = true; } } } else { var primaryKeyValue = currentEntityPrimaryKeyProperty.GetValue(element, null); if (primaryKeyValue != null) { var query = string.Format("select * from {0} where {1} = ? limit 1", entityType.GetTableName(), otherEntityForeignKeyProperty.GetColumnName()); value = conn.Query(tableMapping, query, primaryKeyValue).FirstOrDefault(); // Its a OneToOne, take only the first } // Try to replace the loaded entity with the same object from the cache whenever possible value = recursive ? ReplaceWithCacheObjectIfPossible(value, otherEntityPrimaryKeyProperty, objectCache, out isLoadedFromCache) : value; } relationshipProperty.SetValue(element, value, null); if (value != null && inverseProperty != null) { inverseProperty.SetValue(value, element, null); } if (value != null && !isLoadedFromCache && recursive) { SaveObjectToCache(value, otherEntityPrimaryKeyProperty.GetValue(value, null), objectCache); conn.GetChildrenRecursive(value, true, recursive, objectCache); } return(value); }
public static void DeleteWithChildren <T>(this SQLiteConnection conn, T element) { }
private static IEnumerable GetOneToManyChildren <T>(this SQLiteConnection conn, T element, PropertyInfo relationshipProperty, bool recursive, ObjectCache objectCache) { var type = element.GetType(); EnclosedType enclosedType; var entityType = relationshipProperty.GetEntityType(out enclosedType); Assert(enclosedType != EnclosedType.None, type, relationshipProperty, "OneToMany relationship must be a List or Array"); var currentEntityPrimaryKeyProperty = type.GetPrimaryKey(); Assert(currentEntityPrimaryKeyProperty != null, type, relationshipProperty, "OneToMany relationship origin must have Primary Key"); var otherEntityForeignKeyProperty = type.GetForeignKeyProperty(relationshipProperty, inverse: true); Assert(otherEntityForeignKeyProperty != null, type, relationshipProperty, "OneToMany relationship destination must have Foreign Key to the origin class"); var otherEntityPrimaryKeyProperty = entityType.GetPrimaryKey(); var tableMapping = conn.GetMapping(entityType); Assert(tableMapping != null, type, relationshipProperty, "There's no mapping table for OneToMany relationship destination"); var inverseProperty = type.GetInverseProperty(relationshipProperty); IList cascadeElements = new List <object>(); IList values = null; var primaryKeyValue = currentEntityPrimaryKeyProperty.GetValue(element, null); if (primaryKeyValue != null) { var query = string.Format("select * from {0} where {1} = ?", entityType.GetTableName(), otherEntityForeignKeyProperty.GetColumnName()); var queryResults = conn.Query(tableMapping, query, primaryKeyValue); Array array = null; // Create a generic list of the expected type if (enclosedType == EnclosedType.List) { values = (IList)Activator.CreateInstance(typeof(List <>).MakeGenericType(entityType)); } else { values = array = Array.CreateInstance(entityType, queryResults.Count); } int i = 0; foreach (var result in queryResults) { // Replace obtained value with a cached one whenever possible bool loadedFromCache = false; var value = recursive ? ReplaceWithCacheObjectIfPossible(result, otherEntityPrimaryKeyProperty, objectCache, out loadedFromCache) : result; if (array != null) { array.SetValue(value, i); } else { values.Add(value); } if (!loadedFromCache) { cascadeElements.Add(result); } i++; } } relationshipProperty.SetValue(element, values, null); if (inverseProperty != null && values != null) { // Stablish inverse relationships (we already have that object anyway) foreach (var value in values) { inverseProperty.SetValue(value, element, null); } } if (recursive) { foreach (var child in cascadeElements) { conn.GetChildrenRecursive(child, true, recursive, objectCache); } } return(values); }
private static void UpdateOneToManyInverseForeignKey <T>(this SQLiteConnection conn, T element, PropertyInfo relationshipProperty) { var type = typeof(T); EnclosedType enclosedType; var entityType = relationshipProperty.GetEntityType(out enclosedType); var originPrimaryKeyProperty = type.GetPrimaryKey(); var inversePrimaryKeyProperty = entityType.GetPrimaryKey(); var inverseForeignKeyProperty = type.GetForeignKeyProperty(relationshipProperty, inverse: true); Debug.Assert(enclosedType != EnclosedType.None, "OneToMany relationships must be List or Array of entities"); Debug.Assert(originPrimaryKeyProperty != null, "OneToMany relationships require Primary Key in the origin entity"); Debug.Assert(inversePrimaryKeyProperty != null, "OneToMany relationships require Primary Key in the destination entity"); Debug.Assert(inverseForeignKeyProperty != null, "Unable to find foreign key for OneToMany relationship"); var inverseProperty = type.GetInverseProperty(relationshipProperty); if (inverseProperty != null) { EnclosedType inverseEnclosedType; var inverseEntityType = inverseProperty.GetEntityType(out inverseEnclosedType); Debug.Assert(inverseEnclosedType == EnclosedType.None, "OneToMany inverse relationship shouldn't be List or Array"); Debug.Assert(inverseEntityType == type, "OneToMany inverse relationship is not the expected type"); } var keyValue = originPrimaryKeyProperty.GetValue(element, null); var children = (IEnumerable)relationshipProperty.GetValue(element, null); var childrenKeyList = new List <object>(); if (children != null) { foreach (var child in children) { var childKey = inversePrimaryKeyProperty.GetValue(child, null); childrenKeyList.Add(childKey); inverseForeignKeyProperty.SetValue(child, keyValue, null); if (inverseProperty != null) { inverseProperty.SetValue(child, element, null); } } } string tableName = entityType.GetTableName(); string inverseForeignKeyName = inverseForeignKeyProperty.GetColumnName(); string inversePrimaryKeyName = inversePrimaryKeyProperty.GetColumnName(); // Objects already updated, now change the database var childrenKeysParams = String.Join(",", childrenKeyList.Select(x => "?")); var sqlParams = new List <object> { keyValue }; sqlParams.AddRange(childrenKeyList); var query = string.Format("update {0} set {1} = ? where {2} in ({3})", tableName, inverseForeignKeyName, inversePrimaryKeyName, childrenKeysParams); conn.Execute(query, sqlParams.ToArray()); // Delete previous relationships var deleteQuery = string.Format("update {0} set {1} = NULL where {1} == ? and {2} not in ({3})", tableName, inverseForeignKeyName, inversePrimaryKeyName, childrenKeysParams); conn.Execute(deleteQuery, sqlParams.ToArray()); }
/// <summary> /// Fetches all the entities of the specified type with the filter and fetches all the relationship /// properties of all the returned elements. /// </summary> /// <returns>List of all the elements of the type T that matches the filter with the children already loaded</returns> /// <param name="conn">SQLite Net connection object</param> /// <param name="filter">Filter that will be passed to the <c>Where</c> clause when fetching /// objects from the database. No relationship properties are allowed in this filter as they /// are loaded afterwards</param> /// <param name="recursive">If set to <c>true</c> all the relationships with /// <c>CascadeOperation.CascadeRead</c> will be loaded recusively.</param> /// <typeparam name="T">Entity type where the object should be fetched from</typeparam> public static List <T> GetAllWithChildren <T>(this SQLiteConnection conn, Expression <Func <T, bool> > filter = null, bool recursive = false) where T #if USING_MVVMCROSS : new() #else : class