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

Decouple dotnet-ef from the framework #4977

Closed
wants to merge 3 commits into from
Closed
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
@@ -1,8 +1,6 @@
// 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.

#if NET451

using JetBrains.Annotations;

namespace Microsoft.EntityFrameworkCore.Design.Internal
Expand All @@ -11,8 +9,11 @@ public static class ForwardingProxy
{
public static T Unwrap<T>([NotNull] object target)
where T : class
#if NET451
=> target as T ?? new ForwardingProxy<T>(target).GetTransparentProxy();
#else
=> (T)target;
#endif
}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

namespace Microsoft.EntityFrameworkCore.Design
{
/// <summary>
/// Represents an exception whose stack trace should, by default, not be reported by the commands.
/// </summary>
public class OperationException : Exception
{
public OperationException([NotNull] string message)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// 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.

#if NET451

using System;
using System.Collections;
using System.Collections.Generic;
Expand All @@ -15,16 +13,25 @@

namespace Microsoft.EntityFrameworkCore.Design
{
public class OperationExecutor : MarshalByRefObject
/// <summary>
/// A version-resilient, AppDomain-and-reflection-friendly facade for command operations.
/// </summary>
public partial class OperationExecutor
{
private readonly LazyRef<DbContextOperations> _contextOperations;
private readonly LazyRef<DatabaseOperations> _databaseOperations;
private readonly LazyRef<MigrationsOperations> _migrationsOperations;

public OperationExecutor([NotNull] object logHandler, [NotNull] IDictionary args)
: this(logHandler, args, new AssemblyLoader())
{
}

public OperationExecutor([NotNull] object logHandler, [NotNull] IDictionary args, [NotNull] AssemblyLoader assemblyLoader)
{
Check.NotNull(logHandler, nameof(logHandler));
Check.NotNull(args, nameof(args));
Check.NotNull(assemblyLoader, nameof(assemblyLoader));

var unwrappedLogHandler = ForwardingProxy.Unwrap<IOperationLogHandler>(logHandler);
var loggerProvider = new LoggerProvider(name => new CommandLoggerAdapter(name, unwrappedLogHandler));
Expand All @@ -36,8 +43,6 @@ public OperationExecutor([NotNull] object logHandler, [NotNull] IDictionary args
var startupProjectDir = (string)args["startupProjectDir"];
var rootNamespace = (string)args["rootNamespace"];

var assemblyLoader = new AssemblyLoader();

// NOTE: LazyRef is used so any exceptions get passed to the resultHandler
var startupAssembly = new LazyRef<Assembly>(
() => assemblyLoader.Load(startupTargetName));
Expand Down Expand Up @@ -122,7 +127,7 @@ public AddMigration(
}
}

private IEnumerable<string> AddMigrationImpl(
private IDictionary AddMigrationImpl(
[NotNull] string name,
[CanBeNull] string outputDir,
[CanBeNull] string contextType)
Expand All @@ -134,10 +139,12 @@ private IEnumerable<string> AddMigrationImpl(
outputDir,
contextType);

// NOTE: First file will be opened in VS
yield return files.MigrationFile;
yield return files.MetadataFile;
yield return files.SnapshotFile;
return new Hashtable
Copy link
Contributor

Choose a reason for hiding this comment

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

Change return type of function? This doesn't compile.

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like we also need to update tests

Microsoft.EntityFrameworkCore.Design.Internal.OperationExecutorTest+SimpleProjectTest.AddMigration_works_cross_domain [FAIL]
      System.InvalidCastException : Unable to cast object of type 'System.Collections.Hashtable' to type 'System.Collections.Generic.IEnumerable`1[System.String]'.

Microsoft.EntityFrameworkCore.Design.Internal.OperationExecutorTest.AddMigration_begins_new_namespace_when_foreign_migrations [FAIL]
      System.InvalidCastException : Unable to cast object of type 'System.Collections.Hashtable' to type 'System.Collections.Generic.IEnumerable`1[System.String]'

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks. 😄 I rushed the review out. I was going to actually bash on it today.

{
["MigrationFile"] = files.MigrationFile,
["MetadataFile"] = files.MetadataFile,
["SnapshotFile"] = files.SnapshotFile
};
}

public class UpdateDatabase : OperationBase
Expand Down Expand Up @@ -201,7 +208,7 @@ public RemoveMigration(
Check.NotNull(args, nameof(args));

var contextType = (string)args["contextType"];
var force = args.Contains("force") && (bool)args["force"];
var force = (bool)args["force"];

Execute(() => executor.RemoveMigrationImpl(contextType, force));
}
Expand Down Expand Up @@ -341,7 +348,28 @@ private IEnumerable<string> ReverseEngineerImpl(
}
}

public abstract class OperationBase : MarshalByRefObject
public class DropDatabase : OperationBase
{
public DropDatabase(
[NotNull] OperationExecutor executor,
[NotNull] object resultHandler,
[NotNull] IDictionary args)
: base(resultHandler)
{
Check.NotNull(executor, nameof(executor));
Check.NotNull(args, nameof(args));

var contextType = (string)args["contextType"];
var confirmCheck = (Func<string, string, bool>)args["confirmCheck"];

Execute(() => executor.DropDatabaseImpl(contextType, confirmCheck));
}
}

private void DropDatabaseImpl(string contextType, Func<string, string, bool> confirmCheck)
=> _contextOperations.Value.DropDatabase(contextType, confirmCheck);

public abstract partial class OperationBase
{
private readonly IOperationResultHandler _resultHandler;

Expand Down Expand Up @@ -381,6 +409,14 @@ public virtual void Execute<T>([NotNull] Func<IEnumerable<T>> action)
}
}
}
}

#if NET451
partial class OperationExecutor : MarshalByRefObject
{
partial class OperationBase : MarshalByRefObject
{
}
}
#endif
}

1 change: 1 addition & 0 deletions src/Microsoft.EntityFrameworkCore.Commands/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"portable-net452+win81"
],
"dependencies": {
"System.Collections.NonGeneric": "4.0.1-*",
"System.IO": "4.1.0-*",
"System.IO.FileSystem": "4.0.1-*"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function Use-DbContext {
if ($candidates.length -gt 1 -and $exactMatch -is "String") {
$candidates = $exactMatch
}

if ($candidates.length -lt 1) {
throw "No DbContext named '$Context' was found"
} elseif ($candidates.length -gt 1 -and !($candidates -is "String")) {
Expand Down Expand Up @@ -136,8 +136,10 @@ function Add-Migration {
contextType = $contextTypeName
}

$artifacts | %{ $dteProject.ProjectItems.AddFromFile($_) | Out-Null }
$DTE.ItemOperations.OpenFile($artifacts[0]) | Out-Null
$dteProject.ProjectItems.AddFromFile($artifacts.MigrationFile) | Out-Null
$dteProject.ProjectItems.AddFromFile($artifacts.MetadataFile) | Out-Null
$dteProject.ProjectItems.AddFromFile($artifacts.SnapshotFile) | Out-Null
$DTE.ItemOperations.OpenFile($artifacts.MigrationFile) | Out-Null
ShowConsole
}

Expand Down Expand Up @@ -498,7 +500,7 @@ function Scaffold-DbContext {

$artifacts | %{ $dteProject.ProjectItems.AddFromFile($_) | Out-Null }
$DTE.ItemOperations.OpenFile($artifacts[0]) | Out-Null

ShowConsole
}
}
Expand Down Expand Up @@ -535,7 +537,7 @@ function GetContextTypes($projectName, $startupProjectName, $environment) {
$project = $values.Project

if (IsDotNetProject $startupProject) {
$types = InvokeDotNetEf $startupProject -Json dbcontext list
$types = InvokeDotNetEf $startupProject -Json dbcontext list
return $types | %{ $_.fullName }
} else {
$contextTypes = InvokeOperation $startupProject $environment $project GetContextTypes -skipBuild
Expand Down Expand Up @@ -640,7 +642,7 @@ function InvokeDotNetEf($project, [switch] $Json) {
$arguments += "--json"
} else {
# TODO better json output parsing so we don't need to suppress verbose output
$arguments = ,"--verbose" + $arguments
$arguments = ,"--verbose" + $arguments
}
$command = "ef $($arguments -join ' ')"
try {
Expand Down Expand Up @@ -707,7 +709,7 @@ function InvokeOperation($startupProject, $environment, $project, $operation, $a
if (![Type]::GetType('Microsoft.EntityFrameworkCore.Design.OperationResultHandler')) {
Add-Type -Path (Join-Path $PSScriptRoot OperationHandlers.cs) -CompilerParameters (
New-Object CodeDom.Compiler.CompilerParameters -Property @{
CompilerOptions = '/d:ENABLE_HANDLERS;NET451'
CompilerOptions = '/d:NET451'
})
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +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.

#if ENABLE_HANDLERS && NET451
using System;

#endif
// ReSharper disable once CheckNamespace

namespace Microsoft.EntityFrameworkCore.Design
{
#if !OMIT_HANDLER_INTERFACES
public interface IOperationResultHandler
{
int Version { get; }
Expand All @@ -25,10 +23,8 @@ public interface IOperationLogHandler
void WriteDebug(string message);
void WriteTrace(string message);
}
#endif

#if ENABLE_HANDLERS && NET451
public class OperationResultHandler : MarshalByRefObject, IOperationResultHandler
public partial class OperationResultHandler : IOperationResultHandler
{
private bool _hasResult;
private object _result;
Expand Down Expand Up @@ -62,7 +58,7 @@ public virtual void OnError(string type, string message, string stackTrace)
}
}

public class OperationLogHandler : MarshalByRefObject, IOperationLogHandler
public partial class OperationLogHandler : IOperationLogHandler
{
private readonly Action<string> _writeError;
private readonly Action<string> _writeWarning;
Expand Down Expand Up @@ -96,5 +92,14 @@ public OperationLogHandler(

public virtual void WriteTrace(string message) => _writeTrace?.Invoke(message);
}

#if NET451
partial class OperationResultHandler : MarshalByRefObject
{
}

partial class OperationLogHandler : MarshalByRefObject
{
}
#endif
}
32 changes: 0 additions & 32 deletions src/dotnet-ef/ConsoleCommandLogger.cs

This file was deleted.

2 changes: 1 addition & 1 deletion src/dotnet-ef/DatabaseDropCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public static void Configure([NotNull] CommandLineApplication command)

private static int Execute(string context, string startupProject, string environment, bool isForced)
{
new OperationExecutor(startupProject, environment)
new ReflectionOperationExecutor(startupProject, environment)
.DropDatabase(
context,
(database, dataSource) =>
Expand Down
2 changes: 1 addition & 1 deletion src/dotnet-ef/DatabaseUpdateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public static void Configure([NotNull] CommandLineApplication command)

private static int Execute(string migration, string context, string startupProject, string environment)
{
new OperationExecutor(startupProject, environment)
new ReflectionOperationExecutor(startupProject, environment)
.UpdateDatabase(migration, context);

return 0;
Expand Down
15 changes: 8 additions & 7 deletions src/dotnet-ef/DbContextListCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections;
using System.Collections.Generic;
using JetBrains.Annotations;
using Microsoft.DotNet.Cli.Utils;
Expand Down Expand Up @@ -31,25 +32,25 @@ public static void Configure([NotNull] CommandLineApplication command)
startupProject.Value(),
environment.Value(),
json.HasValue()
? (Action<IEnumerable<Type>>)ReportJsonResults
? (Action<IEnumerable<IDictionary>>)ReportJsonResults
: ReportResults)
);
}

private static int Execute(
string startupProject,
string environment,
Action<IEnumerable<Type>> reportResultsAction)
Action<IEnumerable<IDictionary>> reportResultsAction)
{
var contextTypes = new OperationExecutor(startupProject, environment)
var contextTypes = new ReflectionOperationExecutor(startupProject, environment)
.GetContextTypes();

reportResultsAction(contextTypes);

return 0;
}

private static void ReportJsonResults(IEnumerable<Type> contextTypes)
private static void ReportJsonResults(IEnumerable<IDictionary> contextTypes)
{
Reporter.Output.Write("[");

Expand All @@ -66,19 +67,19 @@ private static void ReportJsonResults(IEnumerable<Type> contextTypes)
}

Reporter.Output.WriteLine();
Reporter.Output.Write(" { \"fullName\": \"" + contextType.FullName + "\" }");
Reporter.Output.Write(" { \"fullName\": \"" + contextType["FullName"] + "\" }");
}

Reporter.Output.WriteLine();
Reporter.Output.WriteLine("]");
}

private static void ReportResults(IEnumerable<Type> contextTypes)
private static void ReportResults(IEnumerable<IDictionary> contextTypes)
{
var any = false;
foreach (var contextType in contextTypes)
{
Reporter.Output.WriteLine(contextType.FullName);
Reporter.Output.WriteLine(contextType["FullName"] as string);
any = true;
}

Expand Down
Loading