Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RevEng: Make some APIs public (non-Internal) #10517

Merged
merged 1 commit into from
Dec 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ namespace Microsoft.EntityFrameworkCore.Scaffolding.Internal
/// </summary>
public class OracleCodeGenerator : ProviderCodeGenerator
{
public OracleCodeGenerator(ProviderCodeGeneratorDependencies dependencies)
: base(dependencies)
{
}

public override string UseProviderMethod
=> nameof(OracleDbContextOptionsExtensions.UseOracle);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class OracleCodeGeneratorTest
[Fact]
public virtual void Use_provider_method_is_generated_correctly()
{
var codeGenerator = new OracleCodeGenerator();
var codeGenerator = new OracleCodeGenerator(new ProviderCodeGeneratorDependencies());

Assert.Equal("UseOracle", codeGenerator.UseProviderMethod);
}
Expand Down
98 changes: 98 additions & 0 deletions src/EFCore.Design/Design/DesignTimeServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Diagnostics;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Design;
using Microsoft.EntityFrameworkCore.Migrations.Internal;
using Microsoft.EntityFrameworkCore.Scaffolding;
using Microsoft.EntityFrameworkCore.Scaffolding.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Microsoft.EntityFrameworkCore.Design
{
/// <summary>
/// Extension methods for adding Entity Framework Core design-time services to an <see cref="IServiceCollection" />.
/// </summary>
public static class DesignTimeServiceCollectionExtensions
{
/// <summary>
/// Adds the Entity Framework Core design-time services.
/// </summary>
/// <param name="services"> The <see cref="IServiceCollection" /> the services will be added to. </param>
/// <returns> The <paramref name="services" />. This enables chaining additional method calls. </returns>
public static IServiceCollection AddEntityFrameworkDesignTimeServices([NotNull] this IServiceCollection services)
=> services.AddEntityFrameworkDesignTimeServices(new OperationReporter(handler: null));

/// <summary>
/// Adds the Entity Framework Core design-time services.
/// </summary>
/// <param name="services"> The <see cref="IServiceCollection" /> the services will be added to. </param>
/// <param name="reporter"> Used to report design-time messages. </param>
/// <returns> The <paramref name="services" />. This enables chaining additional method calls. </returns>
public static IServiceCollection AddEntityFrameworkDesignTimeServices(
[NotNull] this IServiceCollection services,
[NotNull] IOperationReporter reporter)
=> services
.AddSingleton<AnnotationCodeGeneratorDependencies>()
.AddSingleton<CoreTypeMapperDependencies>()
.AddSingleton<CSharpMigrationOperationGeneratorDependencies>()
.AddSingleton<CSharpMigrationsGeneratorDependencies>()
.AddSingleton<CSharpSnapshotGeneratorDependencies>()
.AddSingleton<MigrationsCodeGeneratorDependencies>()
.AddSingleton<ModelCodeGeneratorDependencies>()
.AddSingleton<ProviderCodeGeneratorDependencies>()
.AddSingleton<RelationalTypeMapperDependencies>()
.AddSingleton<ICandidateNamingService, CandidateNamingService>()
.AddSingleton<ICSharpDbContextGenerator, CSharpDbContextGenerator>()
.AddSingleton<ICSharpEntityTypeGenerator, CSharpEntityTypeGenerator>()
.AddSingleton<ICSharpHelper, CSharpHelper>()
.AddSingleton<ICSharpMigrationOperationGenerator, CSharpMigrationOperationGenerator>()
.AddSingleton<ICSharpSnapshotGenerator, CSharpSnapshotGenerator>()
.AddSingleton<ICSharpUtilities, CSharpUtilities>()
.AddSingleton(typeof(IDiagnosticsLogger<>), typeof(DiagnosticsLogger<>))
.AddSingleton<DiagnosticSource>(new DiagnosticListener(DbLoggerCategory.Name))
.AddSingleton<ILoggingOptions, LoggingOptions>()
.AddSingleton<IMigrationsCodeGenerator, CSharpMigrationsGenerator>()
.AddSingleton<IMigrationsCodeGeneratorSelector, MigrationsCodeGeneratorSelector>()
.AddSingleton<IModelCodeGenerator, CSharpModelGenerator>()
.AddSingleton<IModelCodeGeneratorSelector, ModelCodeGeneratorSelector>()
.AddSingleton(reporter)
.AddSingleton<IPluralizer, NullPluralizer>()
.AddSingleton<IReverseEngineerScaffolder, ReverseEngineerScaffolder>()
.AddSingleton<IScaffoldingModelFactory, RelationalScaffoldingModelFactory>()
.AddSingleton<IScaffoldingTypeMapper, ScaffoldingTypeMapper>()
.AddTransient<MigrationsScaffolderDependencies>()
.AddTransient<IMigrationsScaffolder, MigrationsScaffolder>()
.AddTransient<ISnapshotModelProcessor, SnapshotModelProcessor>()
.AddLogging(b => b.SetMinimumLevel(LogLevel.Debug).AddProvider(new OperationLoggerProvider(reporter)));

/// <summary>
/// Adds services from the <see cref="DbContext" /> which are used at design time.
/// </summary>
/// <param name="services"> The <see cref="IServiceCollection" /> the services will be added to. </param>
/// <param name="context"> The <see cref="DbContext" /> the services will be added from. </param>
/// <returns> The <paramref name="services" />. This enables chaining additional method calls. </returns>
public static IServiceCollection AddDbContextDesignTimeServices(
[NotNull] this IServiceCollection services,
[NotNull] DbContext context)
=> services
.AddTransient(_ => context.GetService<ICurrentDbContext>())
.AddTransient(_ => context.GetService<IDatabaseProvider>())
.AddTransient(_ => context.GetService<IDbContextOptions>())
.AddTransient(_ => context.GetService<IHistoryRepository>())
.AddTransient(_ => context.GetService<IMigrationsAssembly>())
.AddTransient(_ => context.GetService<IMigrationsIdGenerator>())
.AddTransient(_ => context.GetService<IMigrationsModelDiffer>())
.AddTransient(_ => context.GetService<IMigrator>())
.AddTransient(_ => context.GetService<IModel>());
}
}
51 changes: 43 additions & 8 deletions src/EFCore.Design/Design/Internal/DatabaseOperations.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Scaffolding.Internal;
using Microsoft.EntityFrameworkCore.Scaffolding;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;

Expand Down Expand Up @@ -51,7 +53,7 @@ public DatabaseOperations(
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual ReverseEngineerFiles ScaffoldContext(
public virtual ModelFiles ScaffoldContext(
[NotNull] string provider,
[NotNull] string connectionString,
[CanBeNull] string outputDir,
Expand All @@ -69,25 +71,58 @@ public virtual ReverseEngineerFiles ScaffoldContext(

var services = _servicesBuilder.Build(provider);

var generator = services.GetRequiredService<IReverseEngineerScaffolder>();
var scaffolder = services.GetRequiredService<IReverseEngineerScaffolder>();

var scaffoldedModel = generator.Generate(
var @namespace = _rootNamespace;

var subNamespace = SubnamespaceFromOutputPath(_projectDir, outputDir);
if (!string.IsNullOrEmpty(subNamespace))
{
@namespace += "." + subNamespace;
}

var scaffoldedModel = scaffolder.ScaffoldModel(
connectionString,
tables,
schemas,
_projectDir,
outputDir,
_rootNamespace,
@namespace,
_language,
dbContextClassName,
useDataAnnotations,
useDatabaseNames);

return generator.Save(
return scaffolder.Save(
scaffoldedModel,
_projectDir,
outputDir,
overwriteFiles);
}

// if outputDir is a subfolder of projectDir, then use each subfolder as a subnamespace
// --output-dir $(projectFolder)/A/B/C
// => "namespace $(rootnamespace).A.B.C"
private string SubnamespaceFromOutputPath(string projectDir, string outputDir)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these args converted to full path specification rather than relative paths?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to do a bit verification in this area before merging.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

projectDir is. outputDir is as it was entered by the user

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will normalize outputDir here.

{
if (outputDir == null)
{
return null;
}

if (!Path.IsPathRooted(outputDir))
{
outputDir = Path.GetFullPath(Path.Combine(projectDir, outputDir));
}

if (!outputDir.StartsWith(projectDir, StringComparison.Ordinal))
{
return null;
}

var subPath = outputDir.Substring(projectDir.Length);

return !string.IsNullOrWhiteSpace(subPath)
? string.Join(".", subPath.Split(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries))
: null;
}
}
}
66 changes: 12 additions & 54 deletions src/EFCore.Design/Design/Internal/DesignTimeServicesBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Design;
using Microsoft.EntityFrameworkCore.Migrations.Internal;
using Microsoft.EntityFrameworkCore.Scaffolding.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -46,13 +41,10 @@ public virtual IServiceProvider Build([NotNull] DbContext context)
{
Check.NotNull(context, nameof(context));

var services = ConfigureServices(new ServiceCollection());

var contextServices = ((IInfrastructure<IServiceProvider>)context).Instance;
ConfigureContextServices(((IInfrastructure<IServiceProvider>)context).Instance, services);

ConfigureProviderServices(contextServices.GetRequiredService<IDatabaseProvider>().Name, services);

var services = new ServiceCollection()
.AddEntityFrameworkDesignTimeServices(_reporter)
.AddDbContextDesignTimeServices(context);
ConfigureProviderServices(context.GetService<IDatabaseProvider>().Name, services);
ConfigureUserServices(services);

return services.BuildServiceProvider();
Expand All @@ -63,50 +55,16 @@ public virtual IServiceProvider Build([NotNull] DbContext context)
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual IServiceProvider Build([NotNull] string provider)
=> ConfigureUserServices(
ConfigureProviderServices(
Check.NotEmpty(provider, nameof(provider)),
ConfigureServices(new ServiceCollection()), throwOnError: true))
.BuildServiceProvider();
{
Check.NotEmpty(provider, nameof(provider));

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
protected virtual IServiceCollection ConfigureServices([NotNull] IServiceCollection services)
=> services
.AddSingleton<ICSharpHelper, CSharpHelper>()
.AddSingleton<CSharpMigrationOperationGeneratorDependencies>()
.AddSingleton<ICSharpMigrationOperationGenerator, CSharpMigrationOperationGenerator>()
.AddSingleton<CSharpSnapshotGeneratorDependencies>()
.AddSingleton<ICSharpSnapshotGenerator, CSharpSnapshotGenerator>()
.AddSingleton<MigrationsCodeGeneratorDependencies>()
.AddSingleton<CSharpMigrationsGeneratorDependencies>()
.AddSingleton<MigrationsCodeGeneratorSelector>()
.AddSingleton<IMigrationsCodeGenerator, CSharpMigrationsGenerator>()
.AddSingleton(_reporter)
.AddScaffolding(_reporter);
var services = new ServiceCollection()
.AddEntityFrameworkDesignTimeServices(_reporter);
ConfigureProviderServices(provider, services, throwOnError: true);
ConfigureUserServices(services);

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
protected virtual IServiceCollection ConfigureContextServices(
[NotNull] IServiceProvider contextServices,
[NotNull] IServiceCollection services)
=> services
.AddTransient<MigrationsScaffolderDependencies>()
.AddTransient<MigrationsScaffolder>()
.AddTransient<ISnapshotModelProcessor, SnapshotModelProcessor>()
.AddTransient(_ => contextServices.GetService<ICurrentDbContext>())
.AddTransient(_ => contextServices.GetService<IDatabaseProvider>())
.AddTransient(_ => contextServices.GetService<IDbContextOptions>())
.AddTransient(_ => contextServices.GetService<IHistoryRepository>())
.AddTransient(_ => contextServices.GetService<IMigrationsAssembly>())
.AddTransient(_ => contextServices.GetService<IMigrationsIdGenerator>())
.AddTransient(_ => contextServices.GetService<IMigrationsModelDiffer>())
.AddTransient(_ => contextServices.GetService<IMigrator>())
.AddTransient(_ => contextServices.GetService<IModel>());
return services.BuildServiceProvider();
}

private IServiceCollection ConfigureUserServices(IServiceCollection services)
{
Expand Down
4 changes: 2 additions & 2 deletions src/EFCore.Design/Design/Internal/MigrationsOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public virtual MigrationFiles AddMigration(
EnsureServices(services);
EnsureMigrationsAssembly(services);

var scaffolder = services.GetRequiredService<MigrationsScaffolder>();
var scaffolder = services.GetRequiredService<IMigrationsScaffolder>();
var migration = scaffolder.ScaffoldMigration(name, _rootNamespace, subNamespace, _language);
var files = scaffolder.Save(_projectDir, migration, outputDir);

Expand Down Expand Up @@ -188,7 +188,7 @@ public virtual MigrationFiles RemoveMigration(
EnsureServices(services);
EnsureMigrationsAssembly(services);

var scaffolder = services.GetRequiredService<MigrationsScaffolder>();
var scaffolder = services.GetRequiredService<IMigrationsScaffolder>();

var files = scaffolder.RemoveMigration(_projectDir, _rootNamespace, force, revert, _language);

Expand Down
2 changes: 1 addition & 1 deletion src/EFCore.Design/Design/OperationExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ private IDictionary ScaffoldContextImpl(
return new Hashtable
{
["ContextFile"] = files.ContextFile,
["EntityTypeFiles"] = files.EntityTypeFiles.ToArray()
["EntityTypeFiles"] = files.AdditionalFiles.ToArray()
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using JetBrains.Annotations;

namespace Microsoft.EntityFrameworkCore.Migrations.Design
{
/// <summary>
/// Selects an <see cref="IMigrationsCodeGenerator" /> service for a given programming language.
/// </summary>
public interface IMigrationsCodeGeneratorSelector
{
/// <summary>
/// Selects an <see cref="IMigrationsCodeGenerator" /> service for a given programming language.
/// </summary>
/// <param name="language"> The programming language. </param>
/// <returns> The <see cref="IMigrationsCodeGenerator" />. </returns>
IMigrationsCodeGenerator Select([CanBeNull] string language);
}
}
Loading