From 69ceb280bfa2cc830c8d3e6c9958653752202449 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Mon, 25 Jul 2022 16:02:10 +0100 Subject: [PATCH 1/3] Add a static flag in EF that will be set when code is being executed for design-time discovery Fixes #27306. --- src/EFCore.Design/Design/OperationExecutor.cs | 1 + src/EFCore/EF.cs | 16 ++++++++++++++++ .../Design/OperationExecutorTest.cs | 15 +++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/src/EFCore.Design/Design/OperationExecutor.cs b/src/EFCore.Design/Design/OperationExecutor.cs index f8ea9076ea2..73dae4cbe28 100644 --- a/src/EFCore.Design/Design/OperationExecutor.cs +++ b/src/EFCore.Design/Design/OperationExecutor.cs @@ -697,6 +697,7 @@ public abstract class OperationBase : MarshalByRefObject /// The . protected OperationBase(IOperationResultHandler resultHandler) { + EF.IsDesignTime = true; _resultHandler = resultHandler; } diff --git a/src/EFCore/EF.cs b/src/EFCore/EF.cs index 457cc47efda..57b650f9251 100644 --- a/src/EFCore/EF.cs +++ b/src/EFCore/EF.cs @@ -17,6 +17,22 @@ public static partial class EF internal static readonly MethodInfo PropertyMethod = typeof(EF).GetTypeInfo().GetDeclaredMethod(nameof(Property))!; + /// + /// This flag is set to when code is being run from a design-time tool, such + /// as "dotnet ef" or one of the Package Manager Console PowerShell commands "Add-Migration", "Update-Database", etc. + /// + /// + /// + /// This flag can be inspected to change application behavior. For example, if the application is being executed by an EF + /// design-time tool, then it may choose to skip executing migrations commands as part of startup. + /// + /// + /// See EF Core command-line reference for more information + /// and examples. + /// + /// + public static bool IsDesignTime { get; set; } + /// /// References a given property or navigation on an entity instance. This is useful for shadow state properties, for /// which no CLR property exists. Currently this method can only be used in LINQ queries and can not be used to diff --git a/test/EFCore.Design.Tests/Design/OperationExecutorTest.cs b/test/EFCore.Design.Tests/Design/OperationExecutorTest.cs index 1b9e1963b31..ce57bf03a06 100644 --- a/test/EFCore.Design.Tests/Design/OperationExecutorTest.cs +++ b/test/EFCore.Design.Tests/Design/OperationExecutorTest.cs @@ -17,6 +17,21 @@ public void Ctor_validates_arguments() public class OperationBaseTests { + [ConditionalFact] + public void Operations_have_design_time_flag_set() + { + var handler = new OperationResultHandler(); + var result = "Twilight Sparkle"; + + new MockOperation(handler, () => + { + Assert.True(EF.IsDesignTime); + return result; + }); + + Assert.Equal(result, handler.Result); + } + [ConditionalFact] public void Execute_catches_exceptions() { From 5e254a5b2f169144344655227632f20255644286 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Tue, 26 Jul 2022 11:54:28 +0100 Subject: [PATCH 2/3] Move setting flag to Execute method. --- src/EFCore.Design/Design/OperationExecutor.cs | 6 +++++- test/EFCore.Design.Tests/Design/OperationExecutorTest.cs | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/EFCore.Design/Design/OperationExecutor.cs b/src/EFCore.Design/Design/OperationExecutor.cs index 73dae4cbe28..e9a7295a62f 100644 --- a/src/EFCore.Design/Design/OperationExecutor.cs +++ b/src/EFCore.Design/Design/OperationExecutor.cs @@ -697,7 +697,6 @@ public abstract class OperationBase : MarshalByRefObject /// The . protected OperationBase(IOperationResultHandler resultHandler) { - EF.IsDesignTime = true; _resultHandler = resultHandler; } @@ -707,6 +706,7 @@ protected OperationBase(IOperationResultHandler resultHandler) /// The action to execute. protected virtual void Execute(Action action) { + EF.IsDesignTime = true; try { action(); @@ -715,6 +715,10 @@ protected virtual void Execute(Action action) { _resultHandler.OnError(ex.GetType().FullName!, ex.Message, ex.ToString()); } + finally + { + EF.IsDesignTime = false; + } } /// diff --git a/test/EFCore.Design.Tests/Design/OperationExecutorTest.cs b/test/EFCore.Design.Tests/Design/OperationExecutorTest.cs index ce57bf03a06..072b5f8cf5b 100644 --- a/test/EFCore.Design.Tests/Design/OperationExecutorTest.cs +++ b/test/EFCore.Design.Tests/Design/OperationExecutorTest.cs @@ -23,12 +23,15 @@ public void Operations_have_design_time_flag_set() var handler = new OperationResultHandler(); var result = "Twilight Sparkle"; + Assert.False(EF.IsDesignTime); + new MockOperation(handler, () => { Assert.True(EF.IsDesignTime); return result; }); + Assert.False(EF.IsDesignTime); Assert.Equal(result, handler.Result); } From f55557b0e5665445bebe08aa234cd7940891b457 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Thu, 28 Jul 2022 14:46:49 +0100 Subject: [PATCH 3/3] Also set flag in DbContextActivator. --- src/EFCore.Design/Design/DbContextActivator.cs | 2 ++ .../Design/DbContextActivatorTest.cs | 14 +++++++++++++- .../Design/OperationExecutorTest.cs | 4 ++-- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/EFCore.Design/Design/DbContextActivator.cs b/src/EFCore.Design/Design/DbContextActivator.cs index 037fe02ff14..164826caaaa 100644 --- a/src/EFCore.Design/Design/DbContextActivator.cs +++ b/src/EFCore.Design/Design/DbContextActivator.cs @@ -43,6 +43,8 @@ public static DbContext CreateInstance( { Check.NotNull(contextType, nameof(contextType)); + EF.IsDesignTime = true; + return new DbContextOperations( new OperationReporter(reportHandler), contextType.Assembly, diff --git a/test/EFCore.Design.Tests/Design/DbContextActivatorTest.cs b/test/EFCore.Design.Tests/Design/DbContextActivatorTest.cs index 8628f6b3272..f0e8f6727b0 100644 --- a/test/EFCore.Design.Tests/Design/DbContextActivatorTest.cs +++ b/test/EFCore.Design.Tests/Design/DbContextActivatorTest.cs @@ -8,14 +8,20 @@ public class DbContextActivatorTest [ConditionalFact] public void CreateInstance_works() { + EF.IsDesignTime = false; + var result = DbContextActivator.CreateInstance(typeof(TestContext)); Assert.IsType(result); + + Assert.True(EF.IsDesignTime); } [ConditionalFact] public void CreateInstance_with_arguments_works() { + EF.IsDesignTime = false; + var result = DbContextActivator.CreateInstance( typeof(TestContext), null, @@ -23,13 +29,19 @@ public void CreateInstance_with_arguments_works() new[] { "A", "B" }); Assert.IsType(result); + + Assert.True(EF.IsDesignTime); } private class TestContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder options) - => options + { + Assert.True(EF.IsDesignTime); + + options .EnableServiceProviderCaching(false) .UseInMemoryDatabase(nameof(DbContextActivatorTest)); + } } } diff --git a/test/EFCore.Design.Tests/Design/OperationExecutorTest.cs b/test/EFCore.Design.Tests/Design/OperationExecutorTest.cs index 072b5f8cf5b..7794f8062e7 100644 --- a/test/EFCore.Design.Tests/Design/OperationExecutorTest.cs +++ b/test/EFCore.Design.Tests/Design/OperationExecutorTest.cs @@ -20,11 +20,11 @@ public class OperationBaseTests [ConditionalFact] public void Operations_have_design_time_flag_set() { + EF.IsDesignTime = false; + var handler = new OperationResultHandler(); var result = "Twilight Sparkle"; - Assert.False(EF.IsDesignTime); - new MockOperation(handler, () => { Assert.True(EF.IsDesignTime);