EF Core or Entity Framework Core is based upon the ORM or Object-Relational Mapper model. With the help of the ORM model, EF Core provides us with an intermediate layer between the domain model and the database objects. Microsoft treated EF Core as a data access-based API which helps us to communicate with the database by using .NET POCOs and Linq-type objects. While we use this Entity Framework in any application, it always takes less time to implement the CRUD-related layer with the database. In this section, we will mainly focus on the new features which are introduced in EF Core 7.0 version.
EF Core Architecture Design
Entity Framework Core or EF Core is a cross-platform-based, lightweight, and open-source version of our existing Entity Framework data access technology. In this architecture, we just need to focus on our programming code, and the rest operations related to the database handling are maintained by the EF Core itself. With the help of EF Core, we can perform many things like –
- Can load data by using C# class objects or entities.
- Can perform add, update, and delete operations by invoking related methods for those entities.
- Can map multiple database table objects into a single C# entity objects.
- Can handle the concurrent user scenario to update the same database table data.
- Can use LINQ query to fetch data from the database.
- Can establish a connection with different types of databases like SQL Server, SQLite, Azure Cosmos DB, PostgreSQL, etc.
- Can build and develop our domain model based on any existing database.
- Can maintain our database schema based on our domain model.
The below diagram represents the architecture structure behind the Entity Framework Core.
The DbContext is a special class that represents a unit of work and provides methods to configure options, connection strings, logging, and the model used to map your domain to the database. During the derive new classes from DbContext, it performs the following steps:
Establish an active connection session with the database.
Save and query instances of entities.
Include properties of type DbSet<T> representing tables in the database.
In EF Core, the provider always translates the objects into the SQL Commands at the time of execution. We can treat EF Core as a Provider of Database.
It Is a plug-in library designed for a specific database engine, such as SQL Server, Azure Cosmos DB, or PostgreSQL.
Translates method calls and LINQ queries to the database's native SQL dialect.
Extends EF Core to enable functionality that's unique to the database engine.
What’s new in EF Core 7.0?
When Microsoft introduced the new .Net Framework version 7.0, at the same time, they also introduced the new version of Entity Framework i.e. EF Core 7.0. EF Core 7.0 has many new features compared to the previous versions. Some of the major changes of EF Core in this new version are as below –
New Bulk Update and Bulk Delete Method
Before EF Core 7.0, we commonly use the SaveChanges method to perform save or update related operations in the database. This method normally identifies the changes in the entity objects and sends updated information to the database. So, in an earlier version of EF Core, if we want to update any record in the database, then first we need to query that record, make the changes in the result and then need to call the SaveChanges method. If we want to delete any records, we need to follow a similar workflow: retrieve the objects, change their state as deleted, and then call the SaveChanges.
Due to this workflow, for a long time, developers and community members request to bring some features quite similar to a LINQ query to directly push the changes into the database. So, in EF Core 7.0, Microsoft introduced two new methods, namely ExecuteUpdate and ExecuteDelete, for this purpose. Here, we need to remember that these two new method does not replace the SaveChanges method at all. These provide us with more flexibility while working with the database.
To delete any records, now, we can perform the code below –
context.Product.Where( s => s.ProductId == 100).ExecuteDelete();
Now, in the above example, we are deleting one single record. But in the same way, we can delete multiple records at a time by providing proper filter conditions in the LINQ expressions. And most importantly, this command will execute immediately and does not require invoking the SaveChanges method.
Just like the delete operation, we can perform the update operations as well. For that, we need to use the SetProperty method to assign the values of the change.
context.People
.Where(p => p.City == "Calcutta")
.ExecuteUpdate (s => s.SetProperty(c =>c.City, c =>"Kolkata"));
As per the above example, we are trying to update the people's city names from Calcutta to Kolkata. Executing this statement will update all records' city property values to a new property value in a single shot. While it executes the commands, it runs the below SQL commands in the database.
UPDATE [p]
SET [p].[City] = N'Kolkata'
FROM [People] AS [p]
WHERE [p].[City] = N'Calcutta'
SQL
Mapping JSON Type Column
In the case of a Database, using JSON Data type columns and storing data in that column is one of the most used and required features nowadays. By using this approach, we can store object-type data in a single column where we do not require a strict type relational concept. Entity Framework Core 7, now provides support for this JSON-based column which helps us to map with .Net object types with JSON documents. Also, we can perform the LINQ queries based on these types of JSON columns along with save and update changes. Although, this feature is still in the basic mode as updated by the team. Probably, in the next release, such a more advanced implementation will be incorporated into this functionality.
During the implementation, the support we want to use the Employee class in which we will use another child object called AddressInfo as below.
public class Employee
{
public int Id { get; set; }
public string FullName { get; set; }
public int Age { get; set; }
public Address AddressInfo { get; set; }
}
public class Address
{
public string State { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string PostalCode { get; set; }
}
Now, related to the Entity Employee, we need to use two different mappings for the Employee.AddressInfo property in the OnModelling method. In this method, OwnsOne mapping provided an overload which help us to mention the relationship of the owned property by using OwnedNavigationBuilder.
modelBuilder.Entity<Employee>()
.OwnsOne(p=> p.AddressInfo, jb => { jb.ToJson(); });
Performance Improvement on SaveChanges Method
In EF Core 6.0, one of the most important features was performance improvement for all types of non-tracking queries. At that time, the EF Core team promises that they will come up will more performance improvements in the next version i.e. EF Core 7. And now, they key their promise.
In EF Core normal or default transaction process, when we invoke the SaveChanges method, then it is performed with a database transaction so that if one statement fails to execute, then the entire process will be rollback. But when we send only a command related to a single table to update or insert, then also this process is followed, which is unnecessary as there is no need to use Transaction for a single statement. Now, in EF Core 7, while we perform SaveChanges in one single entity, then EF Core sends only one command (DDL statement) despite sending send 3 commands (BEGIN Transaction, DDL statement, Commit Transaction) to the database. Due to this, the performance in EF Core is must faster.
Another change is made in the case of Insert Statement in EF Core 7. As we know, while we perform any insert operation in EF Core, it executes two statements in the database part, and that is Insert Statement paid with a select statement to return the database generated value for any primary or foreign key. But now, in EF Core, the SQL Server makes compression on all of those commands to convert into a single command with the help of OUTPUT parameters in the insert statement.
SQL Command executed in EF Core 6.
INSERT INTO [Department] ([Name])
VALUES (@p0);
SELECT [DeptId]
FROM [Department]
WHERE @@ROWCOUNT = 1
AND [DeptId] = scope_identity();
Now, in EF Core 7, SQL Command is executed.
INSERT INTO [Department] ([Name])
OUTPUT INSERTED.[DeptId]
VALUES (@p0);
Mapping Stored Procedure
In the conventional Entity Framework, we can use the stored procedures with the entities. So, we invoke the SaveChanges method, EF called the stored procedure by pushing into the parameters. But, in EF Core 7, this process makes much simpler. In EF Core 7 version, several FluentAPI-related methods have been introduced, like InsertUsingStoredProcedure, UpdateUsingStoredProcedure, and DeleteUsingStoredProcedure. These methods can be applied within an entity in the OnModelCreating method. Each of the above mention methods takes an input string as a procedure name and a StoredProcedureBuilder object which contains different parameters which need to be passed into the stored procedure.
modelBuilder.Entity<Person>()
.InsertUsingStoredProcedure("uspPeopleInsert",
spbuilder => spbuilder
.HasParameter(p => p.PersonId, pb => pb.IsOutput().HasName("id"))
.HasParameter(p => p.FirstName)
.HasParameter(p => p.LastName)
)
Encrypt Defaults to True in SQL Server Connections
In EF Core 7, another breaking change is Encrypt value sets as true by default in the connection string. This is one of the breaking changes in Microsoft.Data.SqlClient packages. In the earlier version, the default value was Encrypt = false within the connection string. But now, in the latest version, this one is considered true by default. It means the Database server must be configured with a valid certificate, and the client must trust this certificate while establishing a connection with the server.
DB Context and API Enhancement
In EF Core 7.0, there are many small types of improvements done in the DBContext and its related classes. Some of them are as,
Now, uninitialized DbSet properties are automatically suppressed in EF Core 7. The default access type of the DbSet property is public. So, we use the DbSet property in the constructor, then it is automatically initialized by the DbContext.
Now, we can distinguish between the cancel the operation due to any hardware failure by using logs in the case of EF Core 7. This failure operation can be performed either by the application when it explicitly canceled any query or also by bypassing the CancellationToken passed to the operating methods.
Now, we can perform overloading against IProperty and INavigation in EntityEntry methods on the model class.
We can use EntityEntry for any shared entity. In that case, EF Core will use the same CLR type for all different entity types. These are commonly called "shared-type entity types", and are treated as a dictionary type with the key/value pairs used for the properties of the entity type.
Model Binding
In EF Core 7.0, there are many small types of improvements done in the Model Binding. Some of them are as,
Indexes can be used as ascending or descending
modelBuilder
.Entity<Post>()
.HasIndex(post => post.Title)
.IsDescending();
modelBuilder
.Entity<Blog>()
.HasIndex(blog => new { blog.Name, blog.Owner })
.IsDescending(false, true);
Can map composite key-related attribute
[PrimaryKey(nameof(PostId), nameof(CommentId))]
public class Comment
{
public int PostId { get; set; }
public int CommentId { get; set; }
public string CommentText { get; set; } = null!;
}
Can define the delete behavior as the mapping attribute for the relationship data. The default attribute value is DeleteBehaviour.Cascade. But that value can be changed to DeleteBehavior.NoAction.
public class Post
{
public int Id { get; set; }
public string? Title { get; set; }
[DeleteBehavior(DeleteBehavior.NoAction)]
public Blog Blog { get; set; } = null!;
}
Improved Code value generation
EF7 provides two significant improvements to the automatic generation of values for key field properties. In EF Core 7, key types based on value converters can use automatically generated key values so long as the underlying type supports this. This is configured in the normal way using ValueGeneratedOnAdd. EF7 also introduces support for a database sequence attached to the key's column default constraint. In its simplest form, this just requires telling EF Core to use a sequence for the key property