The following API and behavior changes have the potential to break existing applications when upgrading them to 3.x.
Changes that we expect to only impact database providers are documented under provider changes.
Before 3.0, when EF Core couldn't convert an expression that was part of a query to either SQL or a parameter, it automatically evaluated the expression on the client.
By default, client evaluation of potentially expensive expressions only triggered a warning.
New behavior
Starting with 3.0, EF Core only allows expressions in the top-level projection (the last Select() call in the query) to be evaluated on the client.
When expressions in any other part of the query can't be converted to either SQL or a parameter, an exception is thrown.
Why
Automatic client evaluation of queries allows many queries to be executed even if important parts of them can't be translated.
This behavior can result in unexpected and potentially damaging behavior that may only become evident in production.
For example, a condition in a Where() call which can't be translated can cause all rows from the table to be transferred from the database server, and the filter to be applied on the client.
This situation can easily go undetected if the table contains only a few rows in development, but hit hard when the application moves to production, where the table may contain millions of rows.
Client evaluation warnings also proved too easy to ignore during development.
Besides this, automatic client evaluation can lead to issues in which improving query translation for specific expressions caused unintended breaking changes between releases.
Mitigations
If a query can't be fully translated, then either rewrite the query in a form that can be translated, or use AsEnumerable(), ToList(), or similar to explicitly bring data back to the client where it can then be further processed using LINQ-to-Objects.
Medium-impact changes
Entity Framework Core is no longer part of the ASP.NET Core shared framework
Before ASP.NET Core 3.0, when you added a package reference to Microsoft.AspNetCore.App or Microsoft.AspNetCore.All, it would include EF Core and some of the EF Core data providers like the SQL Server provider.
New behavior
Starting in 3.0, the ASP.NET Core shared framework doesn't include EF Core or any EF Core data providers.
Why
Before this change, getting EF Core required different steps depending on whether the application targeted ASP.NET Core and SQL Server or not.
Also, upgrading ASP.NET Core forced the upgrade of EF Core and the SQL Server provider, which isn't always desirable.
With this change, the experience of getting EF Core is the same across all providers, supported .NET implementations and application types.
Developers can also now control exactly when EF Core and EF Core data providers are upgraded.
Mitigations
To use EF Core in an ASP.NET Core 3.0 application or any other supported application, explicitly add a package reference to the EF Core database provider that your application will use.
The EF Core command-line tool, dotnet ef, is no longer part of the .NET Core SDK
Before 3.0, the dotnet ef tool was included in the .NET Core SDK and was readily available to use from the command line from any project without requiring extra steps.
New behavior
Starting in 3.0, the .NET SDK does not include the dotnet ef tool, so before you can use it you have to explicitly install it as a local or global tool.
Why
This change allows us to distribute and update dotnet ef as a regular .NET CLI tool on NuGet, consistent with the fact that the EF Core 3.0 is also always distributed as a NuGet package.
Mitigations
To be able to manage migrations or scaffold a DbContext, install dotnet-ef as a global tool:
dotnet tool install --global dotnet-ef
You can also obtain it a local tool when you restore the dependencies of a project that declares it as a tooling dependency using a tool manifest file.
Low-impact changes
FromSql, ExecuteSql, and ExecuteSqlAsync have been renamed
ExecuteSqlCommand and ExecuteSqlCommandAsync are deprecated. Use these methods instead.
Old behavior
Before EF Core 3.0, these method names were overloaded to work with either a normal string or a string that should be interpolated into SQL and parameters.
New behavior
Starting with EF Core 3.0, use FromSqlRaw, ExecuteSqlRaw, and ExecuteSqlRawAsync to create a parameterized query where the parameters are passed separately from the query string.
For example:
context.Products.FromSqlRaw(
"SELECT * FROM Products WHERE Name = {0}",
product.Name);
Use FromSqlInterpolated, ExecuteSqlInterpolated, and ExecuteSqlInterpolatedAsync to create a parameterized query where the parameters are passed as part of an interpolated query string.
For example:
context.Products.FromSqlInterpolated(
$"SELECT * FROM Products WHERE Name = {product.Name}");
Note that both of the queries above will produce the same parameterized SQL with the same SQL parameters.
Why
Method overloads like this make it very easy to accidentally call the raw string method when the intent was to call the interpolated string method, and the other way around.
This could result in queries not being parameterized when they should have been.
Mitigations
Switch to use the new method names.
FromSql method when used with stored procedure cannot be composed
Before EF Core 3.0, FromSql method tried to detect if the passed SQL can be composed upon. It did client evaluation when the SQL was non-composable like a stored procedure. The following query worked by running the stored procedure on the server and doing FirstOrDefault on the client side.
context.Products.FromSqlRaw("[dbo].[Ten Most Expensive Products]").FirstOrDefault();
New behavior
Starting with EF Core 3.0, EF Core will not try to parse the SQL. So if you are composing after FromSqlRaw/FromSqlInterpolated, then EF Core will compose the SQL by causing sub query. So if you are using a stored procedure with composition then you will get an exception for invalid SQL syntax.
Why
EF Core 3.0 does not support automatic client evaluation, since it was error prone as explained here.
Mitigations
If you are using a stored procedure in FromSqlRaw/FromSqlInterpolated, you know that it cannot be composed upon, so you can add AsEnumerable/AsAsyncEnumerable right after the FromSql method call to avoid any composition on server side.
context.Products.FromSqlRaw("[dbo].[Ten Most Expensive Products]").AsEnumerable().FirstOrDefault();
FromSql methods can only be specified on query roots
Before EF Core 3.0, the FromSql method could be specified anywhere in the query.
New behavior
Starting with EF Core 3.0, the new FromSqlRaw and FromSqlInterpolated methods (which replace FromSql) can only be specified on query roots, i.e. directly on the DbSet<>. Attempting to specify them anywhere else will result in a compilation error.
Why
Specifying FromSql anywhere other than on a DbSet had no added meaning or added value, and could cause ambiguity in certain scenarios.
Mitigations
FromSql invocations should be moved to be directly on the DbSet to which they apply.
No-tracking queries no longer perform identity resolution
Before EF Core 3.0, the same entity instance would be used for every occurrence of an entity with a given type and ID. This matches the behavior of tracking queries. For example, this query:
var results = context.Products.Include(e => e.Category).AsNoTracking().ToList();
would return the same Category instance for each Product that is associated with the given category.
New behavior
Starting with EF Core 3.0, different entity instances will be created when an entity with a given type and ID is encountered at different places in the returned graph. For example, the query above will now return a new Category instance for each Product even when two products are associated with the same category.
Why
Identity resolution (that is, determining that an entity has the same type and ID as a previously encountered entity) adds additional performance and memory overhead. This usually runs counter to why no-tracking queries are used in the first place. Also, while identity resolution can sometimes be useful, it is not needed if the entities are to be serialized and sent to a client, which is common for no-tracking queries.
Mitigations
Use a tracking query if identity resolution is required.
Temporary key values are no longer set onto entity instances
Before EF Core 3.0, temporary values were assigned to all key properties that would later have a real value generated by the database.
Usually these temporary values were large negative numbers.
New behavior
Starting with 3.0, EF Core stores the temporary key value as part of the entity's tracking information, and leaves the key property itself unchanged.
Why
This change was made to prevent temporary key values from erroneously becoming permanent when an entity that has been previously tracked by some DbContext instance is moved to a different DbContext instance.
Mitigations
Applications that assign primary key values onto foreign keys to form associations between entities may depend on the old behavior if the primary keys are store-generated and belong to entities in the Added state.
This can be avoided by:
Not using store-generated keys.
Setting navigation properties to form relationships instead of setting foreign key values.
Obtain the actual temporary key values from the entity's tracking information.
For example, context.Entry(blog).Property(e => e.Id).CurrentValue will return the temporary value even though blog.Id itself hasn't been set.
Before EF Core 3.0, an untracked entity found by DetectChanges would be tracked in the Added state and inserted as a new row when SaveChanges is called.
New behavior
Starting with EF Core 3.0, if an entity is using generated key values and some key value is set, then the entity will be tracked in the Modified state.
This means that a row for the entity is assumed to exist and it will be updated when SaveChanges is called.
If the key value isn't set, or if the entity type isn't using generated keys, then the new entity will still be tracked as Added as in previous versions.
Why
This change was made to make it easier and more consistent to work with disconnected entity graphs while using store-generated keys.
Mitigations
This change can break an application if an entity type is configured to use generated keys but key values are explicitly set for new instances.
The fix is to explicitly configure the key properties to not use generated values.
For example, with the fluent API:
Before 3.0, EF Core applied cascading actions (deleting dependent entities when a required principal is deleted or when the relationship to a required principal is severed) did not happen until SaveChanges was called.
New behavior
Starting with 3.0, EF Core applies cascading actions as soon as the triggering condition is detected.
For example, calling context.Remove() to delete a principal entity will result in all tracked related required dependents also being set to Deleted immediately.
Why
This change was made to improve the experience for data binding and auditing scenarios where it is important to understand which entities will be deleted beforeSaveChanges is called.
Mitigations
The previous behavior can be restored through settings on context.ChangeTracker.
For example:
Before 3.0, eagerly loading collection navigations via Include operators caused multiple queries to be generated on relational database, one for each related entity type.
New behavior
Starting with 3.0, EF Core generates a single query with JOINs on relational databases.
Why
Issuing multiple queries to implement a single LINQ query caused numerous issues, including negative performance as multiple database roundtrips were necessary, and data coherency issues as each query could observe a different state of the database.
Mitigations
While technically this is not a breaking change, it could have a considerable effect on application performance when a single query contains a large number of Include operator on collection navigations. See this comment for more information and for rewriting queries in a more efficient way.
Before 3.0, DeleteBehavior.Restrict created foreign keys in the database with Restrict semantics, but also changed internal fixup in a non-obvious way.
New behavior
Starting with 3.0, DeleteBehavior.Restrict ensures that foreign keys are created with Restrict semantics--that is, no cascades; throw on constraint violation--without also impacting EF internal fixup.
Why
This change was made to improve the experience for using DeleteBehavior in an intuitive manner, without unexpected side-effects.
Mitigations
The previous behavior can be restored by using DeleteBehavior.ClientNoAction.
Before EF Core 3.0, query types were a means to query data that doesn't define a primary key in a structured way.
That is, a query type was used for mapping entity types without keys (more likely from a view, but possibly from a table) while a regular entity type was used when a key was available (more likely from a table, but possibly from a view).
New behavior
A query type now becomes just an entity type without a primary key.
Keyless entity types have the same functionality as query types in previous versions.
Why
This change was made to reduce the confusion around the purpose of query types.
Specifically, they are keyless entity types and they are inherently read-only because of this, but they should not be used just because an entity type needs to be read-only.
Likewise, they are often mapped to views, but this is only because views often don't define keys.
Mitigations
The following parts of the API are now obsolete:
ModelBuilder.Query<>() - Instead ModelBuilder.Entity<>().HasNoKey() needs to be called to mark an entity type as having no keys.
This would still not be configured by convention to avoid misconfiguration when a primary key is expected, but doesn't match the convention.
DbQuery<> - Instead DbSet<> should be used.
DbContext.Query<>() - Instead DbContext.Set<>() should be used.
IQueryTypeConfiguration<TQuery> - Instead IEntityTypeConfiguration<TEntity> should be used.
Note
Due to an issue in 3.x when querying keyless entities that have all properties set to null a null will be returned instead of an entity, if this issue is applicable to your scenario also add logic to handle null in results.
Configuration API for owned type relationships has changed
The configuration related to the relationship between owner and owned should now be chained after WithOwner() similarly to how other relationships are configured.
While the configuration for the owned type itself would still be chained after OwnsOne()/OwnsMany().
For example:
Additionally calling Entity(), HasOne(), or Set() with an owned type target will now throw an exception.
Why
This change was made to create a cleaner separation between configuring the owned type itself and the relationship to the owned type.
This in turn removes ambiguity and confusion around methods like HasForeignKey.
Mitigations
Change configuration of owned type relationships to use the new API surface as shown in the example above.
Dependent entities sharing the table with the principal are now optional
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
public OrderDetails Details { get; set; }
}
public class OrderDetails
{
public int Id { get; set; }
public string ShippingAddress { get; set; }
}
Before EF Core 3.0, if OrderDetails is owned by Order or explicitly mapped to the same table then an OrderDetails instance was always required when adding a new Order.
New behavior
Starting with 3.0, EF Core allows to add an Order without an OrderDetails and maps all of the OrderDetails properties except the primary key to nullable columns.
When querying EF Core sets OrderDetails to null if any of its required properties doesn't have a value or if it has no required properties besides the primary key and all properties are null.
Mitigations
If your model has a table sharing dependent with all optional columns, but the navigation pointing to it is not expected to be null then the application should be modified to handle cases when the navigation is null. If this is not possible a required property should be added to the entity type or at least one property should have a non-null value assigned to it.
All entities sharing a table with a concurrency token column have to map it to a property
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
public byte[] Version { get; set; }
public OrderDetails Details { get; set; }
}
public class OrderDetails
{
public int Id { get; set; }
public string ShippingAddress { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>()
.Property(o => o.Version).IsRowVersion().HasColumnName("Version");
}
Before EF Core 3.0, if OrderDetails is owned by Order or explicitly mapped to the same table then updating just OrderDetails will not update Version value on client and the next update will fail.
New behavior
Starting with 3.0, EF Core propagates the new Version value to Order if it owns OrderDetails. Otherwise an exception is thrown during model validation.
Why
This change was made to avoid a stale concurrency token value when only one of the entities mapped to the same table is updated.
Mitigations
All entities sharing the table have to include a property that is mapped to the concurrency token column. It's possible the create one in shadow-state:
public abstract class EntityBase
{
public int Id { get; set; }
}
public abstract class OrderBase : EntityBase
{
public int ShippingAddress { get; set; }
}
public class BulkOrder : OrderBase
{
}
public class Order : OrderBase
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore<OrderBase>();
modelBuilder.Entity<EntityBase>();
modelBuilder.Entity<BulkOrder>();
modelBuilder.Entity<Order>();
}
Before EF Core 3.0, the ShippingAddress property would be mapped to separate columns for BulkOrder and Order by default.
New behavior
Starting with 3.0, EF Core only creates one column for ShippingAddress.
Why
The old behavior was unexpected.
Mitigations
The property can still be explicitly mapped to separate column on the derived types:
public class Customer
{
public int CustomerId { get; set; }
public ICollection<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
}
Before EF Core 3.0, the CustomerId property would be used for the foreign key by convention.
However, if Order is an owned type, then this would also make CustomerId the primary key and this isn't usually the expectation.
New behavior
Starting with 3.0, EF Core doesn't try to use properties for foreign keys by convention if they have the same name as the principal property.
Principal type name concatenated with principal property name, and navigation name concatenated with principal property name patterns are still matched.
For example:
public class Customer
{
public int Id { get; set; }
public ICollection<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
}
public class Customer
{
public int Id { get; set; }
public ICollection<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public int BuyerId { get; set; }
public Customer Buyer { get; set; }
}
Why
This change was made to avoid erroneously defining a primary key property on the owned type.
Mitigations
If the property was intended to be the foreign key, and hence part of the primary key, then explicitly configure it as such.
Database connection is now closed if not used anymore before the TransactionScope has been completed
Before EF Core 3.0, if the context opens the connection inside a TransactionScope, the connection remains open while the current TransactionScope is active.
using (new TransactionScope())
{
using (AdventureWorks context = new AdventureWorks())
{
context.ProductCategories.Add(new ProductCategory());
context.SaveChanges();
// Old behavior: Connection is still open at this point
var categories = context.ProductCategories().ToList();
}
}
New behavior
Starting with 3.0, EF Core closes the connection as soon as it's done using it.
Why
This change allows to use multiple contexts in the same TransactionScope. The new behavior also matches EF6.
Mitigations
If the connection needs to remain open explicit call to OpenConnection() will ensure that EF Core doesn't close it prematurely:
using (new TransactionScope())
{
using (AdventureWorks context = new AdventureWorks())
{
context.Database.OpenConnection();
context.ProductCategories.Add(new ProductCategory());
context.SaveChanges();
var categories = context.ProductCategories().ToList();
context.Database.CloseConnection();
}
}
Each property uses independent in-memory integer key generation
Before EF Core 3.0, one shared value generator was used for all in-memory integer key properties.
New behavior
Starting with EF Core 3.0, each integer key property gets its own value generator when using the in-memory database.
Also, if the database is deleted, then key generation is reset for all tables.
Why
This change was made to align in-memory key generation more closely to real database key generation and to improve the ability to isolate tests from each other when using the in-memory database.
Mitigations
This can break an application that is relying on specific in-memory key values to be set.
Consider instead not relying on specific key values, or updating to match the new behavior.
Before 3.0, even if the backing field for a property was known, EF Core would still by default read and write the property value using the property getter and setter methods.
The exception to this was query execution, where the backing field would be set directly if known.
New behavior
Starting with EF Core 3.0, if the backing field for a property is known, then EF Core will always read and write that property using the backing field.
This could cause an application break if the application is relying on additional behavior coded into the getter or setter methods.
Why
This change was made to prevent EF Core from erroneously triggering business logic by default when performing database operations involving the entities.
Mitigations
The pre-3.0 behavior can be restored through configuration of the property access mode on ModelBuilder.
For example:
Before EF Core 3.0, if multiple fields matched the rules for finding the backing field of a property, then one field would be chosen based on some precedence order.
This could cause the wrong field to be used in ambiguous cases.
New behavior
Starting with EF Core 3.0, if multiple fields are matched to the same property, then an exception is thrown.
Why
This change was made to avoid silently using one field over another when only one can be correct.
Mitigations
Properties with ambiguous backing fields must have the field to use specified explicitly.
For example, using the fluent API:
Field-only property names should match the field name
Old behavior
Before EF Core 3.0, a property could be specified by a string value and if no property with that name was found on the .NET type then EF Core would try to match it to a field using convention rules.
private class Blog
{
private int _id;
public string Name { get; set; }
}
modelBuilder
.Entity<Blog>()
.Property("Id");
New behavior
Starting with EF Core 3.0, a field-only property must match the field name exactly.
modelBuilder
.Entity<Blog>()
.Property("_id");
Why
This change was made to avoid using the same field for two properties named similarly, it also makes the matching rules for field-only properties the same as for properties mapped to CLR properties.
Mitigations
Field-only properties must be named the same as the field they are mapped to.
In a future release of EF Core after 3.0, we plan to re-enable explicitly configuring a field name that is different from the property name (see issue #15307):
Before EF Core 3.0, calling AddDbContext or AddDbContextPool would also register logging and memory caching services with DI through calls to AddLogging and AddMemoryCache.
New behavior
Starting with EF Core 3.0, AddDbContext and AddDbContextPool will no longer register these services with Dependency Injection (DI).
Why
EF Core 3.0 does not require that these services are in the application's DI container. However, if ILoggerFactory is registered in the application's DI container, then it will still be used by EF Core.
Mitigations
If your application needs these services, then register them explicitly with the DI container using AddLogging or AddMemoryCache.
AddEntityFramework* adds IMemoryCache with a size limit
Before EF Core 3.0, calling AddEntityFramework* methods would also register memory caching services with DI without a size limit.
New behavior
Starting with EF Core 3.0, AddEntityFramework* will register an IMemoryCache service with a size limit. If any other services added afterwards depend on IMemoryCache they can quickly reach the default limit causing exceptions or degraded performance.
Why
Using IMemoryCache without a limit could result in uncontrolled memory usage if there is a bug in query caching logic or the queries are generated dynamically. Having a default limit mitigates a potential DoS attack.
Mitigations
In most cases calling AddEntityFramework* is not necessary if AddDbContext or AddDbContextPool is called as well. Therefore, the best mitigation is to remove the AddEntityFramework* call.
If your application needs these services, then register a IMemoryCache implementation explicitly with the DI container beforehand using AddMemoryCache.
DbContext.Entry now performs a local DetectChanges
Before EF Core 3.0, calling DbContext.Entry would cause changes to be detected for all tracked entities.
This ensured that the state exposed in the EntityEntry was up-to-date.
New behavior
Starting with EF Core 3.0, calling DbContext.Entry will now only attempt to detect changes in the given entity and any tracked principal entities related to it.
This means that changes elsewhere may not have been detected by calling this method, which could have implications on application state.
Note that if ChangeTracker.AutoDetectChangesEnabled is set to false then even this local change detection will be disabled.
Other methods that cause change detection--for example ChangeTracker.Entries and SaveChanges--still cause a full DetectChanges of all tracked entities.
Why
This change was made to improve the default performance of using context.Entry.
Mitigations
Call ChangeTracker.DetectChanges() explicitly before calling Entry to ensure the pre-3.0 behavior.
String and byte array keys are not client-generated by default
Before EF Core 3.0, string and byte[] key properties could be used without explicitly setting a non-null value.
In such a case, the key value would be generated on the client as a GUID, serialized to bytes for byte[].
New behavior
Starting with EF Core 3.0 an exception will be thrown indicating that no key value has been set.
Why
This change was made because client-generated string/byte[] values generally aren't useful, and the default behavior made it hard to reason about generated key values in a common way.
Mitigations
The pre-3.0 behavior can be obtained by explicitly specifying that the key properties should use generated values if no other non-null value is set.
For example, with the fluent API:
Before EF Core 3.0, ILoggerFactory was registered as a singleton service.
New behavior
Starting with EF Core 3.0, ILoggerFactory is now registered as scoped.
Why
This change was made to allow association of a logger with a DbContext instance, which enables other functionality and removes some cases of pathological behavior such as an explosion of internal service providers.
Mitigations
This change should not impact application code unless it is registering and using custom services on the EF Core internal service provider.
This isn't common.
In these cases, most things will still work, but any singleton service that was depending on ILoggerFactory will need to be changed to obtain the ILoggerFactory in a different way.
If you run into situations like this, please file an issue at on the EF Core GitHub issue tracker to let us know how you are using ILoggerFactory such that we can better understand how not to break this again in the future.
Lazy-loading proxies no longer assume navigation properties are fully loaded
Before EF Core 3.0, once a DbContext was disposed there was no way of knowing if a given navigation property on an entity obtained from that context was fully loaded or not.
Proxies would instead assume that a reference navigation is loaded if it has a non-null value, and that a collection navigation is loaded if it isn't empty.
In these cases, attempting to lazy-load would be a no-op.
New behavior
Starting with EF Core 3.0, proxies keep track of whether or not a navigation property is loaded.
This means attempting to access a navigation property that is loaded after the context has been disposed will always be a no-op, even when the loaded navigation is empty or null.
Conversely, attempting to access a navigation property that isn't loaded will throw an exception if the context is disposed even if the navigation property is a non-empty collection.
If this situation arises, it means the application code is attempting to use lazy-loading at an invalid time, and the application should be changed to not do this.
Why
This change was made to make the behavior consistent and correct when attempting to lazy-load on a disposed DbContext instance.
Mitigations
Update application code to not attempt lazy-loading with a disposed context, or configure this to be a no-op as described in the exception message.
Excessive creation of internal service providers is now an error by default
Before EF Core 3.0, a warning would be logged for an application creating a pathological number of internal service providers.
New behavior
Starting with EF Core 3.0, this warning is now considered and error and an exception is thrown.
Why
This change was made to drive better application code through exposing this pathological case more explicitly.
Mitigations
The most appropriate cause of action on encountering this error is to understand the root cause and stop creating so many internal service providers.
However, the error can be converted back to a warning (or ignored) via configuration on the DbContextOptionsBuilder.
For example:
The code looks like it is relating Samurai to some other entity type using the Entrance navigation property, which may be private.
In reality, this code attempts to create a relationship to some entity type called Entrance with no navigation property.
New behavior
Starting with EF Core 3.0, the code above now does what it looked like it should have been doing before.
Why
The old behavior was very confusing, especially when reading the configuration code and looking for errors.
Mitigations
This will only break applications that are explicitly configuring relationships using strings for type names and without specifying the navigation property explicitly.
This is not common.
The previous behavior can be obtained through explicitly passing null for the navigation property name.
For example:
The aforementioned methods now return a ValueTask<T> over the same T as before.
Why
This change reduces the number of heap allocations incurred when invoking these methods, improving general performance.
Mitigations
Applications simply awaiting the above APIs only need to be recompiled - no source changes are necessary.
A more complex usage (e.g. passing the returned Task to Task.WhenAny()) typically require that the returned ValueTask<T> be converted to a Task<T> by calling AsTask() on it.
Note that this negates the allocation reduction that this change brings.
The Relational:TypeMapping annotation is now just TypeMapping
The annotation name for type mapping annotations was "Relational:TypeMapping".
New behavior
The annotation name for type mapping annotations is now "TypeMapping".
Why
Type mappings are now used for more than just relational database providers.
Mitigations
This will only break applications that access the type mapping directly as an annotation, which isn't common.
The most appropriate action to fix is to use API surface to access type mappings rather than using the annotation directly.
Before EF Core 3.0, ToTable() called on a derived type would be ignored since only inheritance mapping strategy was TPH where this isn't valid.
New behavior
Starting with EF Core 3.0 and in preparation for adding TPT and TPC support in a later release, ToTable() called on a derived type will now throw an exception to avoid an unexpected mapping change in the future.
Why
Currently it isn't valid to map a derived type to a different table.
This change avoids breaking in the future when it becomes a valid thing to do.
Mitigations
Remove any attempts to map derived types to other tables.
Before EF Core 3.0, EF Core would send PRAGMA foreign_keys = 1 when a connection to SQLite is opened.
New behavior
Starting with EF Core 3.0, EF Core no longer sends PRAGMA foreign_keys = 1 when a connection to SQLite is opened.
Why
This change was made because EF Core uses SQLitePCLRaw.bundle_e_sqlite3 by default, which in turn means that FK enforcement is switched on by default and doesn't need to be explicitly enabled each time a connection is opened.
Mitigations
Foreign keys are enabled by default in SQLitePCLRaw.bundle_e_sqlite3, which is used by default for EF Core.
For other cases, foreign keys can be enabled by specifying Foreign Keys=True in your connection string.
Microsoft.EntityFrameworkCore.Sqlite now depends on SQLitePCLRaw.bundle_e_sqlite3
Old behavior
Before EF Core 3.0, EF Core used SQLitePCLRaw.bundle_green.
New behavior
Starting with EF Core 3.0, EF Core uses SQLitePCLRaw.bundle_e_sqlite3.
Why
This change was made so that the version of SQLite used on iOS consistent with other platforms.
Mitigations
To use the native SQLite version on iOS, configure Microsoft.Data.Sqlite to use a different SQLitePCLRaw bundle.
In EF Core, you could also continue using the previous behavior by configuring a value converter on these properties.
modelBuilder
.Entity<MyEntity>()
.Property(e => e.GuidProperty)
.HasConversion(
g => g.ToByteArray(),
b => new Guid(b));
Microsoft.Data.Sqlite remains capable of reading Guid values from both BLOB and TEXT columns; however, since the default format for parameters and constants has changed you'll likely need to take action for most scenarios involving Guids.
Char values were previously stored as INTEGER values on SQLite. For example, a char value of A was stored as the integer value 65.
New behavior
Char values are now stored as TEXT.
Why
Storing the values as TEXT is more natural and makes the database more compatible with other technologies.
Mitigations
You can migrate existing databases to the new format by executing SQL like the following.
UPDATE MyTable
SET CharColumn = char(CharColumn)
WHERE typeof(CharColumn) = 'integer';
In EF Core, you could also continue using the previous behavior by configuring a value converter on these properties.
modelBuilder
.Entity<MyEntity>()
.Property(e => e.CharProperty)
.HasConversion(
c => (long)c,
i => (char)i);
Microsoft.Data.Sqlite also remains capable of reading character values from both INTEGER and TEXT columns, so certain scenarios may not require any action.
Migration IDs are now generated using the invariant culture's calendar
Migration IDs were inadvertently generated using the current culture's calendar.
New behavior
Migration IDs are now always generated using the invariant culture's calendar (Gregorian).
Why
The order of migrations is important when updating the database or resolving merge conflicts. Using the invariant calendar avoids ordering issues that can result from team members having different system calendars.
Mitigations
This change affects anyone using a non-Gregorian calendar where the year is greater than the Gregorian calendar (like the Thai Buddhist calendar). Existing migration IDs will need to be updated so that new migrations are ordered after existing migrations.
The migration ID can be found in the Migration attribute in the migrations' designer files.
[DbContext(typeof(MyDbContext))]
-[Migration("25620318122820_MyMigration")]
+[Migration("20190318122820_MyMigration")]
partial class MyMigration
{
The Migrations history table also needs to be updated.
We recommend updating to a newer version of SQL Server, or using a higher compatibility level, so that the generated SQL is supported. That being said, if you are unable to do this, then please comment on the tracking issue with details. We may revisit this decision based on feedback.
Extension info/metadata has been removed from IDbContextOptionsExtension
IDbContextOptionsExtension contained methods for providing metadata about the extension.
New behavior
These methods have been moved onto a new DbContextOptionsExtensionInfo abstract base class, which is returned from a new IDbContextOptionsExtension.Info property.
Why
Over the releases from 2.0 to 3.0 we needed to add to or change these methods several times.
Breaking them out into a new abstract base class will make it easier to make these kind of changes without breaking existing extensions.
Mitigations
Update extensions to follow the new pattern.
Examples are found in the many implementations of IDbContextOptionsExtension for different kinds of extensions in the EF Core source code.
LogQueryPossibleExceptionWithAggregateOperator has been renamed
RelationalEventId.LogQueryPossibleExceptionWithAggregateOperator has been renamed to RelationalEventId.LogQueryPossibleExceptionWithAggregateOperatorWarning.
Why
Aligns the naming of this warning event with all other warning events.
Mitigations
Use the new name. (Note that the event ID number has not changed.)
Before EF Core 3.0, foreign key constraint names were referred to as simply the "name". For example:
var constraintName = myForeignKey.Name;
New behavior
Starting with EF Core 3.0, foreign key constraint names are now referred to as the "constraint name". For example:
var constraintName = myForeignKey.ConstraintName;
Why
This change brings consistency to naming in this area, and also clarifies that this is the name of the foreign key constraint, and not the column or property name that the foreign key is defined on.
Mitigations
Use the new name.
IRelationalDatabaseCreator.HasTables/HasTablesAsync have been made public
Starting with EF Core 3.0, these methods are public.
Why
These methods are used by EF to determine if a database is created but empty. This can also be useful from outside EF when determining whether or not to apply migrations.
Mitigations
Change the accessibility of any overrides.
Microsoft.EntityFrameworkCore.Design is now a DevelopmentDependency package
Before EF Core 3.0, Microsoft.EntityFrameworkCore.Design was a regular NuGet package whose assembly could be referenced by projects that depended on it.
New behavior
Starting with EF Core 3.0, it is a DevelopmentDependency package. This means that the dependency won't flow transitively into other projects, and that you can no longer, by default, reference its assembly.
Why
This package is only intended to be used at design time. Deployed applications shouldn't reference it. Making the package a DevelopmentDependency reinforces this recommendation.
Mitigations
If you need to reference this package to override EF Core's design-time behavior, then you can update PackageReference item metadata in your project.
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.0.0">
<PrivateAssets>all</PrivateAssets>
<!-- Remove IncludeAssets to allow compiling against the assembly -->
<!--<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>-->
</PackageReference>
If the package is being referenced transitively via Microsoft.EntityFrameworkCore.Tools, you will need to add an explicit PackageReference to the package to change its metadata. Such an explicit reference must be added to any project where the types from the package are needed.
Microsoft.EntityFrameworkCore.Sqlite previously depended on version 1.1.12 of SQLitePCL.raw.
New behavior
We've updated our package to depend on version 2.0.0.
Why
Version 2.0.0 of SQLitePCL.raw targets .NET Standard 2.0. It previously targeted .NET Standard 1.1 which required a large closure of transitive packages to work.
Mitigations
SQLitePCL.raw version 2.0.0 includes some breaking changes. See the release notes for details.
Microsoft.EntityFrameworkCore.SqlServer previously depended on System.Data.SqlClient.
New behavior
We've updated our package to depend on Microsoft.Data.SqlClient.
Why
Microsoft.Data.SqlClient is the flagship data access driver for SQL Server going forward, and System.Data.SqlClient no longer be the focus of development.
Some important features, such as Always Encrypted, are only available on Microsoft.Data.SqlClient.
Mitigations
If your code takes a direct dependency on System.Data.SqlClient, you must change it to reference Microsoft.Data.SqlClient instead; as the two packages maintain a very high degree
of API compatibility, this should only be a simple package and namespace change.
Multiple ambiguous self-referencing relationships must be configured
An entity type with multiple self-referencing uni-directional navigation properties and matching FKs was incorrectly configured as a single relationship. For example:
public class User
{
public Guid Id { get; set; }
public User CreatedBy { get; set; }
public User UpdatedBy { get; set; }
public Guid CreatedById { get; set; }
public Guid? UpdatedById { get; set; }
}
New behavior
This scenario is now detected in model building and an exception is thrown indicating that the model is ambiguous.
Why
The resultant model was ambiguous and will likely usually be wrong for this case.
Mitigations
Use full configuration of the relationship. For example:
A DbFunction configured with schema as an empty string was treated as built-in function without a schema. For example following code will map DatePart CLR function to DATEPART built-in function on SqlServer.
[DbFunction("DATEPART", Schema = "")]
public static int? DatePart(string datePartArg, DateTime? date) => throw new Exception();
New behavior
All DbFunction mappings are considered to be mapped to user defined functions. Hence empty string value would put the function inside the default schema for the model. Which could be the schema configured explicitly via fluent API modelBuilder.HasDefaultSchema() or dbo otherwise.
Why
Previously schema being empty was a way to treat that function is built-in but that logic is only applicable for SqlServer where built-in functions do not belong to any schema.
Mitigations
Configure DbFunction's translation manually to map it to a built-in function.
EF Core 3.0 targets .NET Standard 2.1, which is a breaking change which excludes .NET Framework applications. EF Core 3.1 reverted this and targets .NET Standard 2.0 again.
We reverted this change because new configuration in EF Core 3.0 allows the log level for any event to be specified by the application. For example, to switch logging of SQL to Debug, explicitly configure the level in OnConfiguring or AddDbContext:
The source for this content can be found on GitHub, where you can also create and review issues and pull requests. For more information, see our contributor guide.
.NET feedback
.NET is an open source project. Select a link to provide feedback:
This module guides you through the steps to create a data access project. You connect to a relational database and construct create, read, update, and delete (CRUD) queries by using Entity Framework Core (EF Core).