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);
        }
예제 #2
0
        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);
        }
예제 #3
0
        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);
            }
        }
예제 #4
0
        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);
            }
        }
예제 #7
0
 /// <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));
 }
예제 #12
0
 /// <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());
 }
예제 #13
0
        /// <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));
 }
예제 #15
0
        //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());
        }
예제 #16
0
        //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));
        }
예제 #17
0
 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));
 }
예제 #18
0
 /// <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));
 }
예제 #19
0
        //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));
        }
예제 #20
0
        /// <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));
        }