public static IStatusGeneric <DecodedDto> GetOrCreateDtoInfo(this Type classType, DecodedEntityClass entityInfo, IGenericServicesConfig publicConfig, PerDtoConfig perDtoConfig) { var status = new StatusGenericHandler <DecodedDto>(); if (classType.IsPublic || classType.IsNestedPublic) { return(status.SetResult(DecodedDtoCache.GetOrAdd(classType, type => new DecodedDto(classType, entityInfo, publicConfig, perDtoConfig)))); } status.AddError($"Sorry, but the DTO/ViewModel class '{classType.Name}' must be public for GenericServices to work."); return(status); }
private static async Task <IStatusGeneric> SaveChangesWithExtrasAsync(this DbContext context, IGenericServicesConfig config, bool turnOffChangeTracker = false) { var status = config?.BeforeSaveChanges != null ? config.BeforeSaveChanges(context) : new StatusGenericHandler(); if (!status.IsValid) { return(status); } if (turnOffChangeTracker) { context.ChangeTracker.AutoDetectChangesEnabled = false; } try { //This handles SaveChangesExceptionHandlers that can fix the exception, and the SaveChanges i tried again do { try { await context.SaveChangesAsync().ConfigureAwait(false); break; //This breaks out of the do/while } catch (Exception e) { var exStatus = config?.SaveChangesExceptionHandler(e, context); if (exStatus == null) { throw; //error wasn't handled, so rethrow } status.CombineStatuses(exStatus); } } while (status.IsValid); } finally { context.ChangeTracker.AutoDetectChangesEnabled = true; } return(status); }
public DecodedDto(Type dtoType, DecodedEntityClass entityInfo, IGenericServicesConfig publicConfig, PerDtoConfig perDtoConfig) { DtoType = dtoType ?? throw new ArgumentNullException(nameof(dtoType)); _matcher = new MethodCtorMatcher(publicConfig.NameMatcher); _perDtoConfig = perDtoConfig; //can be null LinkedEntityInfo = entityInfo; if (entityInfo.EntityStyle == EntityStyles.OwnedType) { throw new InvalidOperationException($"{DtoType.Name}: You cannot use ILinkToEntity<T> with an EF Core Owned Type."); } if (entityInfo.EntityStyle == EntityStyles.HasNoKey) { //If HasNoKey or is OwnedType then exit immediately as properties etc return; } PropertyInfos = dtoType.GetProperties() .Select(x => new DecodedDtoProperty(x, BestPropertyMatch.FindMatch(x, entityInfo.PrimaryKeyProperties).Score >= PropertyMatch.PerfectMatchValue)) .ToImmutableList(); if (!PropertyInfos.Any()) { throw new InvalidOperationException($"The {DtoType.Name} class inherits ILinkToEntity<T> but has no properties in it!"); } if (entityInfo.CanBeUpdatedViaMethods) { _matchedSetterMethods = PreloadPossibleMethodCtorMatches(MatchMethodsToProperties(entityInfo), new DecodeName(_perDtoConfig?.UpdateMethod), null); } if (entityInfo.CanBeCreatedByCtorOrStaticMethod) { _matchedCtorsAndStaticMethods = PreloadPossibleMethodCtorMatches(MatchCtorsAndStaticMethodsToProperties(entityInfo), new DecodeName(_perDtoConfig?.UpdateMethod), null); } }
public RegisterOneDtoType(Type dtoType, Type[] typesInAssembly, IGenericServicesConfig configuration) { Header = dtoType.Name; var entityType = dtoType.GetLinkedEntityFromDto(err => AddError(err)); if (!IsValid) { return; } if (entityType == null) { AddError($"The class {dtoType.Name} is not registered as a valid CrudServices DTO/ViewModel." + $" Have you left off the {DecodedDtoExtensions.HumanReadableILinkToEntity} interface?"); return; } EntityInfo = entityType.GetRegisteredEntityInfo(); if (EntityInfo == null) { AddError($"The DTO/ViewModel class {dtoType.Name} is linked to a entity class {entityType.Name}," + $" but I couldn't find that class in the application's DbContext(s) you gave me to scan"); return; } var perDtoConfig = FindConfigInfoIfPresent(dtoType, entityType, typesInAssembly); if (!IsValid) { return; } ConfigGenerator = new CreateConfigGenerator(dtoType, EntityInfo, perDtoConfig); PerDtoConfig = perDtoConfig as PerDtoConfig; var decodeStatus = dtoType.GetOrCreateDtoInfo(EntityInfo, configuration, PerDtoConfig); CombineStatuses(decodeStatus); DtoInfo = decodeStatus.Result; }
//----------------------------------------------------------------- //private methods private static IStatusGeneric SaveChangesWithExtras(this DbContext context, IGenericServicesConfig config, bool turnOffChangeTracker = false) { var status = config?.BeforeSaveChanges != null ? config.BeforeSaveChanges(context) : new StatusGenericHandler(); if (!status.IsValid) { return(status); } if (turnOffChangeTracker) { context.ChangeTracker.AutoDetectChangesEnabled = false; } try { context.SaveChanges(); } catch (Exception e) { var exStatus = config?.SaveChangesExceptionHandler(e, context); if (exStatus == null) { throw; //error wasn't handled, so rethrow } status.CombineStatuses(exStatus); } finally { context.ChangeTracker.AutoDetectChangesEnabled = true; } return(status); }
public DecodedDto(Type dtoType, DecodedEntityClass entityInfo, IGenericServicesConfig publicConfig, PerDtoConfig perDtoConfig) { DtoType = dtoType ?? throw new ArgumentNullException(nameof(dtoType)); _matcher = new MethodCtorMatcher(publicConfig.NameMatcher); _perDtoConfig = perDtoConfig; //can be null LinkedEntityInfo = entityInfo; PropertyInfos = dtoType.GetProperties() .Select(x => new DecodedDtoProperty(x, BestPropertyMatch.FindMatch(x, entityInfo.PrimaryKeyProperties).Score >= PropertyMatch.PerfectMatchValue)) .ToImmutableList(); if (entityInfo.CanBeUpdatedViaMethods) { _matchedSetterMethods = PreloadPossibleMethodCtorMatches(MatchMethodsToProperties(entityInfo), new DecodeName(_perDtoConfig?.UpdateMethod), null); } if (entityInfo.CanBeCreatedByCtorOrStaticMethod) { _matchedCtorsAndStaticMethods = PreloadPossibleMethodCtorMatches(MatchCtorsAndStaticMethodsToProperties(entityInfo), new DecodeName(_perDtoConfig?.UpdateMethod), null); } }
/// <summary> /// This returns true if the SaveChanges should be validated /// Done this way to allow unit tests to have different DtoAccessValidateOnSave settings /// </summary> /// <param name="publicConfig"></param> /// <returns></returns> public bool ShouldValidateOnSave(IGenericServicesConfig publicConfig) { return(_perDtoConfig?.UseSaveChangesWithValidation ?? publicConfig.DtoAccessValidateOnSave); }
/// <summary> /// ctor /// </summary> /// <param name="publicConfig"></param> public SpecificUseData(IGenericServicesConfig publicConfig) { PublicConfig = publicConfig ?? new GenericServicesConfig(); ReadProfile = new MappingProfile(false); SaveProfile = new MappingProfile(true); }
internal GenericServicesSetupPart2(IServiceCollection services, IGenericServicesConfig publicConfig, IWrappedConfigAndMapper configAndMapper) { Services = services ?? throw new ArgumentNullException(nameof(services)); PublicConfig = publicConfig ?? throw new ArgumentNullException(nameof(publicConfig)); ConfigAndMapper = configAndMapper ?? throw new ArgumentNullException(nameof(configAndMapper)); }
public IWrappedConfigAndMapper ScanAllAssemblies(Assembly[] assembliesToScan, IGenericServicesConfig config) { if (assembliesToScan == null || assembliesToScan.Length == 0) { throw new ArgumentException("There were no assembles to scan!", nameof(assembliesToScan)); } foreach (var assembly in assembliesToScan) { RegisterDtosInAssemblyAndBuildMaps(assembly); } if (!IsValid) { //If errors then don't set up the mappings return(null); } return(CreateConfigAndMapper(config, _readProfile, _saveProfile)); }
public SetupDtosAndMappings(IGenericServicesConfig publicConfig) { PublicConfig = publicConfig ?? throw new ArgumentNullException(nameof(publicConfig)); }
/// <summary> /// This SaveChanges, with a boolean to decide whether to validate or not /// </summary> /// <param name="context"></param> /// <param name="shouldValidate"></param> /// <param name="config"></param> /// <returns></returns> public static IStatusGeneric SaveChangesWithOptionalValidation(this DbContext context, bool shouldValidate, IGenericServicesConfig config = null) { if (shouldValidate) { return(context.SaveChangesWithValidation(config)); } context.SaveChanges(); return(new StatusGenericHandler()); }
/// <summary> /// This will validate any entity classes that will be added or updated /// If the validation does not produce any errors then SaveChangesAsync will be called /// </summary> /// <param name="context"></param> /// <param name="config"></param> /// <returns>List of errors, empty if there were no errors</returns> public static async Task <IStatusGeneric> SaveChangesWithValidationAsync(this DbContext context, IGenericServicesConfig config = null) { var status = context.ExecuteValidation(); if (!status.IsValid) { return(status); } context.ChangeTracker.AutoDetectChangesEnabled = false; try { await context.SaveChangesAsync().ConfigureAwait(false); } catch (Exception e) { var exStatus = config?.SaveChangesExceptionHandler(e, context); if (exStatus == null) { throw; //error wasn't handled, so rethrow } status.CombineStatuses(exStatus); } finally { context.ChangeTracker.AutoDetectChangesEnabled = true; } return(status); }
internal GenericServicesSetupPart2(IServiceCollection services, IGenericServicesConfig publicConfig, IWrappedAutoMapperConfig autoMapperConfig) { Services = services ?? throw new ArgumentNullException(nameof(services)); PublicConfig = publicConfig ?? throw new ArgumentNullException(nameof(publicConfig)); AutoMapperConfig = autoMapperConfig ?? throw new ArgumentNullException(nameof(autoMapperConfig)); }
//see https://blogs.msdn.microsoft.com/dotnet/2016/09/29/implementing-seeding-custom-conventions-and-interceptors-in-ef-core-1-0/ //for why I call DetectChanges before ChangeTracker, and why I then turn ChangeTracker.AutoDetectChangesEnabled off/on around SaveChanges /// <summary> /// This SaveChangesAsync, with a boolean to decide whether to validate or not /// </summary> /// <param name="context"></param> /// <param name="shouldValidate"></param> /// <param name="config"></param> /// <returns></returns> public static async Task <IStatusGeneric> SaveChangesWithOptionalValidationAsync(this DbContext context, bool shouldValidate, IGenericServicesConfig config) { if (shouldValidate) { return(await context.SaveChangesWithValidationAsync(config).ConfigureAwait(false)); } await context.SaveChangesAsync().ConfigureAwait(false); return(new StatusGenericHandler()); }
//see https://blogs.msdn.microsoft.com/dotnet/2016/09/29/implementing-seeding-custom-conventions-and-interceptors-in-ef-core-1-0/ //for why I call DetectChanges before ChangeTracker, and why I then turn ChangeTracker.AutoDetectChangesEnabled off/on around SaveChanges /// <summary> /// This SaveChangesAsync, with a boolean to decide whether to validate or not /// </summary> /// <param name="context"></param> /// <param name="shouldValidate"></param> /// <param name="config"></param> /// <returns></returns> public static async Task <IStatusGeneric> SaveChangesWithOptionalValidationAsync(this DbContext context, bool shouldValidate, IGenericServicesConfig config) { return(shouldValidate ? await context.SaveChangesWithValidationAsync(config).ConfigureAwait(false) : await context.SaveChangesWithExtrasAsync(config).ConfigureAwait(false)); }
internal WrappedAndMapper(IGenericServicesConfig config, MapperConfiguration mapperReadConfig, MapperConfiguration mapperSaveConfig) { Config = config ?? throw new ArgumentNullException(nameof(config)); MapperReadConfig = mapperReadConfig ?? throw new ArgumentNullException(nameof(mapperReadConfig)); MapperSaveConfig = mapperSaveConfig ?? throw new ArgumentNullException(nameof(mapperSaveConfig)); }
/// <summary> /// This SaveChanges, with a boolean to decide whether to validate or not /// </summary> /// <param name="context"></param> /// <param name="shouldValidate"></param> /// <param name="config"></param> /// <returns></returns> public static IStatusGeneric SaveChangesWithOptionalValidation(this DbContext context, bool shouldValidate, IGenericServicesConfig config) { return(shouldValidate ? context.SaveChangesWithValidation(config) : context.SaveChangesWithExtras(config)); }
//see https://blogs.msdn.microsoft.com/dotnet/2016/09/29/implementing-seeding-custom-conventions-and-interceptors-in-ef-core-1-0/ //for why I call DetectChanges before ChangeTracker, and why I then turn ChangeTracker.AutoDetectChangesEnabled off/on around SaveChanges /// <summary> /// This will validate any entity classes that will be added or updated /// If the validation does not produce any errors then SaveChanges will be called /// </summary> /// <param name="context"></param> /// <param name="config"></param> /// <returns>List of errors, empty if there were no errors</returns> public static IStatusGeneric SaveChangesWithValidation(this DbContext context, IGenericServicesConfig config = null) { var status = context.ExecuteValidation(); return(!status.IsValid ? status : context.SaveChangesWithExtras(config, true)); }
/// <summary> /// This will validate any entity classes that will be added or updated /// If the validation does not produce any errors then SaveChangesAsync will be called /// </summary> /// <param name="context"></param> /// <param name="config"></param> /// <returns>List of errors, empty if there were no errors</returns> public static async Task <IStatusGeneric> SaveChangesWithValidationAsync(this DbContext context, IGenericServicesConfig config = null) { var status = context.ExecuteValidation(); return(!status.IsValid ? status : await context.SaveChangesWithExtrasAsync(config, true)); }