Skip to content
Merged
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
Expand Up @@ -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.IsInstantiable()).Select(t => t.AsType())
.FirstOrDefault();
if (designTimeServicesType == null)
{
Expand Down
95 changes: 95 additions & 0 deletions test/EFCore.Design.Tests/Design/DesignTimeServicesTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<MyContext> options) : DbContext(options);

private ServiceProvider CreateDesignServiceProvider(
Expand Down
Loading