Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add auto-compiled models to What's New #4701

Merged
merged 2 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
118 changes: 118 additions & 0 deletions entity-framework/core/what-is-new/ef-core-9.0/whatsnew.md
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,124 @@ protected override void Up(MigrationBuilder migrationBuilder)

## Model building

<a name="auto-compiled-models"></a>

### Auto-compiled models

> [!TIP]
> The code shown here comes from the [NewInEFCore9.CompiledModels](https://github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/Miscellaneous/NewInEFCore9.CompiledModels/) sample.

Compiled models can improve startup time for applications with large models--that is entity type counts in the 100s or 1000s. In previous versions of EF Core, a compiled model had to be generated manually, using the command line. For example:

```dotnetcli
dotnet ef dbcontext optimize
```

After running the command, a line like, `.UseModel(MyCompiledModels.BlogsContextModel.Instance)` must be added to `OnConfiguring` to tell EF Core to use the compiled model.

Starting with EF9, this `.UseModel` line is no longer needed. Instead, the compiled model will be detected and used automatically. This can be seen by having EF log whenever it is building the model. Running a simple application then shows EF building the model when the application starts:
ajcvickers marked this conversation as resolved.
Show resolved Hide resolved

```output
Starting application...
>> EF is building the model...
Model loaded model with 2 entity types.
```

The output from running `dotnet ef dbcontext optimize` on the model project is:

```output
PS D:\code\EntityFramework.Docs\samples\core\Miscellaneous\NewInEFCore9.CompiledModels\Model> dotnet ef dbcontext optimize

Build succeeded in 0.3s

Build succeeded in 0.3s
Build started...
Build succeeded.
>> EF is building the model...
>> EF is building the model...
Successfully generated a compiled model, it will be discovered automatically, but you can also call 'options.UseModel(BlogsContextModel.Instance)'. Run this command again when the model is modified.
PS D:\code\EntityFramework.Docs\samples\core\Miscellaneous\NewInEFCore9.CompiledModels\Model>
```

Notice that the log output indicates that the _model was built when running the command_. If we now run the application again, after rebuilding but without making any code changes, then the output is:

```output
Starting application...
Model loaded model with 2 entity types.
ajcvickers marked this conversation as resolved.
Show resolved Hide resolved
```

Notice that the model was not built when starting the application because the compiled model was detected and used automatically.

### MSBuild integration

With the above approach, the compiled model still needs to be regenerated manually when the entity types or `DbContext` configuration is changed. However, EF9 ships with MSBuild and targets package that can automatically update the compiled model when the model project is built! To get started, install the [Microsoft.EntityFrameworkCore.Tasks](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Tasks/) NuGet package. For example:

```dotnetcli
dotnet add package Microsoft.EntityFrameworkCore.Tasks --version 9.0.0-preview.4.24205.3
```

> [!TIP]
> Use the package version in the command above that matches the version of EF Core that you are using.

Then enable the integration by setting the `EFOptimizeContext` property to your `.csproj` file. For example:

```xml
<PropertyGroup>
<EFOptimizeContext>true</EFOptimizeContext>
</PropertyGroup>
```

There are additional, optional, MSBuild properties for controlling how the model is built, equivalent to the options passed on the command line to `dotnet ef dbcontext optimize`. These include:

| MSBuild property | Description |
|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| EFOptimizeContext | Set to `true` to enable auto-compiled models. |
| DbContextName | The DbContext class to use. Class name only or fully qualified with namespaces. If this option is omitted, EF Core will find the context class. If there are multiple context classes, this option is required. |
| EFStartupProject | Relative path to the project folder of the startup project. Default value is the current folder. |
ajcvickers marked this conversation as resolved.
Show resolved Hide resolved
| EFTargetNamespace | The namespace to use for all generated classes. Defaults to generated from the root namespace and the output directory plus CompiledModels. |

In our example, we need to specify the startup project:

```xml
<PropertyGroup>
<EFOptimizeContext>true</EFOptimizeContext>
<EFStartupProject>..\App\App.csproj</EFStartupProject>
</PropertyGroup>
```

Now, if we build the project, we can see logging at build time indicating that the compiled model is being built:

```output
Optimizing DbContext...
dotnet exec --depsfile D:\code\EntityFramework.Docs\samples\core\Miscellaneous\NewInEFCore9.CompiledModels\App\bin\Release\net8.0\App.deps.json
--additionalprobingpath G:\packages
--additionalprobingpath "C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages"
--runtimeconfig D:\code\EntityFramework.Docs\samples\core\Miscellaneous\NewInEFCore9.CompiledModels\App\bin\Release\net8.0\App.runtimeconfig.json G:\packages\microsoft.entityframeworkcore.tasks\9.0.0-preview.4.24205.3\tasks\net8.0\..\..\tools\netcoreapp2.0\ef.dll dbcontext optimize --output-dir D:\code\EntityFramework.Docs\samples\core\Miscellaneous\NewInEFCore9.CompiledModels\Model\obj\Release\net8.0\
--namespace NewInEfCore9
--suffix .g
--assembly D:\code\EntityFramework.Docs\samples\core\Miscellaneous\NewInEFCore9.CompiledModels\Model\bin\Release\net8.0\Model.dll --startup-assembly D:\code\EntityFramework.Docs\samples\core\Miscellaneous\NewInEFCore9.CompiledModels\App\bin\Release\net8.0\App.dll
--project-dir D:\code\EntityFramework.Docs\samples\core\Miscellaneous\NewInEFCore9.CompiledModels\Model
--root-namespace NewInEfCore9
--language C#
--nullable
--working-dir D:\code\EntityFramework.Docs\samples\core\Miscellaneous\NewInEFCore9.CompiledModels\App
--verbose
--no-color
--prefix-output
```

And running the application shows that the compiled model has been detected and hence the model is not built again:

```output
Starting application...
Model loaded model with 2 entity types.
```

Now, whenever the model changes, the compiled model will be automatically rebuilt as soon as the project is built.

> [NOTE!]
> We are working through some performance issues with changes made to the compiled model in EF8 and EF9. See [Issue 33483#](https://github.com/dotnet/efcore/issues/33483) for more information.

<a name="sequence-caching"></a>

### Specify caching for sequences
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace/>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0-preview.4.24205.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Model\Model.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using NewInEfCore9;

public class Program
{
public static void Main()
{
Console.WriteLine("Starting application...");

using var context = new BlogsContext();

Console.WriteLine($"Model loaded model with {context.Model.GetEntityTypes().Count()} entity types.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.EntityFrameworkCore.Diagnostics;

namespace NewInEfCore9;

public class BlogsContext : DbContext
{
public DbSet<Blog> Blogs => Set<Blog>();

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EF9CompiledModels;ConnectRetryCount=0")
.LogTo(_ => Console.WriteLine(">> EF is building the model..."), [CoreEventId.ShadowPropertyCreated])
.EnableSensitiveDataLogging();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace NewInEfCore9;

public class Blog
{
public int Id { get; set; }

public ICollection<Post> Posts { get; } = new List<Post>();
}

public class Post
{
public int Id { get; set; }
public string? Title { get; set; }

public Blog Blog { get; set; } = null!;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>library</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>NewInEfCore9</RootNamespace>
<LangVersion>Preview</LangVersion>
</PropertyGroup>

<PropertyGroup>
<EFOptimizeContext>true</EFOptimizeContext>
<EFStartupProject>..\App\App.csproj</EFStartupProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0-preview.4.24205.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.0-preview.4.24205.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0-preview.4.24205.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tasks" Version="9.0.0-preview.4.24205.3" />
</ItemGroup>

<ItemGroup>
<Using Include="System.ComponentModel.DataAnnotations" />
<Using Include="System.ComponentModel.DataAnnotations.Schema" />
<Using Include="System.Data.Common" />
<Using Include="System.Linq.Expressions" />
<Using Include="System.Reflection" />
<Using Include="System.Runtime.CompilerServices" />
<Using Include="Microsoft.EntityFrameworkCore" />
<Using Include="Microsoft.Extensions.Logging" />
<Using Include="Microsoft.Extensions.DependencyInjection" />
</ItemGroup>

</Project>
13 changes: 11 additions & 2 deletions samples/core/Miscellaneous/NewInEFCore9/ExecuteUpdateSample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,19 @@ public class CustomerContext(bool useSqlite = false) : DbContext

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> (UseSqlite
? optionsBuilder.UseSqlite(@$"DataSource={GetType().Name}.db")
? optionsBuilder.UseSqlite(@$"DataSource={GetType().Name}.db",
sqliteOptionsBuilder =>
{
sqliteOptionsBuilder.UseNetTopologySuite();
sqliteOptionsBuilder.CommandTimeout(0);
})
: optionsBuilder.UseSqlServer(
@$"Server=(localdb)\mssqllocaldb;Database={GetType().Name};ConnectRetryCount=0",
sqlServerOptionsBuilder => sqlServerOptionsBuilder.UseNetTopologySuite()))
sqlServerOptionsBuilder =>
{
sqlServerOptionsBuilder.UseNetTopologySuite();
sqlServerOptionsBuilder.CommandTimeout(0);
}))
.EnableSensitiveDataLogging()
.LogTo(Console.WriteLine, LogLevel.Information);

Expand Down
18 changes: 9 additions & 9 deletions samples/core/Miscellaneous/NewInEFCore9/NewInEFCore9.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Cosmos" Version="9.0.0-preview.3.24157.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0-preview.3.24157.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.0-preview.3.24157.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0-preview.3.24157.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite" Version="9.0.0-preview.3.24157.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.HierarchyId" Version="9.0.0-preview.3.24157.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite" Version="9.0.0-preview.3.24157.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.0-preview.3.24157.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.0-preview.3.24157.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Cosmos" Version="9.0.0-preview.4.24205.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0-preview.4.24205.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.0-preview.4.24205.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0-preview.4.24205.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite" Version="9.0.0-preview.4.24205.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.HierarchyId" Version="9.0.0-preview.4.24205.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite" Version="9.0.0-preview.4.24205.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.0-preview.4.24205.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.0-preview.4.24205.3" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
</ItemGroup>

Expand Down
17 changes: 17 additions & 0 deletions samples/core/Samples.sln
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NewInEFCore8", "Miscellaneo
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NewInEFCore9", "Miscellaneous\NewInEFCore9\NewInEFCore9.csproj", "{3F17B59C-7D33-4BF0-B386-ACFE76D45697}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NewInEFCore9.CompiledModels", "NewInEFCore9.CompiledModels", "{26184A10-7CF8-4ED2-9F70-E00A55BE063B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "Miscellaneous\NewInEFCore9.CompiledModels\App\App.csproj", "{22548A1B-0833-49E9-A04E-A7E8690EAE03}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model", "Miscellaneous\NewInEFCore9.CompiledModels\Model\Model.csproj", "{8E9DD759-B6D0-4424-BC6C-97ECE559CE02}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -547,6 +553,14 @@ Global
{3F17B59C-7D33-4BF0-B386-ACFE76D45697}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F17B59C-7D33-4BF0-B386-ACFE76D45697}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F17B59C-7D33-4BF0-B386-ACFE76D45697}.Release|Any CPU.Build.0 = Release|Any CPU
{22548A1B-0833-49E9-A04E-A7E8690EAE03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{22548A1B-0833-49E9-A04E-A7E8690EAE03}.Debug|Any CPU.Build.0 = Debug|Any CPU
{22548A1B-0833-49E9-A04E-A7E8690EAE03}.Release|Any CPU.ActiveCfg = Release|Any CPU
{22548A1B-0833-49E9-A04E-A7E8690EAE03}.Release|Any CPU.Build.0 = Release|Any CPU
{8E9DD759-B6D0-4424-BC6C-97ECE559CE02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8E9DD759-B6D0-4424-BC6C-97ECE559CE02}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8E9DD759-B6D0-4424-BC6C-97ECE559CE02}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8E9DD759-B6D0-4424-BC6C-97ECE559CE02}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -633,6 +647,9 @@ Global
{72C964FF-07C3-4234-B277-D91C3D83BAEC} = {85AFD7F1-6943-40FE-B8EC-AA9DBB42CCA6}
{F9CA30FF-70A9-4EA5-8F12-7B08A695AB8F} = {85AFD7F1-6943-40FE-B8EC-AA9DBB42CCA6}
{3F17B59C-7D33-4BF0-B386-ACFE76D45697} = {85AFD7F1-6943-40FE-B8EC-AA9DBB42CCA6}
{26184A10-7CF8-4ED2-9F70-E00A55BE063B} = {85AFD7F1-6943-40FE-B8EC-AA9DBB42CCA6}
{22548A1B-0833-49E9-A04E-A7E8690EAE03} = {26184A10-7CF8-4ED2-9F70-E00A55BE063B}
{8E9DD759-B6D0-4424-BC6C-97ECE559CE02} = {26184A10-7CF8-4ED2-9F70-E00A55BE063B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {20C98D35-54EF-46A6-8F3B-1855C1AE4F70}
Expand Down