Entity Framework Core 7 (EF7) RC2 has shipped! The RC2 release contains all features planned for the EF7 GA release. This includes:
- Mapping to SQL Server JSON Columns
- ExecuteUpdate and ExecuteDelete (Bulk updates)
- Improved performance for SaveChanges
- Table-per-concrete-type (TPC) inheritance mapping
- Custom Reverse Engineering Templates for Database First
- Customizable model building conventions
- Stored procedure mapping for inserts, updates, and deletes
- New and improved interceptors and events
- Query enhancements, including more GroupBy and GroupJoin translations
RC2 contains more than 80 fixed issues over RC1. In total, EF7 contains more than 350 fixed issues over EF Core 6.0, as well as significant documentation updates. Full details can be found on the dotnet/efcore GitHub Releases page.
In this blog post, well take a deep dive into one of the most hotly anticipated features in EF7–the ability to map .NET aggregates to JSON documents stored in relational database columns.
Mapping to JSON Columns
Most relational databases support columns that contain JSON documents. The JSON in these columns can be drilled into with queries. This allows, for example, filtering and sorting by the elements of the documents, as well as projection of elements out of the documents into results. JSON columns allow relational databases to take on some of the characteristics of document databases, creating a useful hybrid between the two.
EF7 contains provider-agnostic support for JSON columns, with an implementation for SQL Server. This support allows mapping of aggregates built from .NET types to JSON documents. Normal LINQ queries can be used on the aggregates, and these will be translated to the appropriate query constructs needed to drill into the JSON. EF7 also supports updating and saving changes to the JSON documents.
NOTE SQLite support for JSON is planned for post EF7. The PostgreSQL and Pomelo MySQL providers already contain some support for JSON columns. We will be working with the authors of those providers to align JSON support across all providers.
Mapping to JSON columns
In EF Core, aggregate types are defined using OwnsOne
and OwnsMany
. For example, consider the aggregate ContactDetails
type from the sample model shown at the end of this post.
public class ContactDetails
{
public Address Address { get; set; } = null!;
public string? Phone { get; set; }
}
public class Address
{
public Address(string street, string city, string postcode, string country)
{
Street = street;
City = city;
Postcode = postcode;
Country = country;
}
public string Street { get; set; }
public string City { get; set; }
public string Postcode { get; set; }
public string Country { get; set; }
}
This can then be used in an “owner” entity type, for example, to store the contact details of an author:
public class Author
{
public Author(string name)
{
Name = name;
}
public int Id { get; private set; }
public string Name { get; set; }
public ContactDetails Contact { get; set; } = null!;
public List<Post> Posts { get; } = new();
}
The aggregate type is configured in OnModelCreating
using OwnsOne
:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Author>().OwnsOne(
author => author.Contact, ownedNavigationBuilder =>
{
ownedNavigationBuilder.OwnsOne(contactDetails => contactDetails.Address);
});
}
TIP The code shown in this post comes from the samples on GitHub for the What’s New in EF7 documentation. Specifically, this code comes from JsonColumnsSample.cs.
By default, EF Core relational database providers map aggregate types like this to the same table as the owning entity type. That is, each property of the ContactDetails
and Address
classes are mapped to a column in the Authors
table.
Some saved authors with contact details will look like this:
Authors
Id | Name | Contact_Address_Street | Contact_Address_City | Contact_Address_Postcode | Contact_Address_Country | Contact_Phone |
---|---|---|---|---|---|---|
1 | Maddy Montaquila | 1 Main St | Camberwick Green | CW1 5ZH | UK | 01632 12345 |
2 | Jeremy Likness | 2 Main St | Chigley | CW1 5ZH | UK | 01632 12346 |
3 | Daniel Roth | 3 Main St | Camberwick Green | CW1 5ZH | UK | 01632 12347 |
4 | Arthur Vickers | 15a Main St | Chigley | CW1 5ZH | United Kingdom | 01632 22345 |
5 | Brice Lambson | 4 Main St | Chigley | CW1 5ZH | UK | 01632 12349 |
If desired, each entity type making up the aggregate can be mapped to its own table instead:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Author>().OwnsOne(
author => author.Contact, ownedNavigationBuilder =>
{
ownedNavigationBuilder.ToTable("Contacts");
ownedNavigationBuilder.OwnsOne(contactDetails => contactDetails.Address, ownedOwnedNavigationBuilder =>
{
ownedOwnedNavigationBuilder.ToTable("Addresses");
});
});
}
The same data is then stored across three tables:
Authors
Id | Name |
---|---|
1 | Maddy Montaquila |
2 | Jeremy Likness |
3 | Daniel Roth |
4 | Arthur Vickers |
5 | Brice Lambson |
Contacts
AuthorId | Phone |
---|---|
1 | 01632 12345 |
2 | 01632 12346 |
3 | 01632 12347 |
4 | 01632 22345 |
5 | 01632 12349 |
Addresses
ContactDetailsAuthorId | Street | City | Postcode | Country |
---|---|---|---|---|
1 | 1 Main St | Camberwick Green | CW1 5ZH | UK |
2 | 2 Main St | Chigley | CW1 5ZH | UK |
3 | 3 Main St | Camberwick Green | CW1 5ZH | UK |
4 | 15a Main St | Chigley | CW1 5ZH | United Kingdom |
5 | 4 Main St | Chigley | CW1 5ZH | UK |
Now, for the interesting part. In EF7, the ContactDetails
aggregate can be mapped to a JSON column. This requires just one call to ToJson()
when configuring the aggregate type:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Author>().OwnsOne(
author => author.Contact, ownedNavigationBuilder =>
{
ownedNavigationBuilder.ToJson();
ownedNavigationBuilder.OwnsOne(contactDetails => contactDetails.Address);
});
}
The Authors
table will now contain a JSON column for ContactDetails
populated with a JSON document for each author:
Authors
Id | Name | Contact |
---|---|---|
1 | Maddy Montaquila | { “Phone”:”01632 12345″, “Address”: { “City”:”Camberwick Green”, “Country”:”UK”, “Postcode”:”CW1 5ZH”, “Street”:”1 Main St” } } |
2 | Jeremy Likness | { “Phone”:”01632 12346″, “Address”: { “City”:”Chigley”, “Country”:”UK”, “Postcode”:”CH1 5ZH”, “Street”:”2 Main St” } } |
3 | Daniel Roth | { “Phone”:”01632 12347″, “Address”: { “City”:”Camberwick Green”, “Country”:”UK”, “Postcode”:”CW1 5ZH”, “Street”:”3 Main St” } } |
4 | Arthur Vickers | { “Phone”:”01632 12348″, “Address”: { “City”:”Chigley”, “Country”:”UK”, “Postcode”:”CH1 5ZH”, “Street”:”15a Main St” } } |
5 | Brice Lambson | { “Phone”:”01632 12349″, “Address”: { “City”:”Chigley”, “Country”:”UK”, “Postcode”:”CH1 5ZH”, “Street”:”4 Main St” } } |
TIP This use of aggregates is very similar to the way JSON documents are mapped when using the EF Core provider for Azure Cosmos DB. JSON columns bring the capabilities of using EF Core against document databases to documents embedded in a relational database.
The JSON documents shown above are very simple, but this mapping capability can also be used with more complex document structures. For example, consider another aggregate type from our sample model, used to represent metadata about a post:
public class PostMetadata
{
public PostMetadata(int views)
{
Views = views;
}
public int Views { get; set; }
public List<SearchTerm> TopSearches { get; } = new();
public List<Visits> TopGeographies { get; } = new();
public List<PostUpdate> Updates { get; } = new();
}
public class SearchTerm
{
public SearchTerm(string term, int count)
{
Term = term;
Count = count;
}
public string Term { get; private set; }
public int Count { get; private set; }
}
public class Visits
{
public Visits(double latitude, double longitude, int count)
{
Latitude = latitude;
Longitude = longitude;
Count = count;
}
public double Latitude { get; private set; }
public double Longitude { get; private set; }
public int Count { get; private set; }
public List<string>? Browsers { get; set; }
}
public class PostUpdate
{
public PostUpdate(IPAddress postedFrom, DateTime updatedOn)
{
PostedFrom = postedFrom;
UpdatedOn = updatedOn;
}
public IPAddress PostedFrom { get; private set; }
public string? UpdatedBy { get; init; }
public DateTime UpdatedOn { get; private set; }
public List<Commit> Commits { get; } = new();
}
public class Commit
{
public Commit(DateTime committedOn, string comment)
{
CommittedOn = committedOn;
Comment = comment;
}
public DateTime CommittedOn { get; private set; }
public string Comment { get; set; }
}
This aggregate type contains several nested types and collections. Calls to OwnsOne
and OwnsMany
are used to map this aggregate type:
modelBuilder.Entity<Post>().OwnsOne(
post => post.Metadata, ownedNavigationBuilder =>
{
ownedNavigationBuilder.ToJson();
ownedNavigationBuilder.OwnsMany(metadata => metadata.TopSearches);
ownedNavigationBuilder.OwnsMany(metadata => metadata.TopGeographies);
ownedNavigationBuilder.OwnsMany(
metadata => metadata.Updates,
ownedOwnedNavigationBuilder => ownedOwnedNavigationBuilder.OwnsMany(update => update.Commits));
});
TIP
ToJson
is only needed on the aggregate root to map the entire aggregate to a JSON document.
With this mapping, EF7 can create and query into a complex JSON document like this:
{
"Views": 5085,
"TopGeographies": [
{
"Browsers": "Firefox, Netscape",
"Count": 924,
"Latitude": 110.793,
"Longitude": 39.2431
},
{
"Browsers": "Firefox, Netscape",
"Count": 885,
"Latitude": 133.793,
"Longitude": 45.2431
}
],
"TopSearches": [
{
"Count": 9359,
"Term": "Search #1"
}
],
"Updates": [
{
"PostedFrom": "127.0.0.1",
"UpdatedBy": "Admin",
"UpdatedOn": "1996-02-17T19:24:29.5429092Z",
"Commits": []
},
{
"PostedFrom": "127.0.0.1",
"UpdatedBy": "Admin",
"UpdatedOn": "2019-11-24T19:24:29.5429093Z",
"Commits": [
{
"Comment": "Commit #1",
"CommittedOn": "2022-08-21T00:00:00+01:00"
}
]
},
{
"PostedFrom": "127.0.0.1",
"UpdatedBy": "Admin",
"UpdatedOn": "1997-05-28T19:24:29.5429097Z",
"Commits": [
{
"Comment": "Commit #1",
"CommittedOn": "2022-08-21T00:00:00+01:00"
},
{
"Comment": "Commit #2",
"CommittedOn": "2022-08-21T00:00:00+01:00"
}
]
}
]
}
Queries into JSON columns
Queries into JSON columns work just the same as querying into any other aggregate type in EF Core. That is, just use LINQ! Here are some examples.
A query for all authors that live in Chigley:
var authorsInChigley = await context.Authors
.Where(author => author.Contact.Address.City == "Chigley")
.ToListAsync();
This query generates the following SQL when using SQL Server:
SELECT [a].[Id], [a].[Name], JSON_QUERY([a].[Contact],'$')
FROM [Authors] AS [a]
WHERE CAST(JSON_VALUE([a].[Contact],'$.Address.City') AS nvarchar(max)) = N'Chigley'
Notice the use of JSON_VALUE
to get the City
from the Address
inside the JSON document.
Select
can be used to extract and project elements from the JSON document:
var postcodesInChigley = await context.Authors
.Where(author => author.Contact.Address.City == "Chigley")
.Select(author => author.Contact.Address.Postcode)
.ToListAsync();
This query generates the following SQL:
SELECT CAST(JSON_VALUE([a].[Contact],'$.Address.Postcode') AS nvarchar(max))
FROM [Authors] AS [a]
WHERE CAST(JSON_VALUE([a].[Contact],'$.Address.City') AS nvarchar(max)) = N'Chigley'
Here’s an example that does a bit more in the filter and projection, and also orders by the phone number in the JSON document:
var orderedAddresses = await context.Authors
.Where(
author => (author.Contact.Address.City == "Chigley"
&& author.Contact.Phone != null)
|| author.Name.StartsWith("D"))
.OrderBy(author => author.Contact.Phone)
.Select(
author => author.Name + " (" + author.Contact.Address.Street
+ ", " + author.Contact.Address.City
+ " " + author.Contact.Address.Postcode + ")")
.ToListAsync();
This query generates the following SQL:
SELECT (((((([a].[Name] + N' (') + CAST(JSON_VALUE([a].[Contact],'$.Address.Street') AS nvarchar(max))) + N', ') + CAST(JSON_VALUE([a].[Contact],'$.Address.City') AS nvarchar(max))) + N' ') + CAST(JSON_VALUE([a].[Contact],'$.Address.Postcode') AS nvarchar(max))) + N')'
FROM [Authors] AS [a]
WHERE (CAST(JSON_VALUE([a].[Contact],'$.Address.City') AS nvarchar(max)) = N'Chigley' AND CAST(JSON_VALUE([a].[Contact],'$.Phone') AS nvarchar(max)) IS NOT NULL) OR ([a].[Name] LIKE N'D%')
ORDER BY CAST(JSON_VALUE([a].[Contact],'$.Phone') AS nvarchar(max))
And when the JSON document contains collections, then these can be projected out in the results:
var postsWithViews = await context.Posts.Where(post => post.Metadata!.Views > 3000)
.AsNoTracking()
.Select(
post => new
{
post.Author!.Name,
post.Metadata!.Views,
Searches = post.Metadata.TopSearches,
Commits = post.Metadata.Updates
})
.ToListAsync();
This query generates the following SQL:
SELECT [a].[Name], CAST(JSON_VALUE([p].[Metadata],'$.Views') AS int), JSON_QUERY([p].[Metadata],'$.TopSearches'), [p].[Id], JSON_QUERY([p].[Metadata],'$.Updates')
FROM [Posts] AS [p]
LEFT JOIN [Authors] AS [a] ON [p].[AuthorId] = [a].[Id]
WHERE CAST(JSON_VALUE([p].[Metadata],'$.Views') AS int) > 3000
TIP Consider creating indexes to improve query performance in JSON documents. For example, see Index Json data when using SQL Server.
Updating JSON columns
SaveChanges
and SaveChangesAsync
work in the normal way to make updates a JSON column. For extensive changes, the entire document will be updated. For example, replacing most of the Contact
document for an author:
var jeremy = await context.Authors.SingleAsync(author => author.Name.StartsWith("Jeremy"));
jeremy.Contact = new() { Address = new("2 Riverside", "Trimbridge", "TB1 5ZS", "UK"), Phone = "01632 88346" };
await context.SaveChangesAsync();
In this case, the entire new document is passed as a parameter:
info: 8/30/2022 20:21:24.392 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DbCommand (2ms) [Parameters=[@p0='{"Phone":"01632 88346","Address":{"City":"Trimbridge","Country":"UK","Postcode":"TB1 5ZS","Street":"2 Riverside"}}' (Nullable = false) (Size = 114), @p1='2'], CommandType='Text', CommandTimeout='30']
Which is then used in the UPDATE
SQL:
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
UPDATE [Authors] SET [Contact] = @p0
OUTPUT 1
WHERE [Id] = @p1;
However, if only a sub-document is changed, then EF Core will use a JSON_MODIFY
command to update only the sub-document. For example, changing the Address
inside a Contact
document:
var brice = await context.Authors.SingleAsync(author => author.Name.StartsWith("Brice"));
brice.Contact.Address = new("4 Riverside", "Trimbridge", "TB1 5ZS", "UK");
await context.SaveChangesAsync();
Generates the following parameters:
info: 10/2/2022 15:51:15.895 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DbCommand (2ms) [Parameters=[@p0='{"City":"Trimbridge","Country":"UK","Postcode":"TB1 5ZS","Street":"4 Riverside"}' (Nullable = false) (Size = 80), @p1='5'], CommandType='Text', CommandTimeout='30']
Which is used in the UPDATE
via a JSON_MODIFY
call:
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
UPDATE [Authors] SET [Contact] = JSON_MODIFY([Contact], 'strict $.Address', JSON_QUERY(@p0))
OUTPUT 1
WHERE [Id] = @p1;
Finally, if only a single property is changed, then EF Core will again use a “JSON_MODIFY” command, this time to patch only the changed property value. For example:
var arthur = await context.Authors.SingleAsync(author => author.Name.StartsWith("Arthur"));
arthur.Contact.Address.Country = "United Kingdom";
await context.SaveChangesAsync();
Generates the following parameters:
info: 10/2/2022 15:54:05.112 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DbCommand (2ms) [Parameters=[@p0='["United Kingdom"]' (Nullable = false) (Size = 18), @p1='4'], CommandType='Text', CommandTimeout='30']
Which are again used with a JSON_MODIFY
:
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
UPDATE [Authors] SET [Contact] = JSON_MODIFY([Contact], 'strict $.Address.Country', JSON_VALUE(@p0, '$[0]'))
OUTPUT 1
WHERE [Id] = @p1;
Limitations
The JSON support in EF7 lays the groundwork for fully-featured cross-provider JSON column support in future releases. However, the EF7 schedule means that some important features did not make it into EF7. These include:
Feature | Tracking GitHub Issue |
---|---|
JSON column support for SQLite. | #28816 |
Map spatial types to JSON. | #28811 |
Map collections of primitive types to JSON. | #28688 |
Support JSON columns when using TPT or TPC inheritance mapping. | #28443 |
Support more complex queries, such as querying into collections, that require jsonpath . |
#28616 |
Mapping attribute (aka data annotation) for property mapped to JSON column. | #28933 |
Configure serialization options for JSON columns. | #28043 |
We plan to implement these features in future releases. Make sure to vote for the appropriate GitHub issue for any feature that is particularly important to you.
The model
As noted above, the code shown in this post comes from the EF Core samples on GitHub. The entity types used in this post are shown below.
public class Blog
{
public Blog(string name)
{
Name = name;
}
public int Id { get; private set; }
public string Name { get; set; }
public List<Post> Posts { get; } = new();
}
public class Post
{
public Post(string title, string content, DateTime publishedOn)
{
Title = title;
Content = content;
PublishedOn = publishedOn;
}
public int Id { get; private set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PublishedOn { get; set; }
public Blog Blog { get; set; } = null!;
public List<Tag> Tags { get; } = new();
public Author? Author { get; set; }
public PostMetadata? Metadata { get; set; }
}
public class FeaturedPost : Post
{
public FeaturedPost(string title, string content, DateTime publishedOn, string promoText)
: base(title, content, publishedOn)
{
PromoText = promoText;
}
public string PromoText { get; set; }
}
public class Tag
{
public Tag(string id, string text)
{
Id = id;
Text = text;
}
public string Id { get; private set; }
public string Text { get; set; }
public List<Post> Posts { get; } = new();
}
public class Author
{
public Author(string name)
{
Name = name;
}
public int Id { get; private set; }
public string Name { get; set; }
public ContactDetails Contact { get; set; } = null!;
public List<Post> Posts { get; } = new();
}
public class ContactDetails
{
public Address Address { get; set; } = null!;
public string? Phone { get; set; }
}
public class Address
{
public Address(string street, string city, string postcode, string country)
{
Street = street;
City = city;
Postcode = postcode;
Country = country;
}
public string Street { get; set; }
public string City { get; set; }
public string Postcode { get; set; }
public string Country { get; set; }
}
public class PostMetadata
{
public PostMetadata(int views)
{
Views = views;
}
public int Views { get; set; }
public List<SearchTerm> TopSearches { get; } = new();
public List<Visits> TopGeographies { get; } = new();
public List<PostUpdate> Updates { get; } = new();
}
public class SearchTerm
{
public SearchTerm(string term, int count)
{
Term = term;
Count = count;
}
public string Term { get; private set; }
public int Count { get; private set; }
}
public class Visits
{
public Visits(double latitude, double longitude, int count)
{
Latitude = latitude;
Longitude = longitude;
Count = count;
}
public double Latitude { get; private set; }
public double Longitude { get; private set; }
public int Count { get; private set; }
public List<string>? Browsers { get; set; }
}
public class PostUpdate
{
public PostUpdate(IPAddress postedFrom, DateTime updatedOn)
{
PostedFrom = postedFrom;
UpdatedOn = updatedOn;
}
public IPAddress PostedFrom { get; private set; }
public string? UpdatedBy { get; init; }
public DateTime UpdatedOn { get; private set; }
public List<Commit> Commits { get; } = new();
}
public class Commit
{
public Commit(DateTime committedOn, string comment)
{
CommittedOn = committedOn;
Comment = comment;
}
public DateTime CommittedOn { get; private set; }
public string Comment { get; set; }
}
Summary
EF Core 7.0 (EF7) adds support for mapping aggregate types to JSON documents stored in “JSON columns” of a relational database. This allows relational databases to directly store documents while retaining the overall relational structure of the data. EF7 contains provider-agnostic support for JSON columns, with an implementation for SQL Server. The JSON in these columns can queried using LINQ, allowing filtering and sorting by the elements of the documents, as well as projection of elements out of the documents into results. In addition, EF7 supports element-level change tracking of the documents and partial updates for only the changed elements when SaveChanges
is called.
EF7 Prerequisites
- EF7 targets .NET 6, which means it can be used on .NET 6 (LTS) or .NET 7.
- EF7 will not run on .NET Framework.
EF7 is the successor to EF Core 6.0, not to be confused with EF6. If you are considering upgrading from EF6, please read our guide to port from EF6 to EF Core.
How to get EF7 RC2
EF7 is distributed exclusively as a set of NuGet packages. For example, to add the SQL Server provider to your project, you can use the following command using the dotnet tool:
dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 7.0.0-rc.2.22472.11
This following table links to the RC2 versions of the EF Core packages and describes what they are used for.
Package | Purpose |
---|---|
Microsoft.EntityFrameworkCore | The main EF Core package |
Microsoft.EntityFrameworkCore.SqlServer | Database provider for Microsoft SQL Server and SQL Azure |
Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite | SQL Server support for spatial types |
Microsoft.EntityFrameworkCore.Sqlite | Database provider for SQLite that includes the native binary for the database engine |
Microsoft.EntityFrameworkCore.Sqlite.Core | Database provider for SQLite without a packaged native binary |
Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite | SQLite support for spatial types |
Microsoft.EntityFrameworkCore.Cosmos | Database provider for Azure Cosmos DB |
Microsoft.EntityFrameworkCore.InMemory | The in-memory database provider |
Microsoft.EntityFrameworkCore.Tools | Visual Studio Package Manager Console commands for migrations and scaffolding |
dotnet-ef | dotnet command line tools for migrations and scaffolding |
Microsoft.EntityFrameworkCore.Design | Shared design-time components for EF Core tools |
Microsoft.EntityFrameworkCore.Proxies | Lazy-loading and change-tracking proxies |
Microsoft.EntityFrameworkCore.Abstractions | Decoupled EF Core abstractions |
Microsoft.EntityFrameworkCore.Relational | Shared EF Core components for relational database providers |
Microsoft.EntityFrameworkCore.Analyzers | C# analyzers for EF Core |
The 7.0 RC2 release of the Microsoft.Data.Sqlite.Core
ADO.NET provider is also available from NuGet.
Installing the EF7 Command Line Interface (CLI)
Before you can execute EF7 Core migration or scaffolding commands, you’ll have to install the CLI package as either a global or local tool.
To install the preview tool globally, install with:
dotnet tool install --global dotnet-ef --version 7.0.0-rc.2.22472.11
If you already have the tool installed, you can upgrade it with the following command:
dotnet tool update --global dotnet-ef --version 7.0.0-rc.2.22472.11
This new version of dotnet-ef
is supported for projects that use older versions of the EF Core runtime.
Daily builds
EF7 previews and RC releases are aligned with .NET 7 previews, which tend to lag behind the latest work on EF7. Consider using the daily builds instead to get the most up-to-date EF7 features and bug fixes.
The .NET Data Community Standup
The .NET data team is now live streaming every other Wednesday at 10am Pacific Time, 1pm Eastern Time, or 18:00 UTC. Join the stream learn and ask questions about the many .NET Data related topics.
- Watch our YouTube playlist of previous shows
- Visit the .NET Community Standup page to preview upcoming shows
- Submit your ideas for a guest, product, demo, or other content to cover
Documentation and Feedback
The starting point for all EF Core documentation is docs.microsoft.com/ef/.
Please file issues found and any other feedback on the dotnet/efcore GitHub repo.
Helpful Links
The following links are provided for easy reference and access.
- EF Core Community Standup Playlist: https://aka.ms/efstandups
- Main documentation: https://aka.ms/efdocs
- Issues and feature requests for EF Core: https://aka.ms/efcorefeedback
- Entity Framework Roadmap: https://aka.ms/efroadmap
- Bi-weekly updates: https://github.com/dotnet/efcore/issues/27185
Thank you from the team
A big thank you from the EF team to everyone who has used and contributed to EF over the years!
Welcome to EF7.
It looks like in the object above you show a List for Browsers but in the Json you have a string property specified: "Browsers": "Firefox, Netscape"
Is this an error? Trying to get a List to work but entity throws an exception saying it can't construct the object. Is this a bug or a known issue? Or does it simply not support a list of simple property? Changing it to a List...
After going to the github it looks like there’s a value converter for the comma separated string to convert to a List
I think it’s worth calling out that this would likely prevent querying by that property.
Just out of curiosity: why do you make this statement?
<code>
Why? Because you are currently unable to batch these calls? Stored procedures are the only way some code may run. I've being forced to rewrite thousands of C# lines, that took about 25 hours to execute, into a hundred lines of Transact SQL that ran in just thirty seconds. That general recommendation doesn't make any sense.
Thank you so much, this is amazing and long awaited!
Does it possible to use
JsonObject
orIEnumerable<JsonObject>
to define the Json Column ?I love JSON Columns, but when I add a new field, e.g. from class { string :field1 } to class { string field1, string? field2 } without changed the old data, it causes a runtime exception, unless I update the database from “{ “field1″:”value1″}” to “{ “field1″:”value1”, “field2″:”value2″}”. If this is a intended behavior, may I suggest you add some documentation about the best way handle this kind of changes.
That’s an impressive laundry list of improvements. EF Core has without a doubt matured into the most significant ORM on the market. Keep up the good work.