public virtual Task <ReverseEngineerFiles> GenerateAsync(
            [NotNull] ReverseEngineeringConfiguration configuration,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            Check.NotNull(configuration, nameof(configuration));

            cancellationToken.ThrowIfCancellationRequested();

            configuration.CheckValidity();

            var metadataModel = GetMetadataModel(configuration);

            var outputPathsAndNamespace = ConstructNamespaceAndCanonicalizedPaths(
                configuration.ProjectRootNamespace,
                configuration.ProjectPath, configuration.OutputPath);

            var customConfiguration = _configurationFactory
                                      .CreateCustomConfiguration(
                configuration.ConnectionString, configuration.ContextClassName,
                outputPathsAndNamespace.Namespace, configuration.UseFluentApiOnly);
            var modelConfiguration = _configurationFactory
                                     .CreateModelConfiguration(metadataModel, customConfiguration);

            var dbContextClassName =
                string.IsNullOrWhiteSpace(customConfiguration.ContextClassName)
                    ? modelConfiguration.ClassName()
                    : customConfiguration.ContextClassName;

            CheckOutputFiles(outputPathsAndNamespace.CanonicalizedFullOutputPath,
                             dbContextClassName, metadataModel, configuration.OverwriteFiles);

            return(CodeWriter.WriteCodeAsync(
                       modelConfiguration, outputPathsAndNamespace.CanonicalizedFullOutputPath,
                       dbContextClassName, cancellationToken));
        }
        public void Throws_exceptions_for_incorrect_configuration()
        {
            var configuration = new ReverseEngineeringConfiguration
            {
                ConnectionString = null,
                ProjectPath      = null,
                OutputPath       = null
            };

            Assert.Equal(CommandsStrings.ConnectionStringRequired,
                         Assert.Throws <ArgumentException>(
                             () => configuration.CheckValidity()).Message);

            configuration.ConnectionString = "NonEmptyConnectionString";
            Assert.Equal(CommandsStrings.ProjectPathRequired,
                         Assert.Throws <ArgumentException>(
                             () => configuration.CheckValidity()).Message);

            configuration.ProjectPath = "NonEmptyProjectPath";
            Assert.Equal(CommandsStrings.RootNamespaceRequired,
                         Assert.Throws <ArgumentException>(
                             () => configuration.CheckValidity()).Message);

            configuration.ContextClassName = @"Invalid!CSharp*Class&Name";
            Assert.Equal(CommandsStrings.ContextClassNotValidCSharpIdentifier(@"Invalid!CSharp*Class&Name"),
                         Assert.Throws <ArgumentException>(
                             () => configuration.CheckValidity()).Message);

            configuration.ContextClassName = "1CSharpClassNameCannotStartWithNumber";
            Assert.Equal(CommandsStrings.ContextClassNotValidCSharpIdentifier("1CSharpClassNameCannotStartWithNumber"),
                         Assert.Throws <ArgumentException>(
                             () => configuration.CheckValidity()).Message);

            configuration.ContextClassName = "volatile";  // cannot be C# keyword
            Assert.Equal(CommandsStrings.ContextClassNotValidCSharpIdentifier("volatile"),
                         Assert.Throws <ArgumentException>(
                             () => configuration.CheckValidity()).Message);

            configuration.ContextClassName = "GoodClassName";
            configuration.OutputPath       = @"\AnAbsolutePath";
            Assert.Equal(CommandsStrings.RootNamespaceRequired,
                         Assert.Throws <ArgumentException>(
                             () => configuration.CheckValidity()).Message);

            configuration.OutputPath = @"A\Relative\Path";
            Assert.Equal(CommandsStrings.RootNamespaceRequired,
                         Assert.Throws <ArgumentException>(
                             () => configuration.CheckValidity()).Message);
        }
        public void Throws_exceptions_for_incorrect_configuration()
        {
            var configuration = new ReverseEngineeringConfiguration
            {
                ConnectionString = null,
                ProjectPath = null,
                OutputPath = null
            };

            Assert.Equal(DesignCoreStrings.ConnectionStringRequired,
                Assert.Throws<ArgumentException>(
                    () => configuration.CheckValidity()).Message);

            configuration.ConnectionString = "NonEmptyConnectionString";
            Assert.Equal(DesignCoreStrings.ProjectPathRequired,
                Assert.Throws<ArgumentException>(
                    () => configuration.CheckValidity()).Message);

            configuration.ProjectPath = "NonEmptyProjectPath";
            Assert.Equal(DesignCoreStrings.RootNamespaceRequired,
                Assert.Throws<ArgumentException>(
                    () => configuration.CheckValidity()).Message);

            configuration.ContextClassName = @"Invalid!CSharp*Class&Name";
            Assert.Equal(DesignCoreStrings.ContextClassNotValidCSharpIdentifier(@"Invalid!CSharp*Class&Name"),
                Assert.Throws<ArgumentException>(
                    () => configuration.CheckValidity()).Message);

            configuration.ContextClassName = "1CSharpClassNameCannotStartWithNumber";
            Assert.Equal(DesignCoreStrings.ContextClassNotValidCSharpIdentifier("1CSharpClassNameCannotStartWithNumber"),
                Assert.Throws<ArgumentException>(
                    () => configuration.CheckValidity()).Message);

            configuration.ContextClassName = "volatile"; // cannot be C# keyword
            Assert.Equal(DesignCoreStrings.ContextClassNotValidCSharpIdentifier("volatile"),
                Assert.Throws<ArgumentException>(
                    () => configuration.CheckValidity()).Message);

            configuration.ContextClassName = "GoodClassName";
            configuration.OutputPath = @"\AnAbsolutePath";
            Assert.Equal(DesignCoreStrings.RootNamespaceRequired,
                Assert.Throws<ArgumentException>(
                    () => configuration.CheckValidity()).Message);

            configuration.OutputPath = @"A\Relative\Path";
            Assert.Equal(DesignCoreStrings.RootNamespaceRequired,
                Assert.Throws<ArgumentException>(
                    () => configuration.CheckValidity()).Message);
        }
        public virtual IModel GetMetadataModel([NotNull] ReverseEngineeringConfiguration configuration)
        {
            Check.NotNull(configuration, nameof(configuration));

            var metadataModel = _factory.Create(
                configuration.ConnectionString, configuration.TableSelectionSet);

            if (metadataModel == null)
            {
                throw new InvalidOperationException(
                          RelationalDesignStrings.ProviderReturnedNullModel(
                              _factory.GetType().FullName));
            }

            return(metadataModel);
        }
        public virtual Task<ReverseEngineerFiles> ReverseEngineerAsync(
            [NotNull] string provider,
            [NotNull] string connectionString,
            [CanBeNull] string outputDir,
            [CanBeNull] string dbContextClassName,
            [NotNull] IEnumerable<string> schemas,
            [NotNull] IEnumerable<string> tables,
            bool useDataAnnotations,
            bool overwriteFiles,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            Check.NotEmpty(provider, nameof(provider));
            Check.NotEmpty(connectionString, nameof(connectionString));
            Check.NotNull(schemas, nameof(schemas));
            Check.NotNull(tables, nameof(tables));

            var services = _servicesBuilder.Build(provider);

            var loggerFactory = services.GetRequiredService<ILoggerFactory>();
            loggerFactory.AddProvider(_loggerProvider);

            var generator = services.GetRequiredService<ReverseEngineeringGenerator>();
            var tableSelectionSet = new TableSelectionSet(tables, schemas);
            var configuration = new ReverseEngineeringConfiguration
            {
                ConnectionString = connectionString,
                ContextClassName = dbContextClassName,
                ProjectPath = _projectDir,
                ProjectRootNamespace = _rootNamespace,
                OutputPath = outputDir,
                TableSelectionSet = tableSelectionSet,
                UseFluentApiOnly = !useDataAnnotations,
                OverwriteFiles = overwriteFiles
            };

            return generator.GenerateAsync(configuration, cancellationToken);
        }