Training
Learning path
Use advance techniques in canvas apps to perform custom updates and optimization - Training
Use advance techniques in canvas apps to perform custom updates and optimization
This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
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.
Tracking Issue #14935 Also see issue #12795
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.
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.
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.
If a query can't be fully translated, then either rewrite the query in a form that can be translated, or use AsEnumerableAsync()
, ToListAsync()
, or similar to explicitly bring data back to the client where it can then be further processed using LINQ-to-Objects.
Tracking Issue Announcements#325
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.
Starting in 3.0, the ASP.NET Core shared framework doesn't include EF Core or any EF Core data providers.
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.
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.
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.
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.
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.
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.
Important
ExecuteSqlCommand
and ExecuteSqlCommandAsync
are deprecated. Use these methods instead.
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.
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.
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.
Switch to use the new method names.
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();
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.
EF Core 3.0 does not support automatic client evaluation, since it was error prone as explained here.
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();
Before EF Core 3.0, the FromSql
method could be specified anywhere in the query.
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.
Specifying FromSql
anywhere other than on a DbSet
had no added meaning or added value, and could cause ambiguity in certain scenarios.
FromSql
invocations should be moved to be directly on the DbSet
to which they apply.
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 = await context.Products.Include(e => e.Category).AsNoTracking().ToListAsync();
would return the same Category
instance for each Product
that is associated with the given category.
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.
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.
Use a tracking query if identity resolution is required.
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.
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.
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.
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:
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.
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.
This change was made to make it easier and more consistent to work with disconnected entity graphs while using store-generated keys.
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:
modelBuilder
.Entity<Blog>()
.Property(e => e.Id)
.ValueGeneratedNever();
Or with data annotations:
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id { get; set; }
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.
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.
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 before SaveChanges
is called.
The previous behavior can be restored through settings on context.ChangeTracker
.
For example:
context.ChangeTracker.CascadeDeleteTiming = CascadeTiming.OnSaveChanges;
context.ChangeTracker.DeleteOrphansTiming = CascadeTiming.OnSaveChanges;
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.
Starting with 3.0, EF Core generates a single query with JOINs on relational databases.
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.
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.
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.
This change was made to improve the experience for using DeleteBehavior
in an intuitive manner, without unexpected side-effects.
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).
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.
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.
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.
Tracking Issue #12444 Tracking Issue #9148 Tracking Issue #14153
Before EF Core 3.0, configuration of the owned relationship was performed directly after the OwnsOne
or OwnsMany
call.
Starting with EF Core 3.0, there is now fluent API to configure a navigation property to the owner using WithOwner()
.
For example:
modelBuilder.Entity<Order>.OwnsOne(e => e.Details).WithOwner(e => e.Order);
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:
modelBuilder.Entity<Order>.OwnsOne(e => e.Details, eb =>
{
eb.WithOwner()
.HasForeignKey(e => e.AlternateId)
.HasConstraintName("FK_OrderDetails");
eb.ToTable("OrderDetails");
eb.HasKey(e => e.AlternateId);
eb.HasIndex(e => e.Id);
eb.HasOne(e => e.Customer).WithOne();
eb.HasData(
new OrderDetails
{
AlternateId = 1,
Id = -1
});
});
Additionally calling Entity()
, HasOne()
, or Set()
with an owned type target will now throw an exception.
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
.
Change configuration of owned type relationships to use the new API surface as shown in the example above.
Consider the following model:
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
.
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
.
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.
Consider the following model:
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.
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.
This change was made to avoid a stale concurrency token value when only one of the entities mapped to the same table is updated.
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:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<OrderDetails>()
.Property<byte[]>("Version").IsRowVersion().HasColumnName("Version");
}
Before EF Core 3.0, the owned entities could be queried as any other navigation.
context.People.Select(p => p.Address);
Starting with 3.0, EF Core will throw if a tracking query projects an owned entity without the owner.
Owned entities cannot be manipulated without the owner, so in the vast majority of cases querying them in this way is an error.
If the owned entity should be tracked to be modified in any way later then the owner should be included in the query.
Otherwise add an AsNoTracking()
call:
context.People.Select(p => p.Address).AsNoTracking();
Consider the following model:
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.
Starting with 3.0, EF Core only creates one column for ShippingAddress
.
The old behavior was unexpected.
The property can still be explicitly mapped to separate column on the derived types:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore<OrderBase>();
modelBuilder.Entity<EntityBase>();
modelBuilder.Entity<BulkOrder>()
.Property(o => o.ShippingAddress).HasColumnName("BulkShippingAddress");
modelBuilder.Entity<Order>()
.Property(o => o.ShippingAddress).HasColumnName("ShippingAddress");
}
Consider the following model:
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.
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; }
}
This change was made to avoid erroneously defining a primary key property on the owned type.
If the property was intended to be the foreign key, and hence part of the primary key, then explicitly configure it as such.
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());
await context.SaveChangesAsync();
// Old behavior: Connection is still open at this point
var categories = await context.ProductCategories().ToListAsync();
}
}
Starting with 3.0, EF Core closes the connection as soon as it's done using it.
This change allows to use multiple contexts in the same TransactionScope
. The new behavior also matches EF6.
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())
{
await context.Database.OpenConnectionAsync();
context.ProductCategories.Add(new ProductCategory());
await context.SaveChangesAsync();
var categories = await context.ProductCategories().ToListAsync();
await context.Database.CloseConnectionAsync();
}
}
Before EF Core 3.0, one shared value generator was used for all in-memory integer key properties.
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.
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.
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.
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.
This change was made to prevent EF Core from erroneously triggering business logic by default when performing database operations involving the entities.
The pre-3.0 behavior can be restored through configuration of the property access mode on ModelBuilder
.
For example:
modelBuilder.UsePropertyAccessMode(PropertyAccessMode.PreferFieldDuringConstruction);
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.
Starting with EF Core 3.0, if multiple fields are matched to the same property, then an exception is thrown.
This change was made to avoid silently using one field over another when only one can be correct.
Properties with ambiguous backing fields must have the field to use specified explicitly. For example, using the fluent API:
modelBuilder
.Entity<Blog>()
.Property(e => e.Id)
.HasField("_id");
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");
Starting with EF Core 3.0, a field-only property must match the field name exactly.
modelBuilder
.Entity<Blog>()
.Property("_id");
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.
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):
modelBuilder
.Entity<Blog>()
.Property("Id")
.HasField("_id");
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.
Starting with EF Core 3.0, AddDbContext
and AddDbContextPool
will no longer register these services with Dependency Injection (DI).
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.
If your application needs these services, then register them explicitly with the DI container using AddLogging or AddMemoryCache.
Before EF Core 3.0, calling AddEntityFramework*
methods would also register memory caching services with DI without a size limit.
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.
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.
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.
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.
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.
This change was made to improve the default performance of using context.Entry
.
Call ChangeTracker.DetectChanges()
explicitly before calling Entry
to ensure the pre-3.0 behavior.
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[]
.
Starting with EF Core 3.0 an exception will be thrown indicating that no key value has been set.
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.
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:
modelBuilder
.Entity<Blog>()
.Property(e => e.Id)
.ValueGeneratedOnAdd();
Or with data annotations:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; set; }
Before EF Core 3.0, ILoggerFactory
was registered as a singleton service.
Starting with EF Core 3.0, ILoggerFactory
is now registered as scoped.
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.
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.
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.
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.
This change was made to make the behavior consistent and correct when attempting to lazy-load on a disposed DbContext
instance.
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.
Before EF Core 3.0, a warning would be logged for an application creating a pathological number of internal service providers.
Starting with EF Core 3.0, this warning is now considered and error and an exception is thrown.
This change was made to drive better application code through exposing this pathological case more explicitly.
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:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.ConfigureWarnings(w => w.Log(CoreEventId.ManyServiceProvidersCreatedWarning));
}
Before EF Core 3.0, code calling HasOne
or HasMany
with a single string was interpreted in a confusing way.
For example:
modelBuilder.Entity<Samurai>().HasOne("Entrance").WithOne();
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.
Starting with EF Core 3.0, the code above now does what it looked like it should have been doing before.
The old behavior was very confusing, especially when reading the configuration code and looking for errors.
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:
modelBuilder.Entity<Samurai>().HasOne("Some.Entity.Type.Name", null).WithOne();
The following async methods previously returned a Task<T>
:
DbContext.FindAsync()
DbSet.FindAsync()
DbContext.AddAsync()
DbSet.AddAsync()
ValueGenerator.NextValueAsync()
(and deriving classes)The aforementioned methods now return a ValueTask<T>
over the same T
as before.
This change reduces the number of heap allocations incurred when invoking these methods, improving general performance.
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 annotation name for type mapping annotations was "Relational:TypeMapping".
The annotation name for type mapping annotations is now "TypeMapping".
Type mappings are now used for more than just relational database providers.
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.
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.
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.
Remove any attempts to map derived types to other tables.
Before EF Core 3.0, ForSqlServerHasIndex().ForSqlServerInclude()
provided a way to configure columns used with INCLUDE
.
Starting with EF Core 3.0, using Include
on an index is now supported at the relational level.
Use HasIndex().ForSqlServerInclude()
.
This change was made to consolidate the API for indexes with Include
into one place for all database providers.
Use the new API, as shown above.
The following properties were converted to extension methods:
IEntityType.QueryFilter
-> GetQueryFilter()
IEntityType.DefiningQuery
-> GetDefiningQuery()
IProperty.IsShadowProperty
-> IsShadowProperty()
IProperty.BeforeSaveBehavior
-> GetBeforeSaveBehavior()
IProperty.AfterSaveBehavior
-> GetAfterSaveBehavior()
This change simplifies the implementation of the aforementioned interfaces.
Use the new extension methods.
The provider-specific extension methods will be flattened out:
IProperty.Relational().ColumnName
-> IProperty.GetColumnName()
IEntityType.SqlServer().IsMemoryOptimized
-> IEntityType.IsMemoryOptimized()
PropertyBuilder.UseSqlServerIdentityColumn()
-> PropertyBuilder.UseIdentityColumn()
This change simplifies the implementation of the aforementioned extension methods.
Use the new extension methods.
Before EF Core 3.0, EF Core would send PRAGMA foreign_keys = 1
when a connection to SQLite is opened.
Starting with EF Core 3.0, EF Core no longer sends PRAGMA foreign_keys = 1
when a connection to SQLite is opened.
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.
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.
Before EF Core 3.0, EF Core used SQLitePCLRaw.bundle_green
.
Starting with EF Core 3.0, EF Core uses SQLitePCLRaw.bundle_e_sqlite3
.
This change was made so that the version of SQLite used on iOS consistent with other platforms.
To use the native SQLite version on iOS, configure Microsoft.Data.Sqlite
to use a different SQLitePCLRaw
bundle.
Guid values were previously stored as BLOB values on SQLite.
Guid values are now stored as TEXT.
The binary format of Guids is not standardized. Storing the values as TEXT makes the database more compatible with other technologies.
You can migrate existing databases to the new format by executing SQL like the following.
UPDATE MyTable
SET GuidColumn = hex(substr(GuidColumn, 4, 1)) ||
hex(substr(GuidColumn, 3, 1)) ||
hex(substr(GuidColumn, 2, 1)) ||
hex(substr(GuidColumn, 1, 1)) || '-' ||
hex(substr(GuidColumn, 6, 1)) ||
hex(substr(GuidColumn, 5, 1)) || '-' ||
hex(substr(GuidColumn, 8, 1)) ||
hex(substr(GuidColumn, 7, 1)) || '-' ||
hex(substr(GuidColumn, 9, 2)) || '-' ||
hex(substr(GuidColumn, 11, 6))
WHERE typeof(GuidColumn) == 'blob';
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.
Char values are now stored as TEXT.
Storing the values as TEXT is more natural and makes the database more compatible with other technologies.
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 were inadvertently generated using the current culture's calendar.
Migration IDs are now always generated using the invariant culture's calendar (Gregorian).
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.
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.
UPDATE __EFMigrationsHistory
SET MigrationId = CONCAT(LEFT(MigrationId, 4) - 543, SUBSTRING(MigrationId, 4, 150))
Before EF Core 3.0, UseRowNumberForPaging
could be used to generate SQL for paging that is compatible with SQL Server 2008.
Starting with EF Core 3.0, EF will only generate SQL for paging that is only compatible with later SQL Server versions.
We are making this change because SQL Server 2008 is no longer a supported product and updating this feature to work with the query changes made in EF Core 3.0 is significant work.
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.
IDbContextOptionsExtension
contained methods for providing metadata about the extension.
These methods have been moved onto a new DbContextOptionsExtensionInfo
abstract base class, which is returned from a new IDbContextOptionsExtension.Info
property.
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.
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.
RelationalEventId.LogQueryPossibleExceptionWithAggregateOperator
has been renamed to RelationalEventId.LogQueryPossibleExceptionWithAggregateOperatorWarning
.
Aligns the naming of this warning event with all other warning events.
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;
Starting with EF Core 3.0, foreign key constraint names are now referred to as the "constraint name". For example:
var constraintName = myForeignKey.ConstraintName;
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.
Use the new name.
Before EF Core 3.0, these methods were protected.
Starting with EF Core 3.0, these methods are public.
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.
Change the accessibility of any overrides.
Before EF Core 3.0, Microsoft.EntityFrameworkCore.Design was a regular NuGet package whose assembly could be referenced by projects that depended on it.
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.
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.
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.
We've updated our package to depend on version 2.0.0.
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.
SQLitePCL.raw version 2.0.0 includes some breaking changes. See the release notes for details.
The spatial packages previously depended on version 1.15.1 of NetTopologySuite.
We've update our package to depend on version 2.0.0.
Version 2.0.0 of NetTopologySuite aims to address several usability issues encountered by EF Core users.
NetTopologySuite version 2.0.0 includes some breaking changes. See the release notes for details.
Microsoft.EntityFrameworkCore.SqlServer previously depended on System.Data.SqlClient.
We've updated our package to depend on Microsoft.Data.SqlClient.
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.
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.
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; }
}
This scenario is now detected in model building and an exception is thrown indicating that the model is ambiguous.
The resultant model was ambiguous and will likely usually be wrong for this case.
Use full configuration of the relationship. For example:
modelBuilder
.Entity<User>()
.HasOne(e => e.CreatedBy)
.WithMany();
modelBuilder
.Entity<User>()
.HasOne(e => e.UpdatedBy)
.WithMany();
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();
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.
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.
Configure DbFunction's translation manually to map it to a built-in function.
modelBuilder
.HasDbFunction(typeof(MyContext).GetMethod(nameof(MyContext.DatePart)))
.HasTranslation(args => SqlFunctionExpression.Create("DatePart", args, typeof(int?), null));
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
:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer(connectionString)
.ConfigureWarnings(c => c.Log((RelationalEventId.CommandExecuting, LogLevel.Debug)));
.NET feedback
.NET is an open source project. Select a link to provide feedback:
Training
Learning path
Use advance techniques in canvas apps to perform custom updates and optimization - Training
Use advance techniques in canvas apps to perform custom updates and optimization