dotnet / dotnet/efcore

Allow IDesignTimeDbContextFactory to be picked up via an assembly attribute

Open
#28,363 1 comment 1 reaction 0 assignees View on GitHub
area-tools customer-reported
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

### Problem Context

F# forces a strict ordering where the last function in the last file (and files cannot reference later files either) has to be the entrypoint.
When a design time factory needs to share code with other app startup concerns this requires an awkward weaving in F#.

The outline of the code is forced to go from the simple form:
```fs
module MyApp.Program

open ...
open ...

[]
let main args =
Host.CreateDefaultBuilder().ConfigureServices(...).Build.Run()
0

```

To this 'weaved' form, given F# also requires intra-file top to bottom definitions.

```fs
namespace MyApp

open ...
open ...

module Startup =
let configureServices ... = ...

type AppDesignTimeDbContextFactory() =
interface IDesignTimeDbContextFactory with
member _.CreateDbContext(args) =
// Change some config just for design time and pull out a DbContext.
Host.CreateDefaultBuilder().ConfigureServices(configureServices).Build().ApplicationServices // etc

module Program =
[]
let main args =
Host.CreateDefaultBuilder().ConfigureServices(Startup.configureServices).Build.Run()
0
```

And `AppDesignTimeDbContextFactory` cannot just be defined inside a module because the design time code doesn't (and IMO shouldn't) scan for nested types.

### Feature Request

As a solution I propose we add an assembly attribute `DesignTimeDbContextFactoryAttribute` that the design time tooling will try to find before attempting type scanning (which should still include support for enumerating multiple definitions of the attribute by filtering down to a matching interface instantiation candidate).

Allowing the code to stay like:

```fs
module MyApp.Program

open ...
open ...

let configureServices ... = ...

type AppDesignTimeDbContextFactory() =
interface IDesignTimeDbContextFactory with
member _.CreateDbContext(args) =
// Change some config just for design time and pull out a DbContext.
Host.CreateDefaultBuilder().ConfigureServices(configureServices).Build().ApplicationServices // etc

[)>]
do ()

[]
let main args =
Host.CreateDefaultBuilder().ConfigureServices(configureServices).Build.Run()
0
```

Or alternatively:

```fs
namespace MyApp

open ...
open ...

module Program =

let configureServices ... = ...

type AppDesignTimeDbContextFactory() =
interface IDesignTimeDbContextFactory with
member _.CreateDbContext(args) =
// Change some config just for design time and pull out a DbContext.
Host.CreateDefaultBuilder().ConfigureServices(configureServices).Build().ApplicationServices // etc

[]
let main args =
Host.CreateDefaultBuilder().ConfigureServices(configureServices).Build.Run()
0

[)>]
do ()
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.