Пример #1
0
        public void E2ETestAllFluentApi()
        {
            var configuration = new ReverseEngineeringConfiguration
            {
                ConnectionString     = _connectionString,
                ProjectPath          = TestProjectDir,
                ProjectRootNamespace = TestNamespace,
                OutputPath           = null, // not used for this test
                UseFluentApiOnly     = true,
                TableSelectionSet    = Filter,
            };

            var filePaths = Generator.GenerateAsync(configuration).GetAwaiter().GetResult();

            var actualFileSet = new FileSet(InMemoryFiles, Path.GetFullPath(TestProjectDir))
            {
                Files = Enumerable.Repeat(filePaths.ContextFile, 1).Concat(filePaths.EntityTypeFiles).Select(Path.GetFileName).ToList()
            };

            var expectedFileSet = new FileSet(new FileSystemFileService(),
                                              Path.Combine("ReverseEngineering", "ExpectedResults", "E2E_AllFluentApi"),
                                              inputFile => inputFile.Replace("{{connectionString}}", _connectionString))
            {
                Files = new List <string> {
                    "E2EContext.expected"
                }
                .Concat(_expectedEntityTypeFiles).ToList()
            };

            AssertEqualFileContents(expectedFileSet, actualFileSet);
            AssertCompile(actualFileSet);
        }
Пример #2
0
        public virtual IEnumerable <string> ReverseEngineer(
            [NotNull] string providerAssemblyName,
            [NotNull] string connectionString,
            [NotNull] string rootNamespace,
            [NotNull] string projectDir)
        {
            Check.NotNull(providerAssemblyName, nameof(providerAssemblyName));
            Check.NotEmpty(connectionString, nameof(connectionString));
            Check.NotEmpty(rootNamespace, nameof(rootNamespace));
            Check.NotEmpty(projectDir, nameof(projectDir));

            var assembly = Assembly.Load(new AssemblyName(providerAssemblyName));

            if (assembly == null)
            {
                throw new InvalidOperationException(Strings.CannotFindAssembly(providerAssemblyName));
            }

            var configuration = new ReverseEngineeringConfiguration()
            {
                ProviderAssembly = assembly,
                ConnectionString = connectionString,
                Namespace        = rootNamespace,
                OutputPath       = projectDir
            };

            var generator = new ReverseEngineeringGenerator(_serviceProvider);

            return(generator.GenerateAsync(configuration).Result);
        }
Пример #3
0
        public void E2ETestUseAttributesInsteadOfFluentApi()
        {
            var configuration = new ReverseEngineeringConfiguration
            {
                ConnectionString     = _connectionString,
                ContextClassName     = "AttributesContext",
                ProjectPath          = TestProjectDir + Path.DirectorySeparatorChar, // tests that ending DirectorySeparatorChar does not affect namespace
                ProjectRootNamespace = TestNamespace,
                OutputPath           = TestSubDir,
                TableSelectionSet    = Filter,
            };

            var filePaths = Generator.GenerateAsync(configuration).GetAwaiter().GetResult();

            var actualFileSet = new FileSet(InMemoryFiles, Path.GetFullPath(Path.Combine(TestProjectDir, TestSubDir)))
            {
                Files = Enumerable.Repeat(filePaths.ContextFile, 1).Concat(filePaths.EntityTypeFiles).Select(Path.GetFileName).ToList()
            };

            var expectedFileSet = new FileSet(new FileSystemFileService(),
                                              Path.Combine("ReverseEngineering", "ExpectedResults", "E2E_UseAttributesInsteadOfFluentApi"),
                                              contents => contents.Replace("namespace " + TestNamespace, "namespace " + TestNamespace + "." + TestSubDir)
                                              .Replace("{{connectionString}}", _connectionString))
            {
                Files = new List <string> {
                    "AttributesContext.expected"
                }
                .Concat(_expectedEntityTypeFiles).ToList()
            };

            AssertEqualFileContents(expectedFileSet, actualFileSet);
            AssertCompile(actualFileSet);
        }
Пример #4
0
        public virtual Task <ReverseEngineerFiles> ReverseEngineerAsync(
            [NotNull] string provider,
            [NotNull] string connectionString,
            [CanBeNull] string outputDir,
            [CanBeNull] string dbContextClassName,
            [CanBeNull] string tableFilters,
            bool useFluentApiOnly,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            Check.NotEmpty(provider, nameof(provider));
            Check.NotEmpty(connectionString, nameof(connectionString));

            var services = _servicesBuilder.Build(provider);

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

            loggerFactory.AddProvider(_loggerProvider);

            var generator         = services.GetRequiredService <ReverseEngineeringGenerator>();
            var tableSelectionSet = TableSelectionSetBuilder.BuildFromString(tableFilters);
            var configuration     = new ReverseEngineeringConfiguration
            {
                ConnectionString     = connectionString,
                ContextClassName     = dbContextClassName,
                ProjectPath          = _projectDir,
                ProjectRootNamespace = _rootNamespace,
                OutputPath           = outputDir,
                TableSelectionSet    = tableSelectionSet,
                UseFluentApiOnly     = useFluentApiOnly
            };

            return(generator.GenerateAsync(configuration, cancellationToken));
        }
Пример #5
0
        public virtual Task <IReadOnlyList <string> > ReverseEngineerAsync(
            [NotNull] string runtimeProviderAssemblyName,
            [NotNull] string connectionString,
            [NotNull] string rootNamespace,
            [NotNull] string projectDir,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            Check.NotEmpty(runtimeProviderAssemblyName, nameof(runtimeProviderAssemblyName));
            Check.NotEmpty(connectionString, nameof(connectionString));
            Check.NotEmpty(rootNamespace, nameof(rootNamespace));
            Check.NotEmpty(projectDir, nameof(projectDir));

            var designTimeMetadataProviderFactory =
                GetDesignTimeMetadataProviderFactory(runtimeProviderAssemblyName);
            var serviceCollection = SetupInitialServices();

            designTimeMetadataProviderFactory.AddMetadataProviderServices(serviceCollection);

            var serviceProvider    = serviceCollection.BuildServiceProvider();
            var designTimeProvider = serviceProvider.GetRequiredService <IDatabaseMetadataModelProvider>();
            var generator          = serviceProvider.GetRequiredService <ReverseEngineeringGenerator>();
            var configuration      = new ReverseEngineeringConfiguration
            {
                Provider           = designTimeProvider,
                ConnectionString   = connectionString,
                Namespace          = rootNamespace,
                CustomTemplatePath = projectDir,
                OutputPath         = projectDir
            };

            return(generator.GenerateAsync(configuration, cancellationToken));
        }
        public virtual Task <ReverseEngineerFiles> ScaffoldContextAsync(
            [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 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));
        }
Пример #7
0
        public ScaffoldingDto ScaffoldDatabase(string connectionString, string rootNamespace, string contextName)
        {
            var scaffUtils           = new ScaffoldingUtilities();
            var csharpUtils          = new CSharpUtilities();
            var provider             = new SqlServerAnnotationProvider();
            var customConfiguration  = new CustomConfiguration(connectionString, contextName, rootNamespace, true);
            var entityTypeWriter     = new EntityTypeWriter(csharpUtils);
            var dbContextWriter      = new SingularDbContextWriter(scaffUtils, csharpUtils);
            var configurationFactory = new ConfigurationFactory(provider, csharpUtils, scaffUtils);
            var loggerFactory        = new LoggerFactory();
            var logger        = loggerFactory.CreateLogger <SqlServerDatabaseModelFactory>();
            var revEngeConfig = new ReverseEngineeringConfiguration {
                ConnectionString = connectionString
            };
            var sqlScaffoldModelFactory = new SingularScaffoldingModelFactory(new LoggerFactory(),
                                                                              new SqlServerTypeMapper(), new SqlServerDatabaseModelFactory(logger), new CandidateNamingService());
            var codeWriter = new StringBuilderCodeWriter(new FileSystemFileService(), dbContextWriter, entityTypeWriter);

            var generator          = new ReverseEngineeringGenerator(sqlScaffoldModelFactory, configurationFactory, codeWriter);
            var metaModel          = generator.GetMetadataModel(revEngeConfig);
            var modelConfiguration = configurationFactory.CreateModelConfiguration(metaModel, customConfiguration);
            var dbContextData      = dbContextWriter.WriteCode(modelConfiguration);
            var scaffoldingDto     = new ScaffoldingDto
            {
                DbContextSource = dbContextData,
                ModelSources    = modelConfiguration.EntityConfigurations
                                  .Select(entityConfiguration => entityTypeWriter.WriteCode(entityConfiguration))
                                  .ToImmutableList()
            };

            return(scaffoldingDto);
        }
Пример #8
0
        public void E2ETest()
        {
            // set current cultures to English because expected results for error messages
            // (both those output to the Logger and those put in comments in the .cs files)
            // are in English
#if DNXCORE50
            CultureInfo.CurrentCulture   = new CultureInfo("en-US");
            CultureInfo.CurrentUICulture = new CultureInfo("en-US");
#else
            Thread.CurrentThread.CurrentCulture   = new CultureInfo("en-US");
            Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
#endif

            var assembly      = Assembly.Load(new AssemblyName(DatabaseTool._defaultReverseEngineeringProviderAssembly));
            var configuration = new ReverseEngineeringConfiguration
            {
                ProviderAssembly = assembly,
                ConnectionString = E2EConnectionString,
                Namespace        = TestNamespace,
                OutputPath       = TestOutputDir
            };

            var serviceProvider = SetupServiceProvider();
            var fileService     = new InMemoryFileService();
            serviceProvider.AddService(typeof(IFileService), fileService);
            var logger = new InMemoryCommandLogger("E2ETest");
            serviceProvider.AddService(typeof(ILogger), logger);

            var expectedFileContents = InitializeExpectedFileContents();

            var generator = new ReverseEngineeringGenerator(serviceProvider);
            var filePaths = generator.GenerateAsync(configuration).Result;

            Assert.Equal(_E2ETestExpectedWarnings.Count, logger.WarningMessages.Count);
            // loop over warnings instead of using the collection form of Assert.Equal()
            // to give better error messages if it does fail. Similarly for file paths below.
            var i = 0;
            foreach (var expectedWarning in _E2ETestExpectedWarnings)
            {
                Assert.Equal(expectedWarning, logger.WarningMessages[i++]);
            }
            Assert.Equal(0, logger.InformationMessages.Count);
            Assert.Equal(0, logger.VerboseMessages.Count);

            var expectedFilePaths = _E2ETestExpectedFileNames.Select(name => TestOutputDir + @"\" + name);
            Assert.Equal(expectedFilePaths.Count(), filePaths.Count);
            i = 0;
            foreach (var expectedFilePath in expectedFilePaths)
            {
                Assert.Equal(expectedFilePath, filePaths[i++]);
            }

            foreach (var fileName in _E2ETestExpectedFileNames)
            {
                var fileContents = fileService.RetrieveFileContents(TestOutputDir, fileName);
                Assert.Equal(expectedFileContents[fileName], fileContents);
            }
        }
Пример #9
0
        public static string Get(string connectionString, string assmNamespace)
        {
            // setup
            var services = new ServiceCollection()
                           .AddLogging()
                           .AddSingleton <ReverseEngineeringGenerator>()
                           .AddSingleton <ScaffoldingUtilities>()
                           .AddSingleton <CSharpUtilities>()
                           .AddSingleton <ConfigurationFactory>()
                           .AddSingleton <DbContextWriter>()
                           .AddSingleton <EntityTypeWriter>()
                           .AddSingleton <CodeWriter, StringBuilderCodeWriter>()
                           .AddSingleton <CandidateNamingService, EntityNamingService>()
                           .AddSingleton(typeof(IFileService), sp => {
                return(InMemoryFiles = new InMemoryFileService());
            });

            new SqliteDesignTimeServices().ConfigureDesignTimeServices(services);
            var serviceProvider         = services.BuildServiceProvider();
            var generator               = serviceProvider.GetRequiredService <ReverseEngineeringGenerator>();
            var scaffoldingModelFactory = serviceProvider.GetRequiredService <IScaffoldingModelFactory>();

            var programName = "Ctx";
            var outputPath  = Environment.GetEnvironmentVariable("TEMP");

            if (string.IsNullOrEmpty(outputPath))
            {
                outputPath = "/tmp";
            }
            var conf = new ReverseEngineeringConfiguration
            {
                ConnectionString     = connectionString,
                ContextClassName     = programName,
                ProjectPath          = "na",
                ProjectRootNamespace = assmNamespace,
                OutputPath           = outputPath
            };
            var taskResult = generator.GenerateAsync(conf);

            taskResult.Wait();
            var resFiles = taskResult.Result;

            var output = new StringBuilder();

            output.Append(InMemoryFiles.RetrieveFileContents(outputPath, programName + ".cs"));
            foreach (var fpath in resFiles.EntityTypeFiles)
            {
                output.Append(InMemoryFiles.RetrieveFileContents(outputPath, System.IO.Path.GetFileName(fpath)).SkipLines(4));
            }
            var usings = @"using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
";

            return(usings + output.ToString());
        }
        public void E2ETest_UseAttributesInsteadOfFluentApi()
        {
            var configuration = new ReverseEngineeringConfiguration
            {
                ConnectionString     = _connectionString,
                ContextClassName     = "AttributesContext",
                ProjectPath          = TestProjectDir + Path.DirectorySeparatorChar, // tests that ending DirectorySeparatorChar does not affect namespace
                ProjectRootNamespace = TestNamespace,
                OutputPath           = TestSubDir,
                TableSelectionSet    = Filter,
            };

            var filePaths = Generator.GenerateAsync(configuration).GetAwaiter().GetResult();

            var actualFileSet = new FileSet(InMemoryFiles, Path.GetFullPath(Path.Combine(TestProjectDir, TestSubDir)))
            {
                Files = Enumerable.Repeat(filePaths.ContextFile, 1).Concat(filePaths.EntityTypeFiles).Select(Path.GetFileName).ToList()
            };

            var expectedFileSet = new FileSet(new FileSystemFileService(),
                                              Path.Combine("ReverseEngineering", "ExpectedResults", "E2E_UseAttributesInsteadOfFluentApi"),
                                              contents => contents.Replace("namespace " + TestNamespace, "namespace " + TestNamespace + "." + TestSubDir)
                                              .Replace("{{connectionString}}", _connectionString))
            {
                Files = (new List <string> {
                    "AttributesContext.expected"
                })
                        .Concat(_expectedEntityTypeFiles).ToList()
            };

            /*
             * AssertLog(new LoggerMessages
             * {
             *  Warn =
             *          {
             *              RelationalDesignStrings.CannotFindTypeMappingForColumn("dbo.AllDataTypes.geographyColumn", "geography"),
             *              RelationalDesignStrings.CannotFindTypeMappingForColumn("dbo.AllDataTypes.geometryColumn", "geometry"),
             *              RelationalDesignStrings.CannotFindTypeMappingForColumn("dbo.AllDataTypes.hierarchyidColumn", "hierarchyid"),
             *              RelationalDesignStrings.CannotFindTypeMappingForColumn("dbo.AllDataTypes.sql_variantColumn", "sql_variant"),
             *              RelationalDesignStrings.CannotFindTypeMappingForColumn("dbo.AllDataTypes.xmlColumn", "xml"),
             *              NpgsqlDesignStrings.DataTypeDoesNotAllowNpgsqlIdentityStrategy("dbo.PropertyConfiguration.PropertyConfigurationID","tinyint"),
             *              RelationalDesignStrings.CannotFindTypeMappingForColumn("dbo.TableWithUnmappablePrimaryKeyColumn.TableWithUnmappablePrimaryKeyColumnID", "hierarchyid"),
             *              RelationalDesignStrings.PrimaryKeyErrorPropertyNotFound("dbo.TableWithUnmappablePrimaryKeyColumn"),
             *              RelationalDesignStrings.UnableToGenerateEntityType("dbo.TableWithUnmappablePrimaryKeyColumn"),
             *          }
             * });
             */

            //throw new Exception(actualFileSet.Contents("Test_Spaces_Keywords_Table.cs"));
            AssertEqualFileContents(expectedFileSet, actualFileSet);
            AssertCompile(actualFileSet);
        }
        public void E2ETest_AllFluentApi()
        {
            var configuration = new ReverseEngineeringConfiguration
            {
                ConnectionString     = _connectionString,
                ProjectPath          = TestProjectDir,
                ProjectRootNamespace = TestNamespace,
                OutputPath           = null, // not used for this test
                UseFluentApiOnly     = true,
                TableSelectionSet    = Filter,
            };

            var filePaths = Generator.GenerateAsync(configuration).GetAwaiter().GetResult();

            var actualFileSet = new FileSet(InMemoryFiles, Path.GetFullPath(TestProjectDir))
            {
                Files = Enumerable.Repeat(filePaths.ContextFile, 1).Concat(filePaths.EntityTypeFiles).Select(Path.GetFileName).ToList()
            };

            var expectedFileSet = new FileSet(new FileSystemFileService(),
                                              Path.Combine("ReverseEngineering", "Expected", "AllFluentApi"),
                                              inputFile => inputFile.Replace("{{connectionString}}", _connectionString))
            {
                Files = (new List <string> {
                    "NpgsqlReverseEngineerTestE2EContext.expected"
                })
                        .Concat(_expectedEntityTypeFiles).ToList()
            };

            /*
             * AssertLog(new LoggerMessages
             * {
             *  Warn =
             *          {
             *              RelationalDesignStrings.CannotFindTypeMappingForColumn("dbo.AllDataTypes.geographyColumn", "geography"),
             *              RelationalDesignStrings.CannotFindTypeMappingForColumn("dbo.AllDataTypes.geometryColumn", "geometry"),
             *              RelationalDesignStrings.CannotFindTypeMappingForColumn("dbo.AllDataTypes.hierarchyidColumn", "hierarchyid"),
             *              RelationalDesignStrings.CannotFindTypeMappingForColumn("dbo.AllDataTypes.sql_variantColumn", "sql_variant"),
             *              RelationalDesignStrings.CannotFindTypeMappingForColumn("dbo.AllDataTypes.xmlColumn", "xml"),
             *              NpgsqlDesignStrings.DataTypeDoesNotAllowNpgsqlIdentityStrategy("dbo.PropertyConfiguration.PropertyConfigurationID","tinyint"),
             *              RelationalDesignStrings.CannotFindTypeMappingForColumn("dbo.TableWithUnmappablePrimaryKeyColumn.TableWithUnmappablePrimaryKeyColumnID", "hierarchyid"),
             *              RelationalDesignStrings.PrimaryKeyErrorPropertyNotFound("dbo.TableWithUnmappablePrimaryKeyColumn"),
             *              RelationalDesignStrings.UnableToGenerateEntityType("dbo.TableWithUnmappablePrimaryKeyColumn"),
             *          }
             * });
             */

            AssertEqualFileContents(expectedFileSet, actualFileSet);
            AssertCompile(actualFileSet);
        }
        public void Throws_exceptions_for_incorrect_configuration()
        {
            var configuration = new ReverseEngineeringConfiguration
            {
                ConnectionString = null,
                ProjectPath      = null,
                OutputPath       = null
            };

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

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

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

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

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

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

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

            configuration.OutputPath = @"A\Relative\Path";
            Assert.Equal(ToolsCoreStrings.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(RelationalDesignStrings.ConnectionStringRequired,
                Assert.Throws<ArgumentException>(
                    () => configuration.CheckValidity()).Message);

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

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

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

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

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

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

            configuration.OutputPath = @"A\Relative\Path";
            Assert.Equal(RelationalDesignStrings.RootNamespaceRequired,
                Assert.Throws<ArgumentException>(
                    () => configuration.CheckValidity()).Message);
        }
Пример #14
0
        public void PrimaryKeyWithSequence()
        {
            using (var scratch = SqlServerTestStore.Create("SqlServerE2E"))
            {
                scratch.ExecuteNonQuery(@"
CREATE SEQUENCE PrimaryKeyWithSequenceSequence
    AS int
    START WITH 1
    INCREMENT BY 1

CREATE TABLE PrimaryKeyWithSequence (
    PrimaryKeyWithSequenceId int DEFAULT(NEXT VALUE FOR PrimaryKeyWithSequenceSequence),
    PRIMARY KEY (PrimaryKeyWithSequenceId)
);
");

                var configuration = new ReverseEngineeringConfiguration
                {
                    ConnectionString     = scratch.ConnectionString,
                    ProjectPath          = TestProjectDir + Path.DirectorySeparatorChar,
                    ProjectRootNamespace = TestNamespace,
                    ContextClassName     = "PrimaryKeyWithSequenceContext",
                    UseFluentApiOnly     = true
                };
                var expectedFileSet = new FileSet(new FileSystemFileService(),
                                                  Path.Combine("ReverseEngineering", "Expected"),
                                                  contents => contents.Replace("{{connectionString}}", scratch.ConnectionString))
                {
                    Files = new List <string>
                    {
                        "PrimaryKeyWithSequenceContext.expected",
                        "PrimaryKeyWithSequence.expected"
                    }
                };

                var filePaths = Generator.GenerateAsync(configuration).GetAwaiter().GetResult();

                var actualFileSet = new FileSet(InMemoryFiles, Path.GetFullPath(TestProjectDir))
                {
                    Files = new[] { filePaths.ContextFile }.Concat(filePaths.EntityTypeFiles).Select(Path.GetFileName).ToList()
                };

                AssertEqualFileContents(expectedFileSet, actualFileSet);
                AssertCompile(actualFileSet);
            }
        }
Пример #15
0
        public async void It_uses_templates()
        {
            var dbContextFileName  = "EntityFramework.Sqlite.Design." + ReverseEngineeringGenerator.DbContextTemplateFileName;
            var entityTypeFileName = "EntityFramework.Sqlite.Design." + ReverseEngineeringGenerator.EntityTypeTemplateFileName;
            var entityTemplate     = "This is an entity type template! (For real)";
            var contextTemplate    = "Also a 100% legit template";
            var outputDir          = "gen";
            var templatesDir       = "templates";

            using (var testStore = SqliteTestStore.CreateScratch())
            {
                testStore.ExecuteNonQuery("CREATE TABLE RealMccoy ( Col1 text PRIMARY KEY); ");

                InMemoryFiles.OutputFile(templatesDir, dbContextFileName, contextTemplate);
                InMemoryFiles.OutputFile(templatesDir, entityTypeFileName, entityTemplate);

                var config = new ReverseEngineeringConfiguration
                {
                    ConnectionString   = testStore.Connection.ConnectionString,
                    Provider           = MetadataModelProvider,
                    Namespace          = "Test",
                    OutputPath         = outputDir,
                    CustomTemplatePath = templatesDir
                };
                var filePaths = await Generator.GenerateAsync(config);

                var expectedLog = new LoggerMessages
                {
                    Info =
                    {
                        "Using custom template " + Path.Combine(templatesDir, dbContextFileName),
                        "Using custom template " + Path.Combine(templatesDir, entityTypeFileName)
                    }
                };
                AssertLog(expectedLog);

                Assert.Equal(2, filePaths.Count);

                foreach (var fileName in filePaths.Select(Path.GetFileName))
                {
                    var fileContents = InMemoryFiles.RetrieveFileContents(outputDir, fileName);
                    var contents     = fileName.EndsWith("Context.cs") ? contextTemplate : entityTemplate;
                    Assert.Equal(contents, fileContents);
                }
            }
        }
        public void E2ETest_UseAttributesInsteadOfFluentApi()
        {
            var configuration = new ReverseEngineeringConfiguration
            {
                ConnectionString     = _connectionString,
                ContextClassName     = "AttributesContext",
                ProjectPath          = TestProjectDir,
                ProjectRootNamespace = TestNamespace,
                OutputPath           = TestSubDir,
                TableSelectionSet    = Filter,
            };

            var filePaths = Generator.GenerateAsync(configuration).GetAwaiter().GetResult();

            var actualFileSet = new FileSet(InMemoryFiles, Path.Combine(TestProjectDir, TestSubDir))
            {
                Files = Enumerable.Repeat(filePaths.ContextFile, 1).Concat(filePaths.EntityTypeFiles).Select(Path.GetFileName).ToList()
            };

            var expectedFileSet = new FileSet(new FileSystemFileService(),
                                              Path.Combine("ReverseEngineering", "ExpectedResults", "E2E_UseAttributesInsteadOfFluentApi"),
                                              contents => contents.Replace("namespace " + TestNamespace, "namespace " + TestNamespace + "." + TestSubDir))
            {
                Files = (new List <string> {
                    "AttributesContext.expected"
                })
                        .Concat(_expectedEntityTypeFiles).ToList()
            };

            AssertLog(new LoggerMessages
            {
                Warn =
                {
                    @"For column [dbo][AllDataTypes][hierarchyidColumn]. Could not find type mapping for SQL Server type hierarchyid. Skipping column.",
                    @"For column [dbo][AllDataTypes][sql_variantColumn]. Could not find type mapping for SQL Server type sql_variant. Skipping column.",
                    @"For column [dbo][AllDataTypes][xmlColumn]. Could not find type mapping for SQL Server type xml. Skipping column.",
                    @"For column [dbo][AllDataTypes][geographyColumn]. Could not find type mapping for SQL Server type geography. Skipping column.",
                    @"For column [dbo][AllDataTypes][geometryColumn]. Could not find type mapping for SQL Server type geometry. Skipping column.",
                    @"For column [dbo][PropertyConfiguration][PropertyConfigurationID]. This column is set up as an Identity column, but the SQL Server data type is tinyint. This will be mapped to CLR type byte which does not allow the SqlServerValueGenerationStrategy.IdentityColumn setting. Generating a matching Property but ignoring the Identity setting.",
                    @"For column [dbo][TableWithUnmappablePrimaryKeyColumn][TableWithUnmappablePrimaryKeyColumnID]. Could not find type mapping for SQL Server type hierarchyid. Skipping column.",
                    @"Unable to identify any primary key columns in the underlying SQL Server table [dbo].[TableWithUnmappablePrimaryKeyColumn]."
                }
            });
            AssertEqualFileContents(expectedFileSet, actualFileSet);
            AssertCompile(actualFileSet);
        }
Пример #17
0
        public void Index_with_filter()
        {
            using (var scratch = SqlServerTestStore.Create("SqlServerE2E"))
            {
                scratch.ExecuteNonQuery(@"
CREATE TABLE FilteredIndex (
    Id int,
    Number int,
    PRIMARY KEY (Id)
);

CREATE INDEX Unicorn_Filtered_Index
    ON FilteredIndex (Number) WHERE Number > 10
");

                var configuration = new ReverseEngineeringConfiguration
                {
                    ConnectionString     = scratch.ConnectionString,
                    ProjectPath          = TestProjectDir + Path.DirectorySeparatorChar,
                    ProjectRootNamespace = TestNamespace,
                    ContextClassName     = "FilteredIndexContext",
                    UseFluentApiOnly     = true
                };
                var expectedFileSet = new FileSet(new FileSystemFileService(),
                                                  Path.Combine("ReverseEngineering", "Expected"),
                                                  contents => contents.Replace("{{connectionString}}", scratch.ConnectionString))
                {
                    Files = new List <string>
                    {
                        "FilteredIndexContext.expected",
                        "FilteredIndex.expected",
                    }
                };

                var filePaths = Generator.GenerateAsync(configuration).GetAwaiter().GetResult();

                var actualFileSet = new FileSet(InMemoryFiles, Path.GetFullPath(TestProjectDir))
                {
                    Files = new[] { filePaths.ContextFile }.Concat(filePaths.EntityTypeFiles).Select(Path.GetFileName).ToList()
                };

                AssertEqualFileContents(expectedFileSet, actualFileSet);
                AssertCompile(actualFileSet);
            }
        }
        public void Code_generation_will_use_customized_templates_if_present()
        {
            var configuration = new ReverseEngineeringConfiguration
            {
                Provider           = MetadataModelProvider,
                ConnectionString   = _connectionString,
                Namespace          = TestNamespace,
                CustomTemplatePath = CustomizedTemplateDir,
                OutputPath         = TestOutputDir
            };

            InMemoryFiles.OutputFile(CustomizedTemplateDir, ProviderDbContextTemplateName, "DbContext template");
            InMemoryFiles.OutputFile(CustomizedTemplateDir, ProviderEntityTypeTemplateName, "EntityType template");

            var filePaths = Generator.GenerateAsync(configuration).GetAwaiter().GetResult();

            AssertLog(new LoggerMessages
            {
                Warn =
                {
                    @"For column [dbo][AllDataTypes][hierarchyidColumn]. Could not find type mapping for SQL Server type hierarchyid. Skipping column.",
                    @"For column [dbo][AllDataTypes][sql_variantColumn]. Could not find type mapping for SQL Server type sql_variant. Skipping column.",
                    @"For column [dbo][AllDataTypes][xmlColumn]. Could not find type mapping for SQL Server type xml. Skipping column.",
                    @"For column [dbo][AllDataTypes][geographyColumn]. Could not find type mapping for SQL Server type geography. Skipping column.",
                    @"For column [dbo][AllDataTypes][geometryColumn]. Could not find type mapping for SQL Server type geometry. Skipping column.",
                    @"For column [dbo][PropertyConfiguration][PropertyConfigurationID]. This column is set up as an Identity column, but the SQL Server data type is tinyint. This will be mapped to CLR type byte which does not allow the SqlServerIdentityStrategy.IdentityColumn setting. Generating a matching Property but ignoring the Identity setting.",
                    @"For column [dbo][TableWithUnmappablePrimaryKeyColumn][TableWithUnmappablePrimaryKeyColumnID]. Could not find type mapping for SQL Server type hierarchyid. Skipping column.",
                    @"Unable to identify any primary key columns in the underlying SQL Server table [dbo].[TableWithUnmappablePrimaryKeyColumn]."
                },
                Info =
                {
                    "Using custom template " + Path.Combine(CustomizedTemplateDir, ProviderDbContextTemplateName),
                    "Using custom template " + Path.Combine(CustomizedTemplateDir, ProviderEntityTypeTemplateName)
                }
            });

            foreach (var fileName in filePaths.Select(Path.GetFileName))
            {
                var fileContents = InMemoryFiles.RetrieveFileContents(TestOutputDir, fileName);
                var contents     = "SqlServerReverseEngineerTestE2EContext.cs" == fileName ? "DbContext template" : "EntityType template";
                Assert.Contains(fileName.Replace(".cs", ".expected"), _expectedFiles);
                Assert.Equal(contents, fileContents);
            }
        }
        public void Sequences()
        {
            using (var scratch = NpgsqlTestStore.CreateScratch())
            {
                scratch.ExecuteNonQuery(@"
CREATE SEQUENCE ""CountByTwo""
    START WITH 1
    INCREMENT BY 2;

CREATE SEQUENCE ""CyclicalCountByThree""
    START WITH 6
    INCREMENT BY 3
    MAXVALUE 27
    MINVALUE 0
    CYCLE;");

                var configuration = new ReverseEngineeringConfiguration
                {
                    ConnectionString     = scratch.ConnectionString,
                    ProjectPath          = TestProjectDir + Path.DirectorySeparatorChar,
                    ProjectRootNamespace = TestNamespace,
                    ContextClassName     = "SequenceContext",
                };
                var expectedFileSet = new FileSet(new FileSystemFileService(),
                                                  Path.Combine("ReverseEngineering", "Expected"),
                                                  contents => contents.Replace("{{connectionString}}", scratch.ConnectionString))
                {
                    Files = new List <string> {
                        "SequenceContext.expected"
                    }
                };

                var filePaths = Generator.GenerateAsync(configuration).GetAwaiter().GetResult();

                var actualFileSet = new FileSet(InMemoryFiles, Path.GetFullPath(TestProjectDir))
                {
                    Files = new[] { filePaths.ContextFile }.Concat(filePaths.EntityTypeFiles).Select(Path.GetFileName).ToList()
                };

                AssertEqualFileContents(expectedFileSet, actualFileSet);
                AssertCompile(actualFileSet);
            }
        }
        public void E2ETest()
        {
            var configuration = new ReverseEngineeringConfiguration
            {
                Provider           = MetadataModelProvider,
                ConnectionString   = _connectionString,
                Namespace          = TestNamespace,
                CustomTemplatePath = null,     // not used for this test
                OutputPath         = TestOutputDir
            };

            var filePaths = Generator.GenerateAsync(configuration).GetAwaiter().GetResult();

            var actualFileSet = new FileSet(InMemoryFiles, TestOutputDir)
            {
                Files = filePaths.Select(Path.GetFileName).ToList()
            };

            var expectedFileSet = new FileSet(new FileSystemFileService(), Path.Combine("ReverseEngineering", "ExpectedResults", "E2E"))
            {
                Files = _expectedFiles
            };

            AssertLog(new LoggerMessages
            {
                Warn =
                {
                    @"For column [dbo][AllDataTypes][hierarchyidColumn]. Could not find type mapping for SQL Server type hierarchyid. Skipping column.",
                    @"For column [dbo][AllDataTypes][sql_variantColumn]. Could not find type mapping for SQL Server type sql_variant. Skipping column.",
                    @"For column [dbo][AllDataTypes][xmlColumn]. Could not find type mapping for SQL Server type xml. Skipping column.",
                    @"For column [dbo][AllDataTypes][geographyColumn]. Could not find type mapping for SQL Server type geography. Skipping column.",
                    @"For column [dbo][AllDataTypes][geometryColumn]. Could not find type mapping for SQL Server type geometry. Skipping column.",
                    @"For column [dbo][PropertyConfiguration][PropertyConfigurationID]. This column is set up as an Identity column, but the SQL Server data type is tinyint. This will be mapped to CLR type byte which does not allow the SqlServerIdentityStrategy.IdentityColumn setting. Generating a matching Property but ignoring the Identity setting.",
                    @"For column [dbo][TableWithUnmappablePrimaryKeyColumn][TableWithUnmappablePrimaryKeyColumnID]. Could not find type mapping for SQL Server type hierarchyid. Skipping column.",
                    @"Unable to identify any primary key columns in the underlying SQL Server table [dbo].[TableWithUnmappablePrimaryKeyColumn]."
                }
            });
            AssertEqualFileContents(expectedFileSet, actualFileSet);
            AssertCompile(actualFileSet);
        }
Пример #21
0
        static void Main(string[] args)
        {
            // Add base services for scaffolding
            var serviceCollection = new ServiceCollection()
                                    .AddScaffolding()
                                    .AddLogging();

            // Add database provider services
            var provider = new SqlServerDesignTimeServices();

            provider.ConfigureDesignTimeServices(serviceCollection);

            var serviceProvider = serviceCollection.BuildServiceProvider();

            var generator = serviceProvider.GetService <ReverseEngineeringGenerator>();
            var options   = new ReverseEngineeringConfiguration
            {
                ConnectionString     = @"Data Source=WIN-DTLAIS5TR8U\LOCALHOST;Integrated Security=True;Initial Catalog=SharedLibrary;MultipleActiveResultSets=True;App=EntityFramework",
                ProjectPath          = @"C:\temp\",
                ProjectRootNamespace = "My.Namespace"
            };

            generator.GenerateAsync(options).Wait();
        }
Пример #22
0
        public void Sequences()
        {
            using (var scratch = SqlServerTestStore.Create("SqlServerE2E"))
            {
                scratch.ExecuteNonQuery(@"
CREATE SEQUENCE CountByTwo
    START WITH 1
    INCREMENT BY 2;

CREATE SEQUENCE CyclicalCountByThree
    START WITH 6
    INCREMENT BY 3
    MAXVALUE 27
    MINVALUE 0
    CYCLE;

CREATE SEQUENCE TinyIntSequence
    AS tinyint
    START WITH 1;

CREATE SEQUENCE SmallIntSequence
    AS smallint
    START WITH 1;

CREATE SEQUENCE IntSequence
    AS int
    START WITH 1;

CREATE SEQUENCE DecimalSequence
    AS decimal;

CREATE SEQUENCE NumericSequence
    AS numeric;");

                var configuration = new ReverseEngineeringConfiguration
                {
                    ConnectionString     = scratch.ConnectionString,
                    ProjectPath          = TestProjectDir + Path.DirectorySeparatorChar,
                    ProjectRootNamespace = TestNamespace,
                    ContextClassName     = "SequenceContext"
                };
                var expectedFileSet = new FileSet(new FileSystemFileService(),
                                                  Path.Combine("ReverseEngineering", "Expected"),
                                                  contents => contents.Replace("{{connectionString}}", scratch.ConnectionString))
                {
                    Files = new List <string> {
                        "SequenceContext.expected"
                    }
                };

                var filePaths = Generator.GenerateAsync(configuration).GetAwaiter().GetResult();

                var actualFileSet = new FileSet(InMemoryFiles, Path.GetFullPath(TestProjectDir))
                {
                    Files = new[] { filePaths.ContextFile }.Concat(filePaths.EntityTypeFiles).Select(Path.GetFileName).ToList()
                };

                AssertLog(new LoggerMessages
                {
                    Warn =
                    {
                        RelationalDesignStrings.BadSequenceType("DecimalSequence", "decimal"),
                        RelationalDesignStrings.BadSequenceType("NumericSequence", "numeric")
                    }
                });

                AssertEqualFileContents(expectedFileSet, actualFileSet);
                AssertCompile(actualFileSet);
            }
        }
Пример #23
0
        public void E2ETest()
        {
            SetCurrentCulture();

            var serviceCollection = SetupInitialServices();
            var logger            = new InMemoryCommandLogger("E2ETest");

            serviceCollection.AddScoped(typeof(ILogger), sp => logger);
            var fileService = new InMemoryFileService();

            serviceCollection.AddScoped(typeof(IFileService), sp => fileService);

            var provider = GetMetadataModelProvider(serviceCollection);

            var configuration = new ReverseEngineeringConfiguration
            {
                Provider           = provider,
                ConnectionString   = E2EConnectionString,
                Namespace          = TestNamespace,
                CustomTemplatePath = null, // not used for this test
                OutputPath         = TestOutputDir
            };

            var expectedFileContents = InitializeE2EExpectedFileContents();

            var serviceProvider = serviceCollection.BuildServiceProvider();
            var generator       = serviceProvider.GetRequiredService <ReverseEngineeringGenerator>();
            var filePaths       = generator.GenerateAsync(configuration).Result;

            Assert.Equal(_E2ETestExpectedWarnings.Count, logger.WarningMessages.Count);
            // loop over warnings instead of using the collection form of Assert.Equal()
            // to give better error messages if it does fail. Similarly for file paths below.
            var i = 0;

            foreach (var expectedWarning in _E2ETestExpectedWarnings)
            {
                Assert.Equal(expectedWarning, logger.WarningMessages[i++]);
            }
            Assert.Equal(0, logger.InformationMessages.Count);
            Assert.Equal(0, logger.VerboseMessages.Count);

            var expectedFilePaths = _E2ETestExpectedFileNames.Select(name => Path.Combine(TestOutputDir, name));

            Assert.Equal(expectedFilePaths.Count(), filePaths.Count);
            i = 0;
            foreach (var expectedFilePath in expectedFilePaths)
            {
                Assert.Equal(expectedFilePath, filePaths[i++]);
            }

            var listOfFileContents = new List <string>();

            foreach (var fileName in _E2ETestExpectedFileNames)
            {
                var fileContents = fileService.RetrieveFileContents(TestOutputDir, fileName);
                Assert.Equal(expectedFileContents[fileName], fileContents);
                listOfFileContents.Add(fileContents);
            }

            // compile generated code
            var metadataReferencesProvider =
                (MetadataReferencesProvider)serviceProvider.GetService(typeof(MetadataReferencesProvider));
            var metadataReferences       = SetupMetadataReferencesForCompilationOfGeneratedCode(metadataReferencesProvider);
            var roslynCompilationService = new RoslynCompilationService();
            var compilationResult        =
                roslynCompilationService.Compile(listOfFileContents, metadataReferences);

            if (compilationResult.Messages.Any())
            {
                _output.WriteLine("Compilation Errors from compiling generated code");
                _output.WriteLine("================================================");
                foreach (var message in compilationResult.Messages)
                {
                    _output.WriteLine(message);
                }
                _output.WriteLine("================================================");
                Assert.Equal(string.Empty, "See Compilation Errors in Output.");
            }
        }
Пример #24
0
        public async Task <SchemaResult> GetSchemaSource(string connectionString, string assemblyNamespace, bool withUsings = true)
        {
            var programName = "Ctx";
            // append guid to create unique output location
            var outputPath = System.IO.Path.Combine(_tempFolder, Guid.NewGuid().ToIdentifierWithPrefix("folder"));
            var conf       = new ReverseEngineeringConfiguration
            {
                ConnectionString     = connectionString,
                ContextClassName     = programName,
                ProjectPath          = "na",
                ProjectRootNamespace = assemblyNamespace,
                OutputPath           = outputPath
            };
            ReverseEngineerFiles resFiles = null;

            try
            {
                // todo there's a race condition in here when fx both template and execute are called at once,
                // which seems to trigger something in EF, instead of this lock, do some sync in SchemaSource.
                lock (_lock) {
                    var resFilesTask = Generator.GenerateAsync(conf);
                    resFilesTask.Wait();
                    resFiles = resFilesTask.Result;
                }
            }
            catch (Exception exn) when(exn.ExpectedError())
            {
                Logger.Debug("Known error {0}", exn.Message);
                return(new SchemaResult {
                    Code = exn.StatusCode(), Message = exn.Message
                });
            }
            catch (Exception exn) {
                Logger.Debug("Uknown error {0} (HResult: {1})", exn.Message, exn.HResult);
                throw exn;
            }
            var output = new StringBuilder();
            var dbCtx  = CreateContext(InMemoryFiles.RetrieveFileContents(outputPath, programName + ".cs"), isLibrary: withUsings);
            var ctx    = dbCtx.Item1;

            if (!withUsings)
            {
                ctx = ctx.SkipLines(3);
            }
            else
            {
                output.Append(_refs);
            }
            // remove the entity generated warning about injected connection strings
            ctx = Regex.Replace(ctx, @"#warning.*", "");
            output.Append(ctx);
            Logger.Info("ContextFile.Count {0}", resFiles.ContextFile.Count());
            foreach (var fpath in resFiles.EntityTypeFiles)
            {
                output.Append(InMemoryFiles.RetrieveFileContents(outputPath, System.IO.Path.GetFileName(fpath)).SkipLines(4));
            }

            return(new SchemaResult
            {
                Schema = output.ToString(),
                DefaultTable = dbCtx.Item2
            });
        }
Пример #25
0
        public void Code_generation_will_use_customized_templates_if_present()
        {
            SetCurrentCulture();

            var serviceCollection = SetupInitialServices();
            var logger            = new InMemoryCommandLogger("E2ETest");

            serviceCollection.AddScoped(typeof(ILogger), sp => logger);
            var fileService = new InMemoryFileService();

            serviceCollection.AddScoped(typeof(IFileService), sp => fileService);
            InitializeCustomizedTemplates(fileService);

            var provider = GetMetadataModelProvider(serviceCollection);

            var configuration = new ReverseEngineeringConfiguration
            {
                Provider           = provider,
                ConnectionString   = E2EConnectionString,
                Namespace          = TestNamespace,
                CustomTemplatePath = CustomizedTemplateDir,
                OutputPath         = TestOutputDir
            };

            var serviceProvider = serviceCollection.BuildServiceProvider();
            var generator       = serviceProvider.GetRequiredService <ReverseEngineeringGenerator>();
            var filePaths       = generator.GenerateAsync(configuration).Result;

            Assert.Equal(_E2ETestExpectedWarnings.Count, logger.WarningMessages.Count);
            // loop over warnings instead of using the collection form of Assert.Equal()
            // to give better error messages if it does fail. Similarly for file paths below.
            var i = 0;

            foreach (var expectedWarning in _E2ETestExpectedWarnings)
            {
                Assert.Equal(expectedWarning, logger.WarningMessages[i++]);
            }

            Assert.Equal(_CustomizedTemplatesTestExpectedInfos.Count, logger.InformationMessages.Count);
            i = 0;
            foreach (var expectedInfo in _CustomizedTemplatesTestExpectedInfos)
            {
                Assert.Equal(expectedInfo, logger.InformationMessages[i++]);
            }

            Assert.Equal(0, logger.VerboseMessages.Count);

            var expectedFilePaths = _E2ETestExpectedFileNames.Select(name => Path.Combine(TestOutputDir, name));

            Assert.Equal(expectedFilePaths.Count(), filePaths.Count);
            i = 0;
            foreach (var expectedFilePath in expectedFilePaths)
            {
                Assert.Equal(expectedFilePath, filePaths[i++]);
            }

            var listOfFileContents = new List <string>();

            foreach (var fileName in _E2ETestExpectedFileNames)
            {
                var fileContents = fileService.RetrieveFileContents(TestOutputDir, fileName);
                if ("SqlServerReverseEngineerTestE2EContext.cs" == fileName)
                {
                    Assert.Equal(CustomDbContextTemplateContents, fileContents);
                }
                else
                {
                    Assert.Equal(CustomEntityTypeTemplateContents, fileContents);
                }
            }
        }
        public void ColumnsWithSequences()
        {
            using (var scratch = NpgsqlTestStore.CreateScratch())
            {
                scratch.ExecuteNonQuery(@"
DROP TABLE IF EXISTS ""IdSerialSequence"";
CREATE TABLE ""IdSerialSequence"" (
  ""Id"" SERIAL PRIMARY KEY
);

DROP TABLE IF EXISTS ""IdNonSerialSequence"";
DROP SEQUENCE IF EXISTS ""IdSomeSequence"";
CREATE SEQUENCE ""IdSomeSequence"";
CREATE TABLE ""IdNonSerialSequence"" (
  ""Id"" INTEGER PRIMARY KEY DEFAULT nextval('""IdSomeSequence""')
);

DROP TABLE IF EXISTS ""SerialSequence"";
CREATE TABLE ""SerialSequence"" (
  ""Id"" INTEGER PRIMARY KEY,
  ""SomeField"" SERIAL
);

DROP TABLE IF EXISTS ""NonSerialSequence"";
DROP SEQUENCE IF EXISTS ""SomeSequence"";
CREATE SEQUENCE ""SomeSequence"";
CREATE TABLE ""NonSerialSequence"" (
  ""Id"" INTEGER PRIMARY KEY,
  ""SomeField"" INTEGER DEFAULT nextval('""SomeSequence""')
);");

                var configuration = new ReverseEngineeringConfiguration
                {
                    ConnectionString     = scratch.ConnectionString,
                    ProjectPath          = TestProjectDir + Path.DirectorySeparatorChar,
                    ProjectRootNamespace = TestNamespace,
                    ContextClassName     = "ColumnsWithSequencesContext",
                };
                var expectedFileSet = new FileSet(new FileSystemFileService(),
                                                  Path.Combine("ReverseEngineering", "Expected", "ColumnsWithSequences"),
                                                  contents => contents.Replace("{{connectionString}}", scratch.ConnectionString))
                {
                    Files = new List <string>
                    {
                        "ColumnsWithSequencesContext.expected",
                        "IdNonSerialSequence.expected",
                        "IdSerialSequence.expected",
                        "NonSerialSequence.expected",
                        "SerialSequence.expected"
                    }
                };

                var filePaths = Generator.GenerateAsync(configuration).GetAwaiter().GetResult();

                var actualFileSet = new FileSet(InMemoryFiles, Path.GetFullPath(TestProjectDir))
                {
                    Files = new[] { filePaths.ContextFile }.Concat(filePaths.EntityTypeFiles).Select(Path.GetFileName).ToList()
                };

                foreach (var f in actualFileSet.Files)
                {
                    File.WriteAllText($@"c:\temp\actual\{f}", actualFileSet.Contents(f));
                }
                AssertEqualFileContents(expectedFileSet, actualFileSet);
                AssertCompile(actualFileSet);
            }
        }
Пример #27
0
        public string GetSchemaSource(string connectionString, string assemblyNamespace, bool withUsings = true)
        {
            var loggerFactory = new LoggerFactory().AddConsole();

            var ssTypeMap     = new Microsoft.EntityFrameworkCore.Storage.Internal.SqlServerTypeMapper();
            var ssDbFac       = new SqlServerDatabaseModelFactory(loggerFactory: loggerFactory);
            var ssScaffoldFac = new SqlServerScaffoldingModelFactory(
                loggerFactory: loggerFactory,
                typeMapper: ssTypeMap,
                databaseModelFactory: ssDbFac
                );

            var ssAnnotationProvider = new Microsoft.EntityFrameworkCore.Metadata.SqlServerAnnotationProvider();
            var csUtils    = new CSharpUtilities();
            var scaffUtils = new ScaffoldingUtilities();

            var confFac = new ConfigurationFactory(
                extensionsProvider: ssAnnotationProvider,
                cSharpUtilities: csUtils,
                scaffoldingUtilities: scaffUtils
                );
            var fs = new InMemoryFileService();
            var sb = new StringBuilderCodeWriter(
                fileService: fs,
                dbContextWriter: new DbContextWriter(
                    scaffoldingUtilities: scaffUtils,
                    cSharpUtilities: csUtils
                    ),
                entityTypeWriter: new EntityTypeWriter(cSharpUtilities: csUtils, scaffoldingUtilities: scaffUtils)
                );

            var rGen = new ReverseEngineeringGenerator(
                loggerFactory: loggerFactory,
                scaffoldingModelFactory: ssScaffoldFac,
                configurationFactory: confFac,
                codeWriter: sb
                );

            var outputPath  = @"C:\temp";
            var programName = "Ctx";
            var conf        = new ReverseEngineeringConfiguration
            {
                ConnectionString     = connectionString,
                ContextClassName     = programName,
                ProjectPath          = "na",
                ProjectRootNamespace = assemblyNamespace,
                OutputPath           = outputPath
            };

            var output   = new StringBuilder();
            var resFiles = rGen.GenerateAsync(conf);

            resFiles.Wait();

            var ctx = CreateContext(fs.RetrieveFileContents(outputPath, programName + ".cs"), isLibrary: withUsings);

            Console.WriteLine("CreateContext", ctx);
            if (!withUsings)
            {
                ctx = StripHeaderLines(3, ctx);
            }
            else
            {
                output.Append(_refs);
            }
            output.Append(ctx);
            foreach (var fpath in resFiles.Result.EntityTypeFiles)
            {
                output.Append(StripHeaderLines(4, fs.RetrieveFileContents(outputPath, System.IO.Path.GetFileName(fpath))));
            }
            return(output.ToString());
        }
Пример #28
0
        public EfCoreReverseEngineerResult GenerateFiles(ReverseEngineerOptions reverseEngineerOptions)
        {
            // Add base services for scaffolding
            var serviceCollection = new ServiceCollection()
                                    .AddScaffolding()
                                    .AddLogging();

            // Add database provider services
            switch (reverseEngineerOptions.DatabaseType)
            {
            case DatabaseType.SQLCE35:
                throw new NotImplementedException();

            case DatabaseType.SQLCE40:
                var sqlCeProvider = new SqlCeDesignTimeServices();
                sqlCeProvider.ConfigureDesignTimeServices(serviceCollection);
                break;

            case DatabaseType.SQLServer:
                var provider = new SqlServerDesignTimeServices();
                provider.ConfigureDesignTimeServices(serviceCollection);
                break;

            case DatabaseType.SQLite:
                var sqliteProvider = new SqliteDesignTimeServices();
                sqliteProvider.ConfigureDesignTimeServices(serviceCollection);
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            var serviceProvider = serviceCollection.BuildServiceProvider();
            var generator       = serviceProvider.GetService <ReverseEngineeringGenerator>();

            var tableSelectionSet = reverseEngineerOptions.Tables.Count == 0
                ? TableSelectionSet.All
                : new TableSelectionSet(reverseEngineerOptions.Tables);

            var options = new ReverseEngineeringConfiguration
            {
                ConnectionString     = reverseEngineerOptions.ConnectionString,
                ProjectPath          = reverseEngineerOptions.ProjectPath,
                OutputPath           = reverseEngineerOptions.OutputPath,
                ProjectRootNamespace = reverseEngineerOptions.ProjectRootNamespace,
                OverwriteFiles       = true,
                UseFluentApiOnly     = reverseEngineerOptions.UseFluentApiOnly,
                ContextClassName     = reverseEngineerOptions.ContextClassName,
                TableSelectionSet    = tableSelectionSet
            };

            var model = generator.GetMetadataModel(options);

            var filePaths = generator.GenerateAsync(options).GetAwaiter().GetResult();

            var errors = model.Scaffolding().EntityTypeErrors;

            var result = new EfCoreReverseEngineerResult
            {
                EntityErrors = errors,
                FilePaths    = filePaths.EntityTypeFiles,
            };

            result.FilePaths.Add(filePaths.ContextFile);

            return(result);
        }