private async ValueTask <object> LoadNavigationSingleton(object principalInstance, INavigation navigation, byte cascadeLevel) { byte levelToLookFor = _whatDoing == CascadeSoftDelWhatDoing.SoftDelete ? (byte)0 //if soft deleting then look for un-deleted entries : (byte)(cascadeLevel + 1); //otherwise look for next level up //for everything else we need to load the singleton with a IgnoreQueryFilters method var navValueType = navigation.PropertyInfo.PropertyType; var genericHelperType = typeof(GenericSingletonLoader <>).MakeGenericType(typeof(TInterface), navValueType); dynamic loader = Activator.CreateInstance(genericHelperType, _context, _config, _isAsync, principalInstance, navigation.PropertyInfo, levelToLookFor); var navValueTask = loader.GetFilteredSingleton(); if (_isAsync) { return(await navValueTask); } return(ValueTaskSyncCheckers.CheckSyncValueTaskWorkedDynamicAndReturnResult <object>(navValueTask)); }
private async ValueTask <IEnumerable> LoadNavigationCollection(object principalInstance, INavigation navigation, byte cascadeLevel) { byte levelToLookFor = _whatDoing == CascadeSoftDelWhatDoing.SoftDelete ? (byte)0 //if soft deleting then look for un-deleted entries : (byte)(cascadeLevel + 1); //otherwise look for next level up var navValueType = navigation.PropertyInfo.PropertyType; var innerType = navValueType.GetGenericArguments().Single(); var genericHelperType = typeof(GenericCollectionLoader <>).MakeGenericType(typeof(TInterface), innerType); dynamic loader = Activator.CreateInstance(genericHelperType, _context, _config, _isAsync, principalInstance, navigation.PropertyInfo, levelToLookFor); var navValueTask = loader.GetFilteredEntities(); if (_isAsync) { return(await navValueTask); } return(ValueTaskSyncCheckers.CheckSyncValueTaskWorkedDynamicAndReturnResult <IEnumerable>(navValueTask)); }
public async ValueTask WalkEntitiesSoftDelete(object principalInstance, byte cascadeLevel) { if (!(principalInstance is TInterface castToCascadeSoftDelete && principalInstance.GetType().IsClass) || _stopCircularLook.Contains(principalInstance)) { return; //isn't something we need to consider, or we saw it before, so it returns } _stopCircularLook.Add(principalInstance); //we keep a reference to this to stop the method going in a circular loop if (ApplyChangeIfAppropriate(castToCascadeSoftDelete, cascadeLevel)) { //If the entity shouldn't be changed then we leave this entity and any of it children return; } var principalNavs = _context.Entry(principalInstance) .Metadata.GetNavigations() .Where(x => !x.IsOnDependent && //navigational link goes to dependent entity(s) //The developer has whatDoing a Cascade delete behaviour (two options) on this link (x.ForeignKey.DeleteBehavior == DeleteBehavior.ClientCascade || x.ForeignKey.DeleteBehavior == DeleteBehavior.Cascade)) .ToList(); foreach (var navigation in principalNavs) { if (navigation.PropertyInfo == null) { //This could be changed by enhancing the navigation.PropertyInfo.GetValue(principalInstance); throw new NotImplementedException("Currently only works with navigation links that are properties"); } //It loads the current navigational value so that we can limit the number of database selects if the data is already loaded var navValue = navigation.PropertyInfo.GetValue(principalInstance); if (navigation.IsCollection) { if (_readEveryTime || navValue == null) { var navValueTask = LoadNavigationCollection(principalInstance, navigation, cascadeLevel); if (_isAsync) { navValue = await navValueTask; } else { navValue = ValueTaskSyncCheckers.CheckSyncValueTaskWorkedDynamicAndReturnResult <IEnumerable>(navValueTask); } } if (navValue == null) { return; //no relationship } foreach (var entity in navValue as IEnumerable) { var walkValueTask = WalkEntitiesSoftDelete(entity, (byte)(cascadeLevel + 1)); if (_isAsync) { await walkValueTask; } else { walkValueTask.CheckSyncValueTaskWorked(); } } } else { if (_readEveryTime || navValue == null) { var navValueTask = LoadNavigationSingleton(principalInstance, navigation, cascadeLevel); if (_isAsync) { navValue = await navValueTask; } else { navValue = navValueTask.CheckSyncValueTaskWorkedAndReturnResult(); } } if (navValue == null) { return; //no relationship } var walkValueTask = WalkEntitiesSoftDelete(navValue, (byte)(cascadeLevel + 1)); if (_isAsync) { await walkValueTask; } else { walkValueTask.CheckSyncValueTaskWorked(); } } } }