The Entity Framework (EF) was first released as part of .NET Framework 3.5 with Service Pack 1 back in late 2008. Since then, it has evolved, as Microsoft has observed how programmers use an object-relational mapping (ORM) tool in the real world.
The version included with .NET Framework 4.6 is Entity Framework 6.1.3 (EF6). It is mature, stable, and supports the old EDMX design-time way of defining the model as well as complex inheritance models, and a few other advanced features. However, EF6 is only supported by the .NET Framework, not by the .NET Core.
The cross-platform version, Entity Framework Core (EF Core), is different. Microsoft has named it that way to emphasize that it is a reset of functionality. Although EF Core has a similar name, you should be aware that it currently varies from EF6.
Look at its pros and cons:
In Visual Studio 2017, press Ctrl + Shift + N or go to File | New | Project....
In the New Project dialog, in the Installed | Templates list, expand Visual C#, and select .NET Core. In the center list, select Console App (.NET Core), type name as Ch08_EFCore
, change the location to C:Code
, type solution name as Chapter08
, and then click on OK.
Right-click on Dependencies and choose Manage NuGet packages. In Package Manager, click on the Browse tab and, in the search box, enter Microsoft.EntityFrameworkCore.SqlServer
, and click on Install:
Review the changes, as shown in the following screenshot, and accept the license agreement:
Use Visual Studio Code to open the Ch08_EFCore
folder that you created earlier.
In Integrated Terminal, enter the dotnet new console
command.
In the Explorer pane, click on the Ch08_EFCore.csproj
file.
Add a package reference to EF Core for SQLite, as shown highlighted in the following markup:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference
Include="Microsoft.EntityFrameworkCore.Sqlite"
Version="1.1.1" />
</ItemGroup>
</Project>
In Integrated Terminal, enter the dotnet restore
command.
EF Core uses a combination of conventions, annotation attributes, and Fluent API statements to build a model at runtime so that any actions performed on the classes can later be automatically translated into actions performed on the actual database.
The code we will write will use the following conventions:
DbSet<T>
property in the DbContext
class, for example, Products
.ProductID
.nvarchar
type in the database.int
.NET type is assumed to be an int
type in the database.ID
or the name of the class with ID
as the suffix is assumed to be a primary key. If this property is any integer type or the Guid
type, then it is also assumed to be IDENTITY
(automatically assigned value when inserting).There are many other conventions, and you can even define your own, but that is beyond the scope of this book, and you can read about them at the following link: https://docs.microsoft.com/en-us/ef/core/modeling/
Conventions often aren't enough to completely map the classes to the database objects. A simple way of adding more smarts to your model is to apply annotation attributes.
For example, in the database, the maximum length of a product name is 40
, and the value cannot be null (empty). In a Product
class, we could apply attributes to specify this:
[Required] [StringLength(40)] public string ProductName { get; set; }
When there isn't an obvious map between .NET types and database types, an attribute can be used. For example, in the database, the column type of UnitPrice
for the Products
table is money. .NET does not have a money
type, so it should use decimal
instead:
[Column(TypeName = "money")] public decimal? UnitPrice { get; set; }
In the Category
table, the Description
column can be longer than the 8,000 characters that can be stored in an nvarchar
variable, so it needs to map to ntext
instead:
[Column(TypeName = "ntext")] public string Description { get; set; }
There are many other attributes, but they are beyond the scope of this book.
The last way that the model can be defined is using the Fluent API. It can be used instead of attributes or in addition to them. For example, look at the following two attributes in a Product
class:
[Required] [StringLength(40)] public string ProductName { get; set; }
They could be deleted and replaced with this Fluent API statement in the Northwind
class' OnModelBuilding
method:
modelBuilder.Entity<Product>() .Property(product => product.ProductName) .IsRequired() .HasMaxLength(40);
In both Visual Studio 2017 and Visual Studio Code, add three class files to the project named Northwind.cs
, Category.cs
, and Product.cs
.
Northwind.cs
should look like this:
using Microsoft.EntityFrameworkCore; namespace Packt.CS7 { // this manages the connection to the database public class Northwind : DbContext { // these properties map to tables in the database public DbSet<Category> Categories { get; set; } public DbSet<Product> Products { get; set; } protected override void OnConfiguring( DbContextOptionsBuilder optionsBuilder) { // for Microsoft SQL Server // optionsBuilder.UseSqlServer( // @"Data Source=(localdb)mssqllocaldb;" + // "Initial Catalog=Northwind;" + // "Integrated Security=true;"); // for SQLite optionsBuilder.UseSqlite( "Filename=../../../../Northwind.db"); } protected override void OnModelCreating( ModelBuilder modelBuilder) { // example of using Fluent API instead of attributes modelBuilder.Entity<Category>() .Property(category => category.CategoryName) .IsRequired() .HasMaxLength(40); } } }
Category.cs
should look like this:
using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; namespace Packt.CS7 { public class Category { public int CategoryID { get; set; } public string CategoryName { get; set; } [Column(TypeName = "ntext")] public string Description { get; set; } // defines a navigation property for related rows public virtual ICollection<Product> Products { get; set; } public Category() { this.Products = new List<Product>(); } } }
Product.cs
should look like this:
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Packt.CS7 { public class Product { public int ProductID { get; set; } [Required] [StringLength(40)] public string ProductName { get; set; } [Column(TypeName = "money")] public decimal? UnitPrice { get; set; } // these two define the foreign key relationship // to the Categories table public int CategoryID { get; set; } public virtual Category Category { get; set; } } }
Note that you did not need to include all columns from a table as properties on a class.
The two properties that relate the two entities, Category.Products
and Product.Category
, are both marked as virtual
. This allows EF to inherit and override them to provide extra features, such as lazy loading. Currently, EF Core does not support lazy loading, but EF6 does. Microsoft intends to add lazy loading support into EF Core soon.
18.116.10.201