From cb6005a58049172c2d232c55c8571283e609519e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 Aug 2025 17:21:49 +0000 Subject: [PATCH 1/3] Initial plan From 3a014f78e502895cb1b0d4f3ef592ebcef86a9ea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 Aug 2025 17:35:58 +0000 Subject: [PATCH 2/3] Fix MissingMethodException for abstract IDesignTimeServices classes Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com> --- .../Internal/DesignTimeServicesBuilder.cs | 2 +- .../Design/DesignTimeServicesTest.cs | 95 +++++++++++++++++++ 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/src/EFCore.Design/Design/Internal/DesignTimeServicesBuilder.cs b/src/EFCore.Design/Design/Internal/DesignTimeServicesBuilder.cs index 9a4ee6d7d3d..bdf6a25c707 100644 --- a/src/EFCore.Design/Design/Internal/DesignTimeServicesBuilder.cs +++ b/src/EFCore.Design/Design/Internal/DesignTimeServicesBuilder.cs @@ -98,7 +98,7 @@ private void ConfigureUserServices(IServiceCollection services) _reporter.WriteVerbose(DesignStrings.FindingDesignTimeServices(_startupAssembly.GetName().Name)); var designTimeServicesType = _startupAssembly.GetLoadableDefinedTypes() - .Where(t => typeof(IDesignTimeServices).IsAssignableFrom(t)).Select(t => t.AsType()) + .Where(t => typeof(IDesignTimeServices).IsAssignableFrom(t) && !t.IsAbstract).Select(t => t.AsType()) .FirstOrDefault(); if (designTimeServicesType == null) { diff --git a/test/EFCore.Design.Tests/Design/DesignTimeServicesTest.cs b/test/EFCore.Design.Tests/Design/DesignTimeServicesTest.cs index 162a18e5e70..2efe1e4d4ae 100644 --- a/test/EFCore.Design.Tests/Design/DesignTimeServicesTest.cs +++ b/test/EFCore.Design.Tests/Design/DesignTimeServicesTest.cs @@ -314,6 +314,101 @@ public string GetInsertScript(HistoryRow row) => throw new NotImplementedException(); } + [ConditionalFact] + public void Abstract_design_time_services_are_ignored() + { + var serviceProvider = CreateDesignServiceProvider( + @" +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.DependencyInjection; + +#pragma warning disable EF1001 + +public abstract class DesignTimeServicesBase : IDesignTimeServices +{ + public void ConfigureDesignTimeServices(IServiceCollection serviceCollection) + { + DoSomething(); + } + + protected abstract void DoSomething(); +} + +public class ActualDesignTimeServices : DesignTimeServicesBase +{ + protected override void DoSomething() + { + // Actual implementation + } +} +").CreateScope().ServiceProvider; + + // Should not throw an exception when creating the service provider + // The abstract base class should be ignored and only concrete implementations should be used + Assert.NotNull(serviceProvider); + } + + [ConditionalFact] + public void Interface_design_time_services_are_ignored() + { + var serviceProvider = CreateDesignServiceProvider( + @" +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.DependencyInjection; + +#pragma warning disable EF1001 + +public interface IDesignTimeServicesInterface : IDesignTimeServices +{ +} + +public class ConcreteDesignTimeServices : IDesignTimeServicesInterface +{ + public void ConfigureDesignTimeServices(IServiceCollection serviceCollection) + { + // Concrete implementation + } +} +").CreateScope().ServiceProvider; + + // Should not throw an exception when creating the service provider + // Interfaces implementing IDesignTimeServices should be ignored + Assert.NotNull(serviceProvider); + } + + [ConditionalFact] + public void Prefers_concrete_class_over_abstract() + { + // This test verifies that when both abstract and concrete classes are present, + // the concrete class is used (since we're using FirstOrDefault which finds the first non-abstract) + var serviceProvider = CreateDesignServiceProvider( + @" +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.DependencyInjection; + +#pragma warning disable EF1001 + +public abstract class AbstractDesignTimeServices : IDesignTimeServices +{ + public void ConfigureDesignTimeServices(IServiceCollection serviceCollection) + { + throw new System.NotImplementedException(""Should not be called""); + } +} + +public class ConcreteDesignTimeServices : IDesignTimeServices +{ + public void ConfigureDesignTimeServices(IServiceCollection serviceCollection) + { + // This should be the one used + } +} +").CreateScope().ServiceProvider; + + // Should not throw an exception and should use the concrete implementation + Assert.NotNull(serviceProvider); + } + public class MyContext(DbContextOptions options) : DbContext(options); private ServiceProvider CreateDesignServiceProvider( From 18a7316c19641dc198e1162b7e545d0c78d7e7ed Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 Aug 2025 17:50:43 +0000 Subject: [PATCH 3/3] Use IsInstantiable() instead of !IsAbstract for comprehensive type filtering Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com> --- src/EFCore.Design/Design/Internal/DesignTimeServicesBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EFCore.Design/Design/Internal/DesignTimeServicesBuilder.cs b/src/EFCore.Design/Design/Internal/DesignTimeServicesBuilder.cs index bdf6a25c707..e382d56f221 100644 --- a/src/EFCore.Design/Design/Internal/DesignTimeServicesBuilder.cs +++ b/src/EFCore.Design/Design/Internal/DesignTimeServicesBuilder.cs @@ -98,7 +98,7 @@ private void ConfigureUserServices(IServiceCollection services) _reporter.WriteVerbose(DesignStrings.FindingDesignTimeServices(_startupAssembly.GetName().Name)); var designTimeServicesType = _startupAssembly.GetLoadableDefinedTypes() - .Where(t => typeof(IDesignTimeServices).IsAssignableFrom(t) && !t.IsAbstract).Select(t => t.AsType()) + .Where(t => typeof(IDesignTimeServices).IsAssignableFrom(t) && t.IsInstantiable()).Select(t => t.AsType()) .FirstOrDefault(); if (designTimeServicesType == null) {