dotnet / dotnet/EntityFramework.Docs

Cleanup and document how to use options extensions

Open
#4,085 1 comment 1 reaction 0 assignees View on GitHub
Dominant language
Mermaid
Stars
1.7k
Forks
2k
Avg merge
7d 23h
Merged PRs (30d)
16

Description

Hi all.

From my understanding of how this piece of code works,
`DbContextOptions.WithExtension(TExtension instance)` is supposed to replace the provided extension, if present, and return a new options object.

However, the [the implementation](https://github.com/dotnet/efcore/blob/809fe7a219c99fbe4f3576ef42bd084d4a6ce056/src/EFCore/DbContextOptions%60.cs#L54) is obtaining the type to replace from the generic parameter. i.e `extensions[typeof(TExtension)]`. I believe the intended behavior would be to use the instance type, i.e `extension.GetType()`

Take this unit test as an example:

```csharp
[Fact]
public void DbContextOptions_WithExtension_ShouldReplaceByInstanceType()
{
DbContextOptions dbContextOptions = new DbContextOptions();

SqliteOptionsExtension extension = new SqliteOptionsExtension();
dbContextOptions = dbContextOptions.WithExtension(extension);

RelationalOptionsExtension replaced = extension.WithMigrationsAssembly("MyMigrations");

Assert.IsType(replaced);
// This will be a WithExtension
// but on runtime, the instance will be of SqliteOptionsExtension type.
var newops = dbContextOptions.WithExtension(replaced);

// This assertion fails
Assert.Single(newops.Extensions);
}
```

I would expect the last call to `WithExtension` to have replaced the original `SqliteOptionsExtension`. However, as can be seen [on the implementation](https://github.com/dotnet/efcore/blob/809fe7a219c99fbe4f3576ef42bd084d4a6ce056/src/EFCore/DbContextOptions%60.cs#L54) the type of the extension is being obtained from the generic argument instead of the provided instance type.

A test where the generic argument is the same as the instance type does succeed

```csharp
[Fact]
public void DbContextOptions_WithExtension_ShouldReplaceByInstanceType_UsingReflection()
{
DbContextOptions dbContextOptions = new DbContextOptions();

SqliteOptionsExtension extension = new SqliteOptionsExtension();
dbContextOptions = dbContextOptions.WithExtension(extension);

RelationalOptionsExtension replaced = extension.WithMigrationsAssembly("MyMigrations");

Assert.IsType(replaced);

var genericMethod = dbContextOptions.GetType()
.GetMethod(nameof(dbContextOptions.WithExtension))
.MakeGenericMethod(new[] { extension.GetType() });

var newops = (DbContextOptions)genericMethod.Invoke(dbContextOptions, new[] { replaced });

Assert.Single(newops.Extensions);
}
```

The reason why I have faced this issue is since I would like to modify the `RelationalOptionsExtension` from a different place than where the `UseSqlite` or similar extension method is executed.

I'm not sure what the expected behavior is supposed to be, and I know that these are internal EFCore APIs, however I would have expected `WithExtension` to use the instance type from the runtime instance instead of the generic constraint being applied.

### Include provider and version information

EF Core version: 5.0.4
Target framework: NET 5.0.

Thank you all very much for your time!
Leo.

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.