{"id":41403,"date":"2022-08-09T10:01:37","date_gmt":"2022-08-09T17:01:37","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=41403"},"modified":"2024-12-13T15:11:38","modified_gmt":"2024-12-13T23:11:38","slug":"announcing-ef7-preview7-entity-framework","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-ef7-preview7-entity-framework\/","title":{"rendered":"Announcing Entity Framework 7 Preview 7: Interceptors!"},"content":{"rendered":"<p>Entity Framework Core 7 (EF7) Preview 7 has shipped with support for several new interceptors, as well as improvements to existing interceptors. This blog post will cover these changes, but Preview 7 also contains several additional enhancements, including:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/dotnet\/efcore\/issues\/3864\">Unidirectional many-to-many relationships<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/efcore\/issues\/28195\">Configure columns for properties mapped to multiple tables using TPT, TPC, and entity splitting<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/efcore\/issues\/28104\">Translate statistics aggregate functions on SQL Server<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/efcore\/issues\/27493\">Filtered Include for hidden navigations<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/efcore\/issues\/22667\">Make it easier to pass cancellation tokens to FindAsync<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/efcore\/issues\/2981\">Translate string.Join and string.Concat<\/a><\/li>\n<\/ul>\n<p>See the <a href=\"https:\/\/github.com\/dotnet\/efcore\/issues?q=is%3Aissue+milestone%3A7.0.0-preview7+is%3Aclosed\">full list of EF7 Preview 7 changes<\/a> on GitHub.<\/p>\n<h2>Interceptors<\/h2>\n<p>EF Core interceptors enable interception, modification, and\/or suppression of EF Core operations. This includes low-level database operations such as executing a command, as well as higher-level operations, such as calls to <code>SaveChanges<\/code>. Interceptors support async operations and the ability to modify or suppress operations, making them more powerful than traditional events, logging, and diagnostics.<\/p>\n<p>Interceptors are registered when configuring a DbContext instance, either in <code>OnConfiguring<\/code> or <code>AddDbContext<\/code>. For example:<\/p>\n<pre><code class=\"language-csharp\">public class ExampleContext : DbContext\r\n{\r\n    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)\r\n        =&gt; optionsBuilder.AddInterceptors(new TaggedQueryCommandInterceptor());\r\n}<\/code><\/pre>\n<blockquote><p>Code for all the examples in this post is <a href=\"https:\/\/github.com\/ajcvickers\/EF7Interceptors\">available on GitHub<\/a>.<\/p><\/blockquote>\n<h3>New and improved interceptors in EF7<\/h3>\n<p>EF7 includes the following enhancements to interceptors:<\/p>\n<ul>\n<li>Interception for <a href=\"https:\/\/github.com\/dotnet\/efcore\/issues\/15911\">creating and populating new entity instances<\/a> (aka &#8220;materialization&#8221;)<\/li>\n<li>Interception to <a href=\"https:\/\/github.com\/dotnet\/efcore\/issues\/28505\">modify the LINQ expression tree<\/a> before a query is compiled<\/li>\n<li>Interception for <a href=\"https:\/\/github.com\/dotnet\/efcore\/issues\/28315\">optimistic concurrency handling<\/a> (<code>DbUpdateConcurrencyException<\/code>)<\/li>\n<li>Interception for <a href=\"https:\/\/github.com\/dotnet\/efcore\/issues\/23085\">connections <em>before<\/em> checking if the connection string has been set<\/a><\/li>\n<li>Interception for when EF Core has <a href=\"https:\/\/github.com\/dotnet\/efcore\/issues\/23535\">finished consuming a result set<\/a>, but before that result set is closed<\/li>\n<li>Interception for <a href=\"https:\/\/github.com\/dotnet\/efcore\/issues\/23087\">creation of a <code>DbConnection<\/code> by EF Core<\/a><\/li>\n<li>Interception for <a href=\"https:\/\/github.com\/dotnet\/efcore\/issues\/17261\"><code>DbCommand<\/code> after it has been initialized<\/a><\/li>\n<\/ul>\n<p>In addition, EF7 includes new traditional .NET events for:<\/p>\n<ul>\n<li>When an <a href=\"https:\/\/github.com\/dotnet\/efcore\/issues\/27093\">entity is about to be tracked or change state<\/a>, but before it is actually tracked or change state<\/li>\n<li>Before and after EF Core <a href=\"https:\/\/github.com\/dotnet\/efcore\/issues\/26506\">detects changes to entities and properties<\/a> (aka <code>DetectChanges<\/code> interception)<\/li>\n<\/ul>\n<p>The following sections show some examples of using these new interception capabilities.<\/p>\n<h3>Simple actions on entity creation<\/h3>\n<p>The new <a href=\"https:\/\/github.com\/dotnet\/efcore\/blob\/main\/src\/EFCore\/Diagnostics\/IMaterializationInterceptor.cs\">IMaterializationInterceptor<\/a> supports interception before and after an entity instance is created, and before and after properties of that instance are initialized. The interceptor can change or replace the entity instance at each point. This allows:<\/p>\n<ul>\n<li>Setting unmapped properties or calling methods needed for validation, computed values, or flags<\/li>\n<li>Using a factory to create instances<\/li>\n<li>Creating a different entity instance than EF would normally create, such as an instance from a cache, or of a proxy type<\/li>\n<li>Injecting services into an entity instance<\/li>\n<\/ul>\n<p>For example, imagine that we want to keep track of the time that an entity was retrieved from the database, perhaps so it can be displayed to a user editing the data. To accomplish this, we first define an interface:<\/p>\n<pre><code class=\"language-csharp\">public interface IHasRetrieved\r\n{\r\n    DateTime Retrieved { get; set; }\r\n}<\/code><\/pre>\n<p>Using an interface is common with interceptors since it allows the same interceptor to work with many different entity types. For example:<\/p>\n<pre><code class=\"language-csharp\">public class Customer : IHasRetrieved\r\n{\r\n    public int Id { get; set; }\r\n    public string Name { get; set; } = null!;\r\n    public string? PhoneNumber { get; set; }\r\n\r\n    [NotMapped]\r\n    public DateTime Retrieved { get; set; }\r\n}<\/code><\/pre>\n<p>Notice that the <code>[NotMapped]<\/code> attribute is used to indicate that this property is used only while working with the entity, and should not be persisted to the database.<\/p>\n<p>The interceptor must then implement the appropriate method from <code>IMaterializationInterceptor<\/code> and set the time retrieved:<\/p>\n<pre><code class=\"language-csharp\">public class SetRetrievedInterceptor : IMaterializationInterceptor\r\n{\r\n    public object InitializedInstance(MaterializationInterceptionData materializationData, object instance)\r\n    {\r\n        if (instance is IHasRetrieved hasRetrieved)\r\n        {\r\n            hasRetrieved.Retrieved = DateTime.UtcNow;\r\n        }\r\n\r\n        return instance;\r\n    }\r\n}<\/code><\/pre>\n<p>An instance of this interceptor is registered when configuring the <code>DbContext<\/code>:<\/p>\n<pre><code class=\"language-csharp\">\r\npublic class CustomerContext : DbContext\r\n{\r\n    private static readonly SetRetrievedInterceptor _setRetrievedInterceptor = new();\r\n\r\n    public DbSet&lt;Customer&gt; Customers =&gt; Set&lt;Customer&gt;();\r\n\r\n    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) \r\n        =&gt; optionsBuilder\r\n            .AddInterceptors(_setRetrievedInterceptor)\r\n            .UseSqlite(\"Data Source = customers.db\");\r\n}<\/code><\/pre>\n<blockquote><p>\ud83d\udca1<strong>TIP<\/strong>\nThis interceptor is stateless, which is common, so a single instance is created and shared between all <code>DbContext<\/code> instances.<\/p><\/blockquote>\n<p>Now, whenever a <code>Customer<\/code> is queried from the database, the <code>Retrieved<\/code> property will be set automatically. For example:<\/p>\n<pre><code class=\"language-csharp\">using (var context = new CustomerContext())\r\n{\r\n    var customer = context.Customers.Single(e =&gt; e.Name == \"Alice\");\r\n    Console.WriteLine($\"Customer '{customer.Name}' was retrieved at '{customer.Retrieved.ToLocalTime()}'\");\r\n}<\/code><\/pre>\n<p>Produces output:<\/p>\n<pre><code class=\"language-bash\">Customer 'Alice' was retrieved at '8\/6\/2022 7:50:25 PM'<\/code><\/pre>\n<h3>LINQ expression tree interception<\/h3>\n<p>EF Core makes use of .NET LINQ queries. This typically involves using the C#, VB, or F# compiler to build an expression tree which is then translated by EF Core into the appropriate SQL. For example, consider a method that returns a page of customers:<\/p>\n<pre><code class=\"language-csharp\">List&lt;Customer&gt; GetPageOfCustomers(string sortProperty, int page)\r\n{\r\n    using var context = new CustomerContext();\r\n\r\n    return context.Customers\r\n        .OrderBy(e =&gt; EF.Property&lt;object&gt;(e, sortProperty))\r\n        .Skip(page * 20).Take(20).ToList();\r\n}<\/code><\/pre>\n<blockquote><p>\ud83d\udca1<strong>TIP<\/strong>\nThis query uses the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/microsoft.entityframeworkcore.ef.property\"><code>EF.Property<\/code><\/a> method to specify the property to sort by. This allows the application to dynamically pass in the property name, easily allowing sorting by any property of the entity type.<\/p><\/blockquote>\n<p>This will work fine as long as the property used for sorting always returns a stable ordering. But this may not always be the case. For example, the LINQ query above generates the following on SQLite when ordering by <code>Customer.City<\/code>:<\/p>\n<pre><code class=\"language-sql\">SELECT \"c\".\"Id\", \"c\".\"City\", \"c\".\"Name\", \"c\".\"PhoneNumber\"\r\nFROM \"Customers\" AS \"c\"\r\nORDER BY \"c\".\"City\"\r\nLIMIT @__p_1 OFFSET @__p_0<\/code><\/pre>\n<p>Uf there are multiple customers with the same <code>City<\/code>, then the ordering of this query is not stable. This could lead to missing or duplicate results as the user pages through the data.<\/p>\n<p>A common way to fix this problem is to perform a secondary sorting by primary key. However, rather than manually adding this to every query, we can intercept the query expression tree and add the secondary ordering dynamically whenever an <code>OrderBy<\/code> is encountered. To facilitate this, we will again use an interface, this time for any entity that has an integer primary key:<\/p>\n<pre><code class=\"language-csharp\">public interface IHasIntKey\r\n{\r\n    int Id { get; }\r\n}<\/code><\/pre>\n<p>This interface is implemented by the entity types of interest:<\/p>\n<pre><code class=\"language-csharp\">public class Customer : IHasIntKey\r\n{\r\n    public int Id { get; set; }\r\n    public string Name { get; set; } = null!;\r\n    public string? City { get; set; }\r\n    public string? PhoneNumber { get; set; }\r\n}<\/code><\/pre>\n<p>We then need an interceptor that implements <a href=\"https:\/\/github.com\/dotnet\/efcore\/blob\/main\/src\/EFCore\/Diagnostics\/IQueryExpressionInterceptor.cs\"><code>IQueryExpressionInterceptor<\/code><\/a>:<\/p>\n<pre><code class=\"language-csharp\">public class KeyOrderingExpressionInterceptor : IQueryExpressionInterceptor\r\n{\r\n    public Expression ProcessingQuery(Expression queryExpression, QueryExpressionEventData eventData)\r\n        =&gt; new KeyOrderingExpressionVisitor().Visit(queryExpression);\r\n\r\n    private class KeyOrderingExpressionVisitor : ExpressionVisitor\r\n    {\r\n        private static readonly MethodInfo ThenByMethod\r\n            = typeof(Queryable).GetMethods()\r\n                .Single(m =&gt; m.Name == nameof(Queryable.ThenBy) &amp;&amp; m.GetParameters().Length == 2);\r\n\r\n        protected override Expression VisitMethodCall(MethodCallExpression? methodCallExpression)\r\n        {\r\n            var methodInfo = methodCallExpression!.Method;\r\n            if (methodInfo.DeclaringType == typeof(Queryable)\r\n                &amp;&amp; methodInfo.Name == nameof(Queryable.OrderBy)\r\n                &amp;&amp; methodInfo.GetParameters().Length == 2)\r\n            {\r\n                var sourceType = methodCallExpression.Type.GetGenericArguments()[0];\r\n                if (typeof(IHasIntKey).IsAssignableFrom(sourceType))\r\n                {\r\n                    var lambdaExpression = (LambdaExpression)((UnaryExpression)methodCallExpression.Arguments[1]).Operand;\r\n                    var entityParameterExpression = lambdaExpression.Parameters[0];\r\n\r\n                    return Expression.Call(\r\n                        ThenByMethod.MakeGenericMethod(\r\n                            sourceType,\r\n                            typeof(int)),\r\n                        methodCallExpression,\r\n                        Expression.Lambda(\r\n                            typeof(Func&lt;,&gt;).MakeGenericType(entityParameterExpression.Type, typeof(int)),\r\n                            Expression.Property(entityParameterExpression, nameof(IHasIntKey.Id)),\r\n                            entityParameterExpression));\r\n                }\r\n            }\r\n\r\n            return base.VisitMethodCall(methodCallExpression);\r\n        }\r\n    }\r\n}<\/code><\/pre>\n<p>This probably looks pretty complicated&#8211;and it is! Working with expression trees is typically not easy. Let&#8217;s look at what&#8217;s happening:<\/p>\n<ul>\n<li>Fundamentally, the interceptor encapsulates an <a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.linq.expressions.expressionvisitor\"><code>ExpressionVisitor<\/code><\/a>. The visitor overrides <code>VisitMethodCall<\/code>, which will be called whenever there is a call to a method in the query expression tree.<\/li>\n<li>The visitor checks whether or not this is a call to the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.linq.queryable.orderby\"><code>Queryable.OrderBy<\/code><\/a> method we are interested in.<\/li>\n<li>If it is, then the visitor further checks when the generic method call is for a type that implements our <code>IHasIntKey<\/code> interface.<\/li>\n<li>At this point we know that the method call is of the form <code>OrderBy(e =&gt; ...)<\/code>. We extract the lambda expression from this call and get the parameter used in that expression&#8211;that is, the <code>e<\/code>.<\/li>\n<li>We now build a new <code>MethodCallExpression<\/code> using the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.linq.expressions.expression.call\"><code>Expression.Call<\/code><\/a> builder method. In this case, the method being called is <code>ThenBy(e =&gt; e.Id)<\/code>. We build this using the parameter extracted above and a property access to the <code>Id<\/code> property of the <code>IHasIntKey<\/code> interface.<\/li>\n<li>The input into this call is the original <code>OrderBy(e =&gt; ...)<\/code>, and so the end result is an expression for <code>OrderBy(e =&gt; ...).ThenBy(e =&gt; e.Id)<\/code>.<\/li>\n<li>This modified expression is returned from the visitor, which means the LINQ query has now been appropriately modified to include a <code>ThenBy<\/code> call.<\/li>\n<li>EF Core continues and compiles this query expression into the appropriate SQL for the database being used.<\/li>\n<\/ul>\n<p>This interceptor is registered in the same way as we did for the first example. Executing <code>GetPageOfCustomers<\/code> now generates the following SQL:<\/p>\n<pre><code class=\"language-sql\">SELECT \"c\".\"Id\", \"c\".\"City\", \"c\".\"Name\", \"c\".\"PhoneNumber\"\r\nFROM \"Customers\" AS \"c\"\r\nORDER BY \"c\".\"City\", \"c\".\"Id\"\r\nLIMIT @__p_1 OFFSET @__p_0<\/code><\/pre>\n<p>This will now always produce a stable ordering, even if there are multiple customers with the same <code>City<\/code>.<\/p>\n<p>Phew! That&#8217;s a lot of code to make a simple change to a query. And even worse, it might not even work for all queries. It is notoriously difficult write an expression visitor that recognizes all the query shapes it should, and none of the ones it should not. For example, this will likely not work if the ordering is done in a subquery.<\/p>\n<p>This brings us to a critical point about interceptors&#8211;always ask yourself if there is an easier way of doing what you want. Interceptors are powerful, but it&#8217;s easy to get things wrong. They are, as the saying goes, an easy way to shoot yourself in the foot.<\/p>\n<p>For example, imagine if we instead changed our <code>GetPageOfCustomers<\/code> method like so:<\/p>\n<pre><code class=\"language-csharp\">List&lt;Customer&gt; GetPageOfCustomers2(string sortProperty, int page)\r\n{\r\n    using var context = new CustomerContext();\r\n\r\n    return context.Customers\r\n        .OrderBy(e =&gt; EF.Property&lt;object&gt;(e, sortProperty))\r\n        .ThenBy(e =&gt; e.Id)\r\n        .Skip(page * 20).Take(20).ToList();\r\n}<\/code><\/pre>\n<p>In this case the <code>ThenBy<\/code> is simply added to the query. Yes, it may need to be done separately to every query, but it&#8217;s simple, easy to understand, and will always work.<\/p>\n<h3>Optimistic concurrency interception<\/h3>\n<p>EF Core supports the <a href=\"https:\/\/docs.microsoft.com\/ef\/core\/saving\/concurrency\">optimistic concurrency pattern<\/a> by checking that the number of rows actually affected by an update or delete is the same as the number of rows expected to be affected. This is often coupled with a concurrency token; that is, a column value that will only match its expected value if the row has not been updated since the expected value was read.+<\/p>\n<p>EF signals a violation of optimistic concurrency by throwing a <a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/microsoft.entityframeworkcore.dbupdateconcurrencyexception\"><code>DbUpdateConcurrencyException<\/code><\/a>. In EF7 <a href=\"https:\/\/github.com\/dotnet\/efcore\/blob\/main\/src\/EFCore\/Diagnostics\/ISaveChangesInterceptor.cs\"><code>ISaveChangesInterceptor<\/code><\/a> has new methods <code>ThrowingConcurrencyException<\/code> and <code>ThrowingConcurrencyExceptionAsync<\/code> that are called before the <code>DbUpdateConcurrencyException<\/code> is thrown. These interception points allow the exception to be suppressed, possibly coupled with async database changes to resolve the violation.<\/p>\n<p>For example, if two requests attempt to delete the same entity at almost the same time, then the second delete may fail because the row in the database no longer exists. This may be fine&#8211;the end result is that the entity has been deleted anyway. The following interceptor demonstrates how this can be done:<\/p>\n<pre><code class=\"language-csharp\">public class SuppressDeleteConcurrencyInterceptor : ISaveChangesInterceptor\r\n{\r\n    public InterceptionResult ThrowingConcurrencyException(\r\n        ConcurrencyExceptionEventData eventData,\r\n        InterceptionResult result)\r\n    {\r\n        if (eventData.Entries.All(e =&gt; e.State == EntityState.Deleted))\r\n        {\r\n            Console.WriteLine(\"Suppressing Concurrency violation for command:\");\r\n            Console.WriteLine(((RelationalConcurrencyExceptionEventData) eventData).Command.CommandText);\r\n\r\n            return InterceptionResult.Suppress();\r\n        }\r\n\r\n        return result;\r\n    }\r\n\r\n    public ValueTask&lt;InterceptionResult&gt; ThrowingConcurrencyExceptionAsync(\r\n        ConcurrencyExceptionEventData eventData,\r\n        InterceptionResult result,\r\n        CancellationToken cancellationToken = default)\r\n        =&gt; new(ThrowingConcurrencyException(eventData, result));\r\n}<\/code><\/pre>\n<p>There are several things worth noting about this interceptor:<\/p>\n<ul>\n<li>Both the synchronous and asynchronous interception methods are implemented. This is important if the application may call either <code>SaveChanges<\/code> or <code>SaveChangesAsync<\/code>. However, if all application code is async, then only <code>ThrowingConcurrencyExceptionAsync<\/code> needs to be implemented. Likewise, if the application never uses synchronous database methods, then only <code>ThrowingConcurrencyException<\/code> needs to be implemented. This is generally true for all interceptors with sync and async methods. (It might be worthwhile implementing the method your application does not use to throw, just in case some sync\/async code creeps in.)<\/li>\n<li>The interceptor has access to <a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/microsoft.entityframeworkcore.changetracking.entityentry-1\"><code>EntityEntry<\/code><\/a> objects for the entities being saved. In this case, this is used to check whether or not the concurrency violation is happening for a delete operation.<\/li>\n<li>If the application is using a relational database provider, then the <a href=\"https:\/\/github.com\/dotnet\/efcore\/blob\/main\/src\/EFCore\/Diagnostics\/ConcurrencyExceptionEventData.cs\"><code>ConcurrencyExceptionEventData<\/code><\/a> object can be cast to a <a href=\"https:\/\/github.com\/dotnet\/efcore\/blob\/main\/src\/EFCore.Relational\/Diagnostics\/RelationalConcurrencyExceptionEventData.cs\"><code>RelationalConcurrencyExceptionEventData<\/code><\/a> object. This provides additional, relational-specific information about the database operation being performed. In this case, the relation command text is printed to the console.<\/li>\n<li>Returning <code>InterceptionResult.Suppress()<\/code> tells EF Core to suppress the action it was about to take&#8211;in this case, throwing the <code>DbUpdateConcurrencyException<\/code>. This is one of the most powerful features of interceptors to <em>change the behavior if EF Core<\/em>, rather than just observing what EF Core is doing.<\/li>\n<\/ul>\n<h3>Summary<\/h3>\n<p>EF Core 7.0 (EF7) adds several new interceptors and enhances the behavior of already existing interceptors. Interceptors allow application react to what EF is doing in a similar way to events. However, interceptors also allow the behavior of EF to be changed by suppressing or replacing the action that EF was going to perform, or by modifying or replacing the result of that action. Where appropriate, interceptors can perform async database access, allowing complete database commands to be changed by an interceptor.<\/p>\n<p>For more information of EF Core interceptors, see <a href=\"https:\/\/docs.microsoft.com\/ef\/core\/logging-events-diagnostics\/interceptors\"><em>Interceptors<\/em><\/a> in the EF Core documentation.<\/p>\n<p>Also, the EF Team showed some additional in-depth interceptor examples in an episode of the .NET Data Community Standup&#8211;available to <a href=\"https:\/\/youtu.be\/EJs3sSetr2Q\">watch now on YouTube<\/a>.<\/p>\n<p>Finally, don&#8217;t forget that all the code in this post is <a href=\"https:\/\/github.com\/ajcvickers\/EF7Interceptors\">available on GitHub<\/a>.<\/p>\n<h2>EF7 Prerequisites<\/h2>\n<ul>\n<li>EF7 targets .NET 6, which means it can be used on .NET 6 (LTS) or .NET 7.<\/li>\n<li>EF7 will not run on .NET Framework.<\/li>\n<\/ul>\n<p>EF7 is the successor to EF Core 6.0, not to be confused with <a href=\"https:\/\/github.com\/dotnet\/ef6\">EF6<\/a>. If you are considering upgrading from EF6, please read our guide to <a href=\"https:\/\/docs.microsoft.com\/ef\/efcore-and-ef6\/porting\/\">port from EF6 to EF Core<\/a>.<\/p>\n<h2>How to get EF7 previews<\/h2>\n<p>EF7 is distributed exclusively as a set of NuGet packages. For example, to add the SQL Server provider to your project, you can use the following command using the dotnet tool:<\/p>\n<pre><code class=\"language-bash\">dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 7.0.0-preview.7.22376.2<\/code><\/pre>\n<p>This following table links to the preview 7 versions of the EF Core packages and describes what they are used for.<\/p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align: right;\"><strong>Package<\/strong><\/th>\n<th style=\"text-align: left;\"><strong>Purpose<\/strong><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align: right;\"><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore\/7.0.0-preview.7.22376.2\">Microsoft.EntityFrameworkCore<\/a><\/td>\n<td style=\"text-align: left;\">The main EF Core package that is independent of specific database providers<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: right;\"><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.SqlServer\/7.0.0-preview.7.22376.2\">Microsoft.EntityFrameworkCore.SqlServer<\/a><\/td>\n<td style=\"text-align: left;\">Database provider for Microsoft SQL Server and SQL Azure<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: right;\"><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite\/7.0.0-preview.7.22376.2\">Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite<\/a><\/td>\n<td style=\"text-align: left;\">SQL Server support for spatial types<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: right;\"><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.Sqlite\/7.0.0-preview.7.22376.2\">Microsoft.EntityFrameworkCore.Sqlite<\/a><\/td>\n<td style=\"text-align: left;\">Database provider for SQLite that includes the native binary for the database engine<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: right;\"><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.Sqlite.Core\/7.0.0-preview.7.22376.2\">Microsoft.EntityFrameworkCore.Sqlite.Core<\/a><\/td>\n<td style=\"text-align: left;\">Database provider for SQLite <em>without<\/em> a packaged native binary<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: right;\"><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite\/7.0.0-preview.7.22376.2\">Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite<\/a><\/td>\n<td style=\"text-align: left;\">SQLite support for spatial types<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: right;\"><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.Cosmos\/7.0.0-preview.7.22376.2\">Microsoft.EntityFrameworkCore.Cosmos<\/a><\/td>\n<td style=\"text-align: left;\">Database provider for Azure Cosmos DB<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: right;\"><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.InMemory\/7.0.0-preview.7.22376.2\">Microsoft.EntityFrameworkCore.InMemory<\/a><\/td>\n<td style=\"text-align: left;\">The in-memory database provider<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: right;\"><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.Tools\/7.0.0-preview.7.22376.2\">Microsoft.EntityFrameworkCore.Tools<\/a><\/td>\n<td style=\"text-align: left;\">EF Core PowerShell commands for the Visual Studio Package Manager Console; use this to integrate tools like <a href=\"https:\/\/docs.microsoft.com\/ef\/core\/managing-schemas\/scaffolding\">scaffolding<\/a> and <a href=\"https:\/\/docs.microsoft.com\/ef\/core\/managing-schemas\/migrations\/\">migrations<\/a> with Visual Studio<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: right;\"><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.Design\/7.0.0-preview.7.22376.2\">Microsoft.EntityFrameworkCore.Design<\/a><\/td>\n<td style=\"text-align: left;\">Shared design-time components for EF Core tools<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: right;\"><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.Proxies\/7.0.0-preview.7.22376.2\">Microsoft.EntityFrameworkCore.Proxies<\/a><\/td>\n<td style=\"text-align: left;\">Lazy-loading and change-tracking proxies<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: right;\"><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.Abstractions\/7.0.0-preview.7.22376.2\">Microsoft.EntityFrameworkCore.Abstractions<\/a><\/td>\n<td style=\"text-align: left;\">Decoupled EF Core abstractions; use this for features like extended data annotations defined by EF Core<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: right;\"><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.Relational\/7.0.0-preview.7.22376.2\">Microsoft.EntityFrameworkCore.Relational<\/a><\/td>\n<td style=\"text-align: left;\">Shared EF Core components for relational database providers<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: right;\"><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.Analyzers\/7.0.0-preview.7.22376.2\">Microsoft.EntityFrameworkCore.Analyzers<\/a><\/td>\n<td style=\"text-align: left;\">C# analyzers for EF Core<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>We also published the 7.0 preview 7 release of the <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.Data.Sqlite.Core\/7.0.0-preview.7.22376.2\">Microsoft.Data.Sqlite.Core<\/a> provider for <a href=\"https:\/\/docs.microsoft.com\/dotnet\/framework\/data\/adonet\/ado-net-overview\">ADO.NET<\/a>.<\/p>\n<h2>Installing the EF7 Command Line Interface (CLI)<\/h2>\n<p>Before you can execute EF7 Core migration or scaffolding commands, you&#8217;ll have to install the CLI package as either a global or local tool.<\/p>\n<p>To install the preview tool globally, install with:<\/p>\n<pre><code class=\"language-bash\">dotnet tool install --global dotnet-ef --version 7.0.0-preview.7.22376.2 <\/code><\/pre>\n<p>If you already have the tool installed, you can upgrade it with the following command:<\/p>\n<pre><code class=\"language-bash\">dotnet tool update --global dotnet-ef --version 7.0.0-preview.7.22376.2 <\/code><\/pre>\n<p>It&#8217;s possible to use this new version of the EF7 CLI with projects that use older versions of the EF Core runtime.<\/p>\n<h2>Daily builds<\/h2>\n<p>EF7 previews are aligned with .NET 7 previews. These previews tend to lag behind the latest work on EF7. Consider using the <a href=\"https:\/\/github.com\/aspnet\/AspNetCore\/blob\/master\/docs\/DailyBuilds.md\">daily builds<\/a> instead to get the most up-to-date EF7 features and bug fixes.<\/p>\n<p>As with the previews, the daily builds require .NET 6.<\/p>\n<h2>The .NET Data Community Standup<\/h2>\n<p>The .NET data team is now live streaming every other Wednesday at 10am Pacific Time, 1pm Eastern Time, or 17:00 UTC. Join the stream to ask questions about the data-related topic of your choice, including the latest preview release.<\/p>\n<ul>\n<li><a href=\"https:\/\/aka.ms\/efstandups\">Watch our YouTube playlist<\/a> of previous shows<\/li>\n<li><a href=\"https:\/\/live.dot.net\">Visit the .NET Community Standup<\/a> page to preview upcoming shows<\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/efcore\/issues\/22700\">Submit your ideas<\/a> for a guest, product, demo, or other content to cover<\/li>\n<\/ul>\n<h2>Documentation and Feedback<\/h2>\n<p>The starting point for all EF Core documentation is <a href=\"https:\/\/docs.microsoft.com\/ef\/\">docs.microsoft.com\/ef\/<\/a>.<\/p>\n<p>Please file issues found and any other feedback on the <a href=\"https:\/\/github.com\/dotnet\/efcore\">dotnet\/efcore GitHub repo<\/a>.<\/p>\n<h2>Helpful Links<\/h2>\n<p>The following links are provided for easy reference and access.<\/p>\n<ul>\n<li><a href=\"https:\/\/aka.ms\/efstandups\">EF Core Community Standup Playlist: https:\/\/aka.ms\/efstandups<\/a><\/li>\n<li><a href=\"https:\/\/aka.ms\/efdocs\">Main documentation: https:\/\/aka.ms\/efdocs<\/a><\/li>\n<li><a href=\"https:\/\/aka.ms\/efcorefeedback\">Issues and feature requests for EF Core: https:\/\/aka.ms\/efcorefeedback<\/a><\/li>\n<li><a href=\"https:\/\/aka.ms\/efroadmap\">Entity Framework Roadmap: https:\/\/aka.ms\/efroadmap<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/efcore\/issues\/27185\">Bi-weekly updates: https:\/\/github.com\/dotnet\/efcore\/issues\/27185<\/a><\/li>\n<\/ul>\n<h2>Thank you from the team<\/h2>\n<p>A big thank you from the EF team to everyone who has used and contributed to EF over the years!<\/p>\n<p>Welcome to EF7.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Announcing EF7 Preview 7 with new and improved interceptors, and so much more!<\/p>\n","protected":false},"author":13047,"featured_media":41404,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,859],"tags":[4,30,7265],"class_list":["post-41403","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-entity-framework","tag-net","tag-announcement","tag-announcements"],"acf":[],"blog_post_summary":"<p>Announcing EF7 Preview 7 with new and improved interceptors, and so much more!<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/41403","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/13047"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=41403"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/41403\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/41404"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=41403"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=41403"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=41403"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}