10.1 release notes¶
InfoCarrier.Core 10.1 is about the store most application servers run: a relational database. The client now knows what a relational server can do, so queries that depend on that knowledge reach it instead of stopping at the boundary.
10.1.1 is the latest of this line and needs Entity Framework Core 10.0.1 or later. 10.1.0
works only against EF Core 10.0.0: on a later patch a query returning entities throws. Move a
pinned 10.0.0 forward.
Upgrading from 10.0.x is a package version change. Read
Queries that now throw first, because some queries that used to return
an answer now raise an exception.
Raw SQL¶
FromSql and Database.SqlQuery<T> reach the server's database.
List<Order> heavy = await context.Orders
.FromSql($"SELECT * FROM Orders WHERE Freight > {minimum}")
.Where(o => o.Customer!.Country == "Germany")
.ToListAsync();
List<decimal> freights = await context.Database
.SqlQuery<decimal>($"SELECT Freight FROM Orders")
.ToListAsync();
Both stay off until two grants line up: services.AddInfoCarrierArbitrarySqlExecution() on the
server, and o.AllowArbitrarySqlExecution() on the client. Only the server's half is a boundary,
and it denies by default.
Weigh what the grant is worth to whoever holds a client. One command text runs every statement in
it, and an uncomposed FromSql reaches the database unchanged, so a caller who has the grant can
run whatever the database allows, under the server's own rights. Security has the
threat model.
A client without the grant is refused. Before this release the SQL was dropped and the query returned the whole table.
Functions your database defines¶
EF.Functions.Like, EF.Constant and EF.Parameter cross the wire. A store's own family, such as
SqliteDbFunctionsExtensions or SqlServerDbFunctionsExtensions, is not in this package's
vocabulary, so name it on both halves:
// The server, which references the provider that declares them.
builder.Services.AddInfoCarrierAllowedTypes(typeof(SqlServerDbFunctionsExtensions));
// The client, which does not.
optionsBuilder.UseInfoCarrier(client, o => o.AllowTypes(typeof(SqlServerDbFunctionsExtensions)));
Functions you map yourself with HasDbFunction work too, scalar and table valued, including one
mapped as an instance method.
Inheritance and table mapping¶
Table per hierarchy, table per type and table per concrete type all round trip, and so do table splitting and entity splitting. The client keeps the mapping the server's model has, rather than the one core EF Core would infer on its own.
AsSplitQuery and AsSingleQuery now travel with the request, so the server decides how many
statements to issue. UseRelationalNulls reaches the server as well.
Relational metadata reaches the client model¶
entityType.GetTableName() and Model.GetRelationalModel() answer on a client context, so tooling
that reads them works against one. They come from your own [Table] attributes and DbSet names,
which both halves compile, so the two models agree. Nothing the client computes from them crosses
the wire.
Queries that now throw¶
Three shapes of query used to return an answer and now raise InvalidOperationException, with EF
Core's own wording:
| Query | Why |
|---|---|
An OrderBy key of a type the wire cannot carry |
The server sent the whole table and the client sorted it |
new Layout() ?? fallback in a predicate or an ordering |
The same, and the coalesce did nothing: new never returns null |
Distinct, Union, Concat, Except or Intersect over a projection that carries a collection |
Every other provider refuses it, because the columns that identify a row do not survive |
The first two were correct answers at a price nothing reported. The third was an answer no other provider gives, so LINQ written against it ran nowhere else.
If your server's store is not relational, say so and the three go away:
You are telling the client something it cannot work out for itself, because it never sees the server's provider. Saying it about a relational server does not make the query work there. It only removes the refusal here.
Seeing what a request costs¶
The meter InfoCarrier.Core counts round trips and how long each took, so you can measure a screen
in a running application. See
Counting round trips.
InfoCarrierEventId.QuerySplit now names which operators stayed on the client, and says whether
they only reshape rows or remove them. A Where, Skip or Take among them means the server sent
more rows than your query asked for.
A server can send the log events it raises back with the result, so a client sees EF's warnings about its own query. The server has to grant that, because a forwarded event describes the server's store. See Sending the server's log to the client.
Fixes¶
A query parameter still reached the database as a literal in four shapes that 10.0.1 did not
cover: a HashSet, ImmutableArray or ReadOnlyCollection in a Contains, an entity compared
with ==, a key with a value converter, and a complex value. Each now travels as a parameter, so
the database reuses one plan per query instead of one per value.
DbUpdateException.Entries names the row the store rejected, rather than every row in the batch.
How it is verified¶
The provider inherits Microsoft's own EF Core specification suite:
The suite is about seven thousand tests larger than at 10.0, which is where the higher failure
count comes from. The nineteen are classified and gated in continuous integration, so the number
cannot grow unnoticed, and Limitations states the ones a caller can observe.
The 238 skips are EF Core's own, not suppressions added here.
Feedback¶
If you encounter a bug, have a question, or would like to request a feature, open an issue.