Skip to content

Commit

Permalink
Move all assembly loading logic to IOperationExecutor
Browse files Browse the repository at this point in the history
  • Loading branch information
AndriySvyryd committed Jan 25, 2025
1 parent 19535a7 commit ef3416c
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 73 deletions.
31 changes: 28 additions & 3 deletions src/ef/AppDomainOperationExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

#if NET472
using System;
using System.Collections;
using System.IO;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Design.Internal;
Expand All @@ -18,11 +16,13 @@ internal class AppDomainOperationExecutor : OperationExecutorBase
private readonly object _executor;
private readonly AppDomain _domain;
private bool _disposed;
private string? _efcoreVersion;
private const string ReportHandlerTypeName = "Microsoft.EntityFrameworkCore.Design.OperationReportHandler";

public AppDomainOperationExecutor(
string assembly,
string? startupAssembly,
string? designAssembly,
string? project,
string? projectDir,
string? dataDirectory,
Expand All @@ -31,7 +31,7 @@ public AppDomainOperationExecutor(
bool nullable,
string[] remainingArguments,
IOperationReportHandler reportHandler)
: base(assembly, startupAssembly, project, projectDir, rootNamespace, language, nullable, remainingArguments, reportHandler)
: base(assembly, startupAssembly, designAssembly, project, projectDir, rootNamespace, language, nullable, remainingArguments, reportHandler)
{
var info = new AppDomainSetup { ApplicationBase = AppBasePath };

Expand Down Expand Up @@ -66,6 +66,15 @@ public AppDomainOperationExecutor(
null,
null);

if (DesignAssemblyPath != null)
{
_domain.AssemblyResolve += (object? sender, ResolveEventArgs args) =>
{
var assemblyPath = Path.Combine(Path.GetDirectoryName(DesignAssemblyPath)!, args.Name + ".dll");
return File.Exists(assemblyPath) ? Assembly.LoadFrom(assemblyPath) : null;
};
}

_executor = _domain.CreateInstanceAndUnwrap(
DesignAssemblyName,
ExecutorTypeName,
Expand All @@ -91,6 +100,22 @@ public AppDomainOperationExecutor(
null);
}

public override string? EFCoreVersion
{
get
{
if (_efcoreVersion != null)
{
return _efcoreVersion;
}

var designAssembly = _domain.GetAssemblies().Single(assembly => assembly.GetName().Name == DesignAssemblyName);
_efcoreVersion = designAssembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
?.InformationalVersion;
return _efcoreVersion;
}
}

protected override object CreateResultHandler()
=> new OperationResultHandler();

Expand Down
4 changes: 2 additions & 2 deletions src/ef/Commands/DbContextOptimizeCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ protected override void Validate()

protected override int Execute(string[] args)
{
if (new SemanticVersionComparer().Compare(EFCoreVersion, "6.0.0") < 0)
using var executor = CreateExecutor(args);
if (new SemanticVersionComparer().Compare(executor.EFCoreVersion, "6.0.0") < 0)
{
throw new CommandException(Resources.VersionRequired("6.0.0"));
}

using var executor = CreateExecutor(args);
var result = executor.OptimizeContext(
_outputDir!.Value(),
_namespace!.Value(),
Expand Down
14 changes: 8 additions & 6 deletions src/ef/Commands/MigrationsBundleCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,16 @@ protected override int Execute(string[] args)
#else
protected override int Execute(string[] args)
{
if (new SemanticVersionComparer().Compare(EFCoreVersion, "6.0.0") < 0)
{
throw new CommandException(Resources.VersionRequired("6.0.0"));
}

string? version;
string context;
using (var executor = CreateExecutor(args))
{
version = executor.EFCoreVersion;
if (new SemanticVersionComparer().Compare(version, "6.0.0") < 0)
{
throw new CommandException(Resources.VersionRequired("6.0.0"));
}

context = (string)executor.GetContextInfo(Context!.Value())["Type"]!;
}

Expand All @@ -53,7 +55,7 @@ protected override int Execute(string[] args)
Session = new Dictionary<string, object>
{
["TargetFramework"] = Framework!.Value()!,
["EFCoreVersion"] = EFCoreVersion!,
["EFCoreVersion"] = version!,
["Project"] = Project!.Value()!,
["StartupProject"] = StartupProject!.Value()!
}
Expand Down
5 changes: 2 additions & 3 deletions src/ef/Commands/MigrationsHasPendingModelChangesCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@ internal partial class MigrationsHasPendingModelChangesCommand
{
protected override int Execute(string[] args)
{
if (new SemanticVersionComparer().Compare(EFCoreVersion, "8.0.0") < 0)
using var executor = CreateExecutor(args);
if (new SemanticVersionComparer().Compare(executor.EFCoreVersion, "8.0.0") < 0)
{
throw new CommandException(Resources.VersionRequired("8.0.0"));
}

using var executor = CreateExecutor(args);

executor.HasPendingModelChanges(Context!.Value());

return base.Execute(args);
Expand Down
58 changes: 2 additions & 56 deletions src/ef/Commands/ProjectCommandBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ internal abstract class ProjectCommandBase : EFCommandBase
private CommandOption? _rootNamespace;
private CommandOption? _language;
private CommandOption? _nullable;
private string? _efcoreVersion;
private CommandOption? _designAssembly;

protected CommandOption? Assembly { get; private set; }
Expand All @@ -31,61 +30,6 @@ internal abstract class ProjectCommandBase : EFCommandBase
protected CommandOption? Framework { get; private set; }
protected CommandOption? Configuration { get; private set; }

#if !NET472
private AssemblyLoadContext? _assemblyLoadContext;
protected AssemblyLoadContext AssemblyLoadContext
{
get
{
if (_assemblyLoadContext != null)
{
return _assemblyLoadContext;
}

if (_designAssembly!.Value() != null)
{
AssemblyLoadContext.Default.Resolving += (context, name) =>
{
var assemblyPath = Path.GetDirectoryName(_designAssembly!.Value())!;
assemblyPath = Path.Combine(assemblyPath, name.Name + ".dll");
return File.Exists(assemblyPath) ? context.LoadFromAssemblyPath(assemblyPath) : null;
};
_assemblyLoadContext = AssemblyLoadContext.Default;
}

return AssemblyLoadContext.Default;
}
}
#endif

protected string? EFCoreVersion
{
get
{
if (_efcoreVersion != null)
{
return _efcoreVersion;
}

Assembly? assembly = null;
#if !NET472
assembly = AssemblyLoadContext.LoadFromAssemblyName(new AssemblyName("Microsoft.EntityFrameworkCore.Design"));
#else
if (_designAssembly!.Value() != null)
{
var assemblyPath = Path.GetDirectoryName(_designAssembly!.Value());
assemblyPath = Path.Combine(assemblyPath, "Microsoft.EntityFrameworkCore.Design.dll");
assembly = System.Reflection.Assembly.LoadFrom(assemblyPath);
} else
{
assembly = System.Reflection.Assembly.Load("Microsoft.EntityFrameworkCore.Design");
}
#endif
_efcoreVersion = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
?.InformationalVersion;
return _efcoreVersion;
}
}

public override void Configure(CommandLineApplication command)
{
Expand Down Expand Up @@ -143,6 +87,7 @@ protected IOperationExecutor CreateExecutor(string[] remainingArguments)
return new AppDomainOperationExecutor(
Assembly!.Value()!,
StartupAssembly!.Value(),
_designAssembly!.Value(),
Project!.Value(),
_projectDir!.Value(),
_dataDir!.Value(),
Expand Down Expand Up @@ -180,6 +125,7 @@ protected IOperationExecutor CreateExecutor(string[] remainingArguments)
return new ReflectionOperationExecutor(
Assembly!.Value()!,
StartupAssembly!.Value(),
_designAssembly!.Value(),
Project!.Value(),
_projectDir!.Value(),
_dataDir!.Value(),
Expand Down
2 changes: 2 additions & 0 deletions src/ef/IOperationExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ namespace Microsoft.EntityFrameworkCore.Tools;

internal interface IOperationExecutor : IDisposable
{
string? EFCoreVersion { get; }

IDictionary AddMigration(string name, string? outputDir, string? contextType, string? @namespace);
IDictionary RemoveMigration(string? contextType, bool force);
IEnumerable<IDictionary> GetMigrations(string? contextType, string? connectionString, bool noConnect);
Expand Down
9 changes: 7 additions & 2 deletions src/ef/OperationExecutorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ internal abstract class OperationExecutorBase : IOperationExecutor
private static readonly IDictionary EmptyArguments = new Dictionary<string, object>(0);
public string AppBasePath { get; }

protected string AssemblyFileName { get; set; }
protected string StartupAssemblyFileName { get; set; }
protected string AssemblyFileName { get; }
protected string StartupAssemblyFileName { get; }
protected string? DesignAssemblyPath { get; }
protected string ProjectDirectory { get; }
protected string Project { get; }
protected string RootNamespace { get; }
Expand All @@ -28,6 +29,7 @@ internal abstract class OperationExecutorBase : IOperationExecutor
protected OperationExecutorBase(
string assembly,
string? startupAssembly,
string? designAssembly,
string? project,
string? projectDir,
string? rootNamespace,
Expand All @@ -40,6 +42,7 @@ protected OperationExecutorBase(
StartupAssemblyFileName = startupAssembly == null
? AssemblyFileName
: Path.GetFileNameWithoutExtension(startupAssembly);
DesignAssemblyPath = designAssembly;

AppBasePath = Path.GetFullPath(
Path.Combine(Directory.GetCurrentDirectory(), Path.GetDirectoryName(startupAssembly ?? assembly)!));
Expand All @@ -61,6 +64,8 @@ protected OperationExecutorBase(
reporter.WriteVerbose(Resources.RemainingArguments(string.Join(",", RemainingArguments.Select(s => "'" + s + "'"))));
}

public abstract string? EFCoreVersion { get; }

public virtual void Dispose()
{
}
Expand Down
66 changes: 65 additions & 1 deletion src/ef/ReflectionOperationExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

using System.Collections;
using System.Reflection;
#if !NET472
using System.Runtime.Loader;
#endif
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
Expand All @@ -17,10 +20,15 @@ internal class ReflectionOperationExecutor : OperationExecutorBase
private const string ReportHandlerTypeName = "Microsoft.EntityFrameworkCore.Design.OperationReportHandler";
private const string ResultHandlerTypeName = "Microsoft.EntityFrameworkCore.Design.OperationResultHandler";
private readonly Type _resultHandlerType;
private string? _efcoreVersion;
#if !NET472
private AssemblyLoadContext? _assemblyLoadContext;
#endif

public ReflectionOperationExecutor(
string assembly,
string? startupAssembly,
string? designAssembly,
string? project,
string? projectDir,
string? dataDirectory,
Expand All @@ -29,7 +37,7 @@ public ReflectionOperationExecutor(
bool nullable,
string[] remainingArguments,
IOperationReportHandler reportHandler)
: base(assembly, startupAssembly, project, projectDir, rootNamespace, language, nullable, remainingArguments, reportHandler)
: base(assembly, startupAssembly, designAssembly, project, projectDir, rootNamespace, language, nullable, remainingArguments, reportHandler)
{
var reporter = new OperationReporter(reportHandler);
var configurationFile = (startupAssembly ?? assembly) + ".config";
Expand Down Expand Up @@ -76,6 +84,62 @@ public ReflectionOperationExecutor(
_resultHandlerType = _commandsAssembly.GetType(ResultHandlerTypeName, throwOnError: true, ignoreCase: false)!;
}

#if !NET472
protected AssemblyLoadContext AssemblyLoadContext
{
get
{
if (_assemblyLoadContext != null)
{
return _assemblyLoadContext;
}

if (DesignAssemblyPath != null)
{
AssemblyLoadContext.Default.Resolving += (context, name) =>
{
var assemblyPath = Path.GetDirectoryName(DesignAssemblyPath)!;
assemblyPath = Path.Combine(assemblyPath, name.Name + ".dll");
return File.Exists(assemblyPath) ? context.LoadFromAssemblyPath(assemblyPath) : null;
};
_assemblyLoadContext = AssemblyLoadContext.Default;
}

return AssemblyLoadContext.Default;
}
}
#endif

public override string? EFCoreVersion
{
get
{
if (_efcoreVersion != null)
{
return _efcoreVersion;
}

Assembly? assembly = null;
#if !NET472
assembly = AssemblyLoadContext.LoadFromAssemblyName(new AssemblyName(DesignAssemblyName));
#else
if (DesignAssemblyPath != null)
{
var assemblyPath = Path.GetDirectoryName(DesignAssemblyPath);
assemblyPath = Path.Combine(assemblyPath, DesignAssemblyName + ".dll");
assembly = Assembly.LoadFrom(assemblyPath);
}
else
{
assembly = Assembly.Load(DesignAssemblyName);
}
#endif
_efcoreVersion = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
?.InformationalVersion;
return _efcoreVersion;
}
}

protected override object CreateResultHandler()
=> Activator.CreateInstance(_resultHandlerType)!;

Expand Down

0 comments on commit ef3416c

Please sign in to comment.