private static IEnumerable <ReferingType> GetReferingTypes(IModel model, Type entityType) { if (ReferingTypesListsCache.TryGetValue(entityType, out List <ReferingType> referingTypes)) { return(referingTypes); } referingTypes = new List <ReferingType>(); if (!ReferingTypesListsCache.TryAdd(entityType, referingTypes)) { //it is there already (done in another thread) return(ReferingTypesListsCache[entityType]); } //find all potential references var types = model.Metadata.Types().Where(t => typeof(IInstantiableEntity).GetTypeInfo().IsAssignableFrom(t.Type)); // ReSharper disable once LoopCanBeConvertedToQuery foreach (var type in types) { if (!ReferingTypesCache.TryGetValue(type.Type, out ReferingType rt)) { var singleReferences = type.Properties.Values.Where(p => p.EntityAttribute != null && p.EntityAttribute.Order > 0 && p.PropertyInfo.PropertyType.GetTypeInfo().IsAssignableFrom(entityType)).ToList(); var listReferences = type.Properties.Values.Where(p => p.EntityAttribute != null && p.EntityAttribute.Order > 0 && p.PropertyInfo.PropertyType.GetTypeInfo().IsGenericType&& p.PropertyInfo.PropertyType.GenericTypeArgumentIsAssignableFrom(entityType)).ToList(); var nestedListReferences = type.Properties.Values.Where(p => p.EntityAttribute != null && p.EntityAttribute.Order > 0 && p.PropertyInfo.PropertyType.GetTypeInfo().IsGenericType&& p.PropertyInfo.PropertyType.GetItemTypeFromGenericType().IsGenericType&& p.PropertyInfo.PropertyType.GetItemTypeFromGenericType().GenericTypeArgumentIsAssignableFrom(entityType)).ToList(); if (!singleReferences.Any() && !listReferences.Any() && !nestedListReferences.Any()) { continue; } rt = new ReferingType { Type = type, SingleReferences = singleReferences, ListReferences = listReferences, NestedListReferences = nestedListReferences }; ReferingTypesCache.TryAdd(type.Type, rt); } referingTypes.Add(rt); } return(referingTypes); }
/// <summary> /// Deletes references to specified entity from all entities in the model where entity is /// a references as an object or as a member of a collection. /// </summary> /// <param name="model">Model to be used</param> /// <param name="entities">Entities to be replaced</param> /// <param name="referingType">Candidate type containing reference to the type of entity</param> /// <param name="replacement">New reference. If this is null it just removes references to entity</param> private static void ReplaceReferences <TEntity, TReplacement>(IModel model, IEnumerable <TEntity> entities, ReferingType referingType, TReplacement replacement) where TEntity : IPersistEntity where TReplacement : TEntity { if (entities == null || !entities.Any()) { return; } // use hash set for quick object reference matching var hash = new HashSet <object>(entities.Cast <object>()); //get all instances of this type and nullify and remove the entity var entitiesToCheck = model.Instances.OfType(referingType.Type.Type.Name, true); foreach (var toCheck in entitiesToCheck) { //check properties foreach (var pInfo in referingType.SingleReferences.Select(p => p.PropertyInfo)) { var pVal = pInfo.GetValue(toCheck); if (pVal == null && replacement == null) { continue; } //it is enough to compare references if (!hash.Contains(pVal)) { continue; } pInfo.SetValue(toCheck, replacement); } foreach (var pInfo in referingType.ListReferences.Select(p => p.PropertyInfo)) { var pVal = pInfo.GetValue(toCheck); if (pVal == null) { continue; } //it might be uninitialized optional item set if (pVal is IOptionalItemSet optSet && !optSet.Initialized) { continue; } //or it is non-optional item set implementing IList if (!(pVal is IList itemSet)) { throw new XbimException($"Unable to remove items from {referingType.Type.Name}.{pInfo.Name}. No IList implementation."); } for (int i = 0; i < itemSet.Count; i++) { var item = itemSet[i]; if (!hash.Contains(item)) { continue; } itemSet.RemoveAt(i); if (replacement != null) { itemSet.Insert(i, replacement); } else { i--; // keep in sync } } } } }
/// <summary> /// Deletes references to specified entity from all entities in the model where entity is /// a references as an object or as a member of a collection. /// </summary> /// <param name="model">Model to be used</param> /// <param name="entity">Entity to be removed from references</param> /// <param name="referingType">Candidate type containing reference to the type of entity</param> /// <param name="replacement">New reference. If this is null it just removes references to entity</param> private static void ReplaceReferences <TEntity, TReplacement>(IModel model, TEntity entity, ReferingType referingType, TReplacement replacement) where TEntity : IPersistEntity where TReplacement : TEntity { if (entity == null) { return; } //get all instances of this type and nullify and remove the entity var entitiesToCheck = model.Instances.OfType(referingType.Type.Type.Name, true); foreach (var toCheck in entitiesToCheck) { //check properties foreach (var pInfo in referingType.SingleReferences.Select(p => p.PropertyInfo)) { var pVal = pInfo.GetValue(toCheck); if (pVal == null && replacement == null) { continue; } //it is enough to compare references if (!ReferenceEquals(pVal, entity)) { continue; } pInfo.SetValue(toCheck, replacement); } foreach (var pInfo in referingType.ListReferences.Select(p => p.PropertyInfo)) { var pVal = pInfo.GetValue(toCheck); if (pVal == null) { continue; } //it might be uninitialized optional item set var optSet = pVal as IOptionalItemSet; if (optSet != null && !optSet.Initialized) { continue; } //or it is non-optional item set implementing IList var itemSet = pVal as IList; if (itemSet != null) { if (itemSet.Contains(entity)) { itemSet.Remove(entity); } if (replacement != null) { itemSet.Add(replacement); } continue; } //fall back operating on common list functions using reflection (this is slow) var contMethod = pInfo.PropertyType.GetTypeInfo().GetMethod("Contains"); if (contMethod == null) { var msg = string.Format( "It wasn't possible to check containment of entity {0} in property {1} of {2}. No suitable method found.", entity.GetType().Name, pInfo.Name, toCheck.GetType().Name); throw new XbimException(msg); } var contains = (bool)contMethod.Invoke(pVal, new object[] { entity }); if (!contains) { continue; } var removeMethod = pInfo.PropertyType.GetTypeInfo().GetMethod("Remove"); if (removeMethod == null) { var msg = string.Format( "It wasn't possible to remove reference to entity {0} in property {1} of {2}. No suitable method found.", entity.GetType().Name, pInfo.Name, toCheck.GetType().Name); throw new XbimException(msg); } removeMethod.Invoke(pVal, new object[] { entity }); if (replacement == null) { continue; } var addMethod = pInfo.PropertyType.GetTypeInfo().GetMethod("Add"); if (addMethod == null) { var msg = string.Format( "It wasn't possible to add reference to entity {0} in property {1} of {2}. No suitable method found.", entity.GetType().Name, pInfo.Name, toCheck.GetType().Name); throw new XbimException(msg); } addMethod.Invoke(pVal, new object[] { replacement }); } } }