question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Scaffold not handling startup assembly, therefore not discovering custom DesignTimeService

See original GitHub issue

We’re having a custom IDesignTimeServices implementation in our EF core project to perform some additional customizations when scaffolding from the database. This customization is changing the handlebar behavior by excluding some additional tables and changing property types.

When scaffolding from command line with dotnet scaffold the design time services are applied correctly. When using EF Core Power Tools “Reverse Engineer”, the services are not applied. Assumption what’s causing this issue is stated below in “Analysis”.

Steps to reproduce

  1. Create blank solution.
  2. Add empty database project and empty C# project to scaffold context and entities into, including NuGet packages for EF Core and handlebars.
  3. Add table dbo.User with UserId INT NOT NULL PRIMARY KEY column to the database project.
  4. Add custom design time services to C# project.
  5. Configure handlebar transformers in custom design time services:
public class ScaffoldingDesignTimeServices : IDesignTimeServices
{
    public void ConfigureDesignTimeServices(IServiceCollection services)
    {
        services.AddHandlebarsTransformers(propertyTransformer: p =>
        {
            // Map to strong types
            var nullable = p.PropertyType.Contains("System.Nullable") || p.PropertyType.Contains("?");
            if (p.PropertyName.Contains("UserId"))
                return new EntityPropertyInfo(CreateNewPropertyType<UNITS.Shared.StrongTypes.InternalUserId>(nullable), p.PropertyName);
        });
    }

    private static string CreateNewPropertyType(string propertyType) => propertyType == "string" ? "string?" : propertyType;

    private static string CreateNewPropertyType<TType>(bool nullable)
    {
        var fullName = typeof(TType).FullName;
        if (fullName == null)
            throw new InvalidOperationException();
        return nullable ? $"{fullName}?" : fullName;
    }
  1. Scaffold the database project into the C# project with handlebars using the “Reverse Engineer” command of EF Core Power Tools.
  2. Generated entity for the User table will have a property int UserId { get; set; } (this is wrong, design time service was not applied).
  3. Scaffold the database project into the C# project with handlebars using the command line dbcontext scaffold.
  4. Generated entity for the User table will have a property UNITS.Shared.StrongTypes.InternalUserId UserId { get; set; } (this is correct, design time service was applied).

Further technical details

EF Core Power Tools version: 2.4.51.0 Database engine: SQL Server / Scaffold from DACPAC Visual Studio version: Visual Studio 2019 16.6.2

Analysis

I had a look at the verbose command line logs of the dbcontext scaffold command and at the source code of the EF Core scaffold pipeline. The verbose output points out that the custom design time services class is found:

Using assembly 'UNITS.Server.Database'.
Using startup assembly 'UNITS.Server.Database'.
Using application base 'S:\herdo-opensource\HoU UNITS\src\Server.Database\bin\Debug\netcoreapp3.1'.
Using working directory 'S:\herdo-opensource\HoU UNITS\src\Server.Database'.
Using root namespace 'UNITS.Server.Database'.
Using project directory 'S:\herdo-opensource\HoU UNITS\src\Server.Database\'.
Finding design-time services for provider 'Microsoft.EntityFrameworkCore.SqlServer'...
Using design-time services from provider 'Microsoft.EntityFrameworkCore.SqlServer'.
Finding design-time services referenced by assembly 'UNITS.Server.Database'.
No referenced design-time services were found.

---> These two lines are important:
Finding IDesignTimeServices implementations in assembly 'UNITS.Server.Database'...
Using design-time services from class 'ScaffoldingDesignTimeServices'.

Looking at the source code of the pipeline in the Microsoft.EntityFrameworkCore.Design.Internal.DesignTimeServicesBuilder class the IDesignTimeServices implementations are searched for in three steps:

  1. ConfigureProviderServices --> which would be the EF Core Power Tools DACPAC provider in the scenario above.
  2. ConfigureReferencedServices --> probably none?
  3. ConfigureUserServices --> this is not working with EF Core Power Tools

ConfigureUserServices however is the important part. This method is loading the IDesignTimeServices implementation from the startup project:

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())
        .FirstOrDefault();
    if (designTimeServicesType == null)
    {
        _reporter.WriteVerbose(DesignStrings.NoDesignTimeServices);
        return;
    }

    _reporter.WriteVerbose(DesignStrings.UsingDesignTimeServices(designTimeServicesType.ShortDisplayName()));
    ConfigureDesignTimeServices(designTimeServicesType, services);
}

Therefore, I assume there’s an issue that EF Core Power Tools is not passing the startup assembly correctly to the scaffold pipeline.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:10 (10 by maintainers)

github_iconTop GitHub Comments

2reactions
ErikEJcommented, Oct 7, 2020

This can be done with the command line tooling, not something I plan to implement.

1reaction
ErikEJcommented, Jul 19, 2020

This is not something I plan to support, too many pits of failure.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Entity Framework Core - No design-time services were found
Using design-time services from provider Microsoft.EntityFrameworkCore.SqlServer. Finding design-time services referenced by assembly BM.Server.
Read more >
Scaffolding (Reverse Engineering) - EF Core
Reverse engineering a model from an existing database using Entity Framework Core.
Read more >
Design-time services - EF Core
Generates OnConfiguring code. The main class for scaffolding reverse engineered models. Creates a model from a database model.
Read more >
Entity Framework Core Extension tips & tricks - Design Time ...
Entity Framework is a powerful tool with so many features it's hard to know them all. Sometimes you might want it to do...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found