Skip to content

Commit ef3416c

Browse files
committed
Move all assembly loading logic to IOperationExecutor
1 parent 19535a7 commit ef3416c

8 files changed

+116
-73
lines changed

src/ef/AppDomainOperationExecutor.cs

+28-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
#if NET472
5-
using System;
65
using System.Collections;
7-
using System.IO;
86
using System.Reflection;
97
using Microsoft.EntityFrameworkCore.Design;
108
using Microsoft.EntityFrameworkCore.Design.Internal;
@@ -18,11 +16,13 @@ internal class AppDomainOperationExecutor : OperationExecutorBase
1816
private readonly object _executor;
1917
private readonly AppDomain _domain;
2018
private bool _disposed;
19+
private string? _efcoreVersion;
2120
private const string ReportHandlerTypeName = "Microsoft.EntityFrameworkCore.Design.OperationReportHandler";
2221

2322
public AppDomainOperationExecutor(
2423
string assembly,
2524
string? startupAssembly,
25+
string? designAssembly,
2626
string? project,
2727
string? projectDir,
2828
string? dataDirectory,
@@ -31,7 +31,7 @@ public AppDomainOperationExecutor(
3131
bool nullable,
3232
string[] remainingArguments,
3333
IOperationReportHandler reportHandler)
34-
: base(assembly, startupAssembly, project, projectDir, rootNamespace, language, nullable, remainingArguments, reportHandler)
34+
: base(assembly, startupAssembly, designAssembly, project, projectDir, rootNamespace, language, nullable, remainingArguments, reportHandler)
3535
{
3636
var info = new AppDomainSetup { ApplicationBase = AppBasePath };
3737

@@ -66,6 +66,15 @@ public AppDomainOperationExecutor(
6666
null,
6767
null);
6868

69+
if (DesignAssemblyPath != null)
70+
{
71+
_domain.AssemblyResolve += (object? sender, ResolveEventArgs args) =>
72+
{
73+
var assemblyPath = Path.Combine(Path.GetDirectoryName(DesignAssemblyPath)!, args.Name + ".dll");
74+
return File.Exists(assemblyPath) ? Assembly.LoadFrom(assemblyPath) : null;
75+
};
76+
}
77+
6978
_executor = _domain.CreateInstanceAndUnwrap(
7079
DesignAssemblyName,
7180
ExecutorTypeName,
@@ -91,6 +100,22 @@ public AppDomainOperationExecutor(
91100
null);
92101
}
93102

103+
public override string? EFCoreVersion
104+
{
105+
get
106+
{
107+
if (_efcoreVersion != null)
108+
{
109+
return _efcoreVersion;
110+
}
111+
112+
var designAssembly = _domain.GetAssemblies().Single(assembly => assembly.GetName().Name == DesignAssemblyName);
113+
_efcoreVersion = designAssembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
114+
?.InformationalVersion;
115+
return _efcoreVersion;
116+
}
117+
}
118+
94119
protected override object CreateResultHandler()
95120
=> new OperationResultHandler();
96121

src/ef/Commands/DbContextOptimizeCommand.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ protected override void Validate()
3232

3333
protected override int Execute(string[] args)
3434
{
35-
if (new SemanticVersionComparer().Compare(EFCoreVersion, "6.0.0") < 0)
35+
using var executor = CreateExecutor(args);
36+
if (new SemanticVersionComparer().Compare(executor.EFCoreVersion, "6.0.0") < 0)
3637
{
3738
throw new CommandException(Resources.VersionRequired("6.0.0"));
3839
}
3940

40-
using var executor = CreateExecutor(args);
4141
var result = executor.OptimizeContext(
4242
_outputDir!.Value(),
4343
_namespace!.Value(),

src/ef/Commands/MigrationsBundleCommand.cs

+8-6
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,16 @@ protected override int Execute(string[] args)
3535
#else
3636
protected override int Execute(string[] args)
3737
{
38-
if (new SemanticVersionComparer().Compare(EFCoreVersion, "6.0.0") < 0)
39-
{
40-
throw new CommandException(Resources.VersionRequired("6.0.0"));
41-
}
42-
38+
string? version;
4339
string context;
4440
using (var executor = CreateExecutor(args))
4541
{
42+
version = executor.EFCoreVersion;
43+
if (new SemanticVersionComparer().Compare(version, "6.0.0") < 0)
44+
{
45+
throw new CommandException(Resources.VersionRequired("6.0.0"));
46+
}
47+
4648
context = (string)executor.GetContextInfo(Context!.Value())["Type"]!;
4749
}
4850

@@ -53,7 +55,7 @@ protected override int Execute(string[] args)
5355
Session = new Dictionary<string, object>
5456
{
5557
["TargetFramework"] = Framework!.Value()!,
56-
["EFCoreVersion"] = EFCoreVersion!,
58+
["EFCoreVersion"] = version!,
5759
["Project"] = Project!.Value()!,
5860
["StartupProject"] = StartupProject!.Value()!
5961
}

src/ef/Commands/MigrationsHasPendingModelChangesCommand.cs

+2-3
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@ internal partial class MigrationsHasPendingModelChangesCommand
1010
{
1111
protected override int Execute(string[] args)
1212
{
13-
if (new SemanticVersionComparer().Compare(EFCoreVersion, "8.0.0") < 0)
13+
using var executor = CreateExecutor(args);
14+
if (new SemanticVersionComparer().Compare(executor.EFCoreVersion, "8.0.0") < 0)
1415
{
1516
throw new CommandException(Resources.VersionRequired("8.0.0"));
1617
}
1718

18-
using var executor = CreateExecutor(args);
19-
2019
executor.HasPendingModelChanges(Context!.Value());
2120

2221
return base.Execute(args);

src/ef/Commands/ProjectCommandBase.cs

+2-56
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ internal abstract class ProjectCommandBase : EFCommandBase
2020
private CommandOption? _rootNamespace;
2121
private CommandOption? _language;
2222
private CommandOption? _nullable;
23-
private string? _efcoreVersion;
2423
private CommandOption? _designAssembly;
2524

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

34-
#if !NET472
35-
private AssemblyLoadContext? _assemblyLoadContext;
36-
protected AssemblyLoadContext AssemblyLoadContext
37-
{
38-
get
39-
{
40-
if (_assemblyLoadContext != null)
41-
{
42-
return _assemblyLoadContext;
43-
}
44-
45-
if (_designAssembly!.Value() != null)
46-
{
47-
AssemblyLoadContext.Default.Resolving += (context, name) =>
48-
{
49-
var assemblyPath = Path.GetDirectoryName(_designAssembly!.Value())!;
50-
assemblyPath = Path.Combine(assemblyPath, name.Name + ".dll");
51-
return File.Exists(assemblyPath) ? context.LoadFromAssemblyPath(assemblyPath) : null;
52-
};
53-
_assemblyLoadContext = AssemblyLoadContext.Default;
54-
}
55-
56-
return AssemblyLoadContext.Default;
57-
}
58-
}
59-
#endif
60-
61-
protected string? EFCoreVersion
62-
{
63-
get
64-
{
65-
if (_efcoreVersion != null)
66-
{
67-
return _efcoreVersion;
68-
}
69-
70-
Assembly? assembly = null;
71-
#if !NET472
72-
assembly = AssemblyLoadContext.LoadFromAssemblyName(new AssemblyName("Microsoft.EntityFrameworkCore.Design"));
73-
#else
74-
if (_designAssembly!.Value() != null)
75-
{
76-
var assemblyPath = Path.GetDirectoryName(_designAssembly!.Value());
77-
assemblyPath = Path.Combine(assemblyPath, "Microsoft.EntityFrameworkCore.Design.dll");
78-
assembly = System.Reflection.Assembly.LoadFrom(assemblyPath);
79-
} else
80-
{
81-
assembly = System.Reflection.Assembly.Load("Microsoft.EntityFrameworkCore.Design");
82-
}
83-
#endif
84-
_efcoreVersion = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
85-
?.InformationalVersion;
86-
return _efcoreVersion;
87-
}
88-
}
8933

9034
public override void Configure(CommandLineApplication command)
9135
{
@@ -143,6 +87,7 @@ protected IOperationExecutor CreateExecutor(string[] remainingArguments)
14387
return new AppDomainOperationExecutor(
14488
Assembly!.Value()!,
14589
StartupAssembly!.Value(),
90+
_designAssembly!.Value(),
14691
Project!.Value(),
14792
_projectDir!.Value(),
14893
_dataDir!.Value(),
@@ -180,6 +125,7 @@ protected IOperationExecutor CreateExecutor(string[] remainingArguments)
180125
return new ReflectionOperationExecutor(
181126
Assembly!.Value()!,
182127
StartupAssembly!.Value(),
128+
_designAssembly!.Value(),
183129
Project!.Value(),
184130
_projectDir!.Value(),
185131
_dataDir!.Value(),

src/ef/IOperationExecutor.cs

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ namespace Microsoft.EntityFrameworkCore.Tools;
77

88
internal interface IOperationExecutor : IDisposable
99
{
10+
string? EFCoreVersion { get; }
11+
1012
IDictionary AddMigration(string name, string? outputDir, string? contextType, string? @namespace);
1113
IDictionary RemoveMigration(string? contextType, bool force);
1214
IEnumerable<IDictionary> GetMigrations(string? contextType, string? connectionString, bool noConnect);

src/ef/OperationExecutorBase.cs

+7-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ internal abstract class OperationExecutorBase : IOperationExecutor
1616
private static readonly IDictionary EmptyArguments = new Dictionary<string, object>(0);
1717
public string AppBasePath { get; }
1818

19-
protected string AssemblyFileName { get; set; }
20-
protected string StartupAssemblyFileName { get; set; }
19+
protected string AssemblyFileName { get; }
20+
protected string StartupAssemblyFileName { get; }
21+
protected string? DesignAssemblyPath { get; }
2122
protected string ProjectDirectory { get; }
2223
protected string Project { get; }
2324
protected string RootNamespace { get; }
@@ -28,6 +29,7 @@ internal abstract class OperationExecutorBase : IOperationExecutor
2829
protected OperationExecutorBase(
2930
string assembly,
3031
string? startupAssembly,
32+
string? designAssembly,
3133
string? project,
3234
string? projectDir,
3335
string? rootNamespace,
@@ -40,6 +42,7 @@ protected OperationExecutorBase(
4042
StartupAssemblyFileName = startupAssembly == null
4143
? AssemblyFileName
4244
: Path.GetFileNameWithoutExtension(startupAssembly);
45+
DesignAssemblyPath = designAssembly;
4346

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

67+
public abstract string? EFCoreVersion { get; }
68+
6469
public virtual void Dispose()
6570
{
6671
}

src/ef/ReflectionOperationExecutor.cs

+65-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
using System.Collections;
55
using System.Reflection;
6+
#if !NET472
7+
using System.Runtime.Loader;
8+
#endif
69
using Microsoft.EntityFrameworkCore.Design;
710
using Microsoft.EntityFrameworkCore.Design.Internal;
811
using Microsoft.EntityFrameworkCore.Infrastructure;
@@ -17,10 +20,15 @@ internal class ReflectionOperationExecutor : OperationExecutorBase
1720
private const string ReportHandlerTypeName = "Microsoft.EntityFrameworkCore.Design.OperationReportHandler";
1821
private const string ResultHandlerTypeName = "Microsoft.EntityFrameworkCore.Design.OperationResultHandler";
1922
private readonly Type _resultHandlerType;
23+
private string? _efcoreVersion;
24+
#if !NET472
25+
private AssemblyLoadContext? _assemblyLoadContext;
26+
#endif
2027

2128
public ReflectionOperationExecutor(
2229
string assembly,
2330
string? startupAssembly,
31+
string? designAssembly,
2432
string? project,
2533
string? projectDir,
2634
string? dataDirectory,
@@ -29,7 +37,7 @@ public ReflectionOperationExecutor(
2937
bool nullable,
3038
string[] remainingArguments,
3139
IOperationReportHandler reportHandler)
32-
: base(assembly, startupAssembly, project, projectDir, rootNamespace, language, nullable, remainingArguments, reportHandler)
40+
: base(assembly, startupAssembly, designAssembly, project, projectDir, rootNamespace, language, nullable, remainingArguments, reportHandler)
3341
{
3442
var reporter = new OperationReporter(reportHandler);
3543
var configurationFile = (startupAssembly ?? assembly) + ".config";
@@ -76,6 +84,62 @@ public ReflectionOperationExecutor(
7684
_resultHandlerType = _commandsAssembly.GetType(ResultHandlerTypeName, throwOnError: true, ignoreCase: false)!;
7785
}
7886

87+
#if !NET472
88+
protected AssemblyLoadContext AssemblyLoadContext
89+
{
90+
get
91+
{
92+
if (_assemblyLoadContext != null)
93+
{
94+
return _assemblyLoadContext;
95+
}
96+
97+
if (DesignAssemblyPath != null)
98+
{
99+
AssemblyLoadContext.Default.Resolving += (context, name) =>
100+
{
101+
var assemblyPath = Path.GetDirectoryName(DesignAssemblyPath)!;
102+
assemblyPath = Path.Combine(assemblyPath, name.Name + ".dll");
103+
return File.Exists(assemblyPath) ? context.LoadFromAssemblyPath(assemblyPath) : null;
104+
};
105+
_assemblyLoadContext = AssemblyLoadContext.Default;
106+
}
107+
108+
return AssemblyLoadContext.Default;
109+
}
110+
}
111+
#endif
112+
113+
public override string? EFCoreVersion
114+
{
115+
get
116+
{
117+
if (_efcoreVersion != null)
118+
{
119+
return _efcoreVersion;
120+
}
121+
122+
Assembly? assembly = null;
123+
#if !NET472
124+
assembly = AssemblyLoadContext.LoadFromAssemblyName(new AssemblyName(DesignAssemblyName));
125+
#else
126+
if (DesignAssemblyPath != null)
127+
{
128+
var assemblyPath = Path.GetDirectoryName(DesignAssemblyPath);
129+
assemblyPath = Path.Combine(assemblyPath, DesignAssemblyName + ".dll");
130+
assembly = Assembly.LoadFrom(assemblyPath);
131+
}
132+
else
133+
{
134+
assembly = Assembly.Load(DesignAssemblyName);
135+
}
136+
#endif
137+
_efcoreVersion = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
138+
?.InformationalVersion;
139+
return _efcoreVersion;
140+
}
141+
}
142+
79143
protected override object CreateResultHandler()
80144
=> Activator.CreateInstance(_resultHandlerType)!;
81145

0 commit comments

Comments
 (0)