dotnet / dotnet/EntityFramework.Docs
Cleanup and document how to use options extensions
- 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
Assessment
This issue has not been assessed yet.