Shadow Properties In Entity Framework Core

Entity framework core added many new features that are already present in older entity framework versions. One of the features of Entity framework core is "Shadow Properties". The feature "Shadow Properties" was originally introduced in EF 7.

Introduction
 
Entity Framework Core has been equipped with many new features but there are some features that were already present in older EF versions. One of the features of Entity Framework Core is "Shadow Properties". The feature "Shadow Properties" was originally introduced in EF 7.
 
Shadow Properties are properties which are not present in our entity model class. The values can be changed and maintained by the Change Tracker API. They can also participate in LINQ to Entity query, database migration, and Create/Update operations. They are very useful when the data of some columns should not be exposed on the mapped entity types.
 
How To define Shadow Properties
 
Fluent API can help us to create and configure shadow properties. They are defined at overridable events of DBcontext called "OnModelCreating".
 
For example, I have an Employee table in the database and it has three columns - Id, Name, and CreatedDate and my Model is exposing only two properties: Id and Name; and also, we want to define "CreatedDate" as Shadow property. Using the following query, I have generated a table and test data.
  1. CREATE TABLE [dbo].[Employee](  
  2.     [Id] [intNOT NULL,  
  3.     [Name] [varchar](50) NULL,  
  4.     [CreatedDate] [datetime2](7) NOT NULL,  
  5.  CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED   
  6. (  
  7.     [Id] ASC  
  8. )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ONON [PRIMARY]  
  9. ON [PRIMARY]  
  10.   
  11. GO  
  12. SET ANSI_PADDING OFF  
  13. GO  
  14. INSERT [dbo].[Employee] ([Id], [Name], [CreatedDate]) VALUES (1, N'Jignesh', GETDATE())  
  15. INSERT [dbo].[Employee] ([Id], [Name], [CreatedDate]) VALUES (2, N'Rakesh', GETDATE())  
  16. INSERT [dbo].[Employee] ([Id], [Name], [CreatedDate]) VALUES (3, N'Tejas', GETDATE())  
  17. INSERT [dbo].[Employee] ([Id], [Name], [CreatedDate]) VALUES (4, N'Rajesh', GETDATE())  
Employee.cs
  1. using System.ComponentModel.DataAnnotations;  
  2. using System.ComponentModel.DataAnnotations.Schema;  
  3.   
  4. namespace ShadowProperties.Model  
  5. {  
  6.     [Table("Employee")]  
  7.     public class Employee  
  8.     {  
  9.         [Key]  
  10.         public int Id { getset; }  
  11.         public string Name { getset; }  
  12.     }  
  13. }  
When we query the employee entity, Entity Framework will automatically generate the query. To analyze the query, I just added console logging provider to DbContextOptionsBuilder.
 
EntityModelContext.cs
  1. using Microsoft.EntityFrameworkCore;  
  2. using Microsoft.Extensions.Logging;  
  3. using System;  
  4.   
  5. namespace ShadowProperties.Model  
  6. {  
  7.     public class EntityModelContext : DbContext  
  8.     {  
  9.         protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)  
  10.         {  
  11.             optionsBuilder.UseSqlServer(@"Server=(local);Database=Test;user Id=sa; password=Passwd@12;");  
  12.             LoggerFactory loggerFactory = new LoggerFactory();    
  13.             loggerFactory.AddConsole();    
  14.             optionsBuilder.UseLoggerFactory(loggerFactory);   
  15.         }  
  16.         protected override void OnModelCreating(ModelBuilder modelBuilder)  
  17.         {  
  18.               
  19.         }  
  20.         public DbSet<Employee> Employees { getset; }  
  21.     }  
  22. }  
To create shadow property, the first step is to override "OnModelCreating" event of DbContext and we can define shadow property by using "Property" method of modelBuilder.Entity class. Here, if the name passed to the "Property" method matches the name of existing entity model class property, then Entity Framework uses this existing property rather than creating a new shadow property.
 
The following code helps us to create shadow properties “CreatedDate” in Employee class.
  1. protected override void OnModelCreating(ModelBuilder modelBuilder)  
  2. {  
  3.     modelBuilder.Entity<Employee>().Property<DateTime>("CreatedDate");  
  4.     base.OnModelCreating(modelBuilder);  
  5. }  
How to use Shadow Property
 
As mentioned earlier, Shadow properties are not a part of entity model class and the value and state of Shadow properties are maintained by the Change Tracker API.
 
Use with Linq Query
 
Shadow property can also be used in LINQ query by using “EF.Property” static method. For example, the following code can be used to retrieve employee list order by created date.
  1. var data1 = context.Employees  
  2.             .OrderBy(b => EF.Property<DateTime>(b, "CreatedDate")).ToList();  
 
We can also retrieve the Shadow property with an anonymous type.
  1. var data1 = (from p in context.Employees  
  2.             select new  
  3.             {  
  4.                 Id = p.Id,  
  5.                 Name = p.Name,  
  6.                 CreateDate = EF.Property<DateTime>(p, "CreatedDate")  
  7.             }).ToList();  
 
Insert and Update the value in Shadow property
 
We can also insert/ update the value in Shadow properties using “EF.Property” static method.
  1. using (EntityModelContext context = new EntityModelContext())  
  2. {  
  3.     Employee emp = new Employee  
  4.     {  
  5.         Id = 5,  
  6.         Name = "Vishal",  
  7.     };  
  8.     context.Entry(emp).Property("CreatedDate").CurrentValue = DateTime.Now;  
  9.   
  10.     context.Add(emp);  
  11.     context.SaveChanges();  
  12. }  
 
  1. using (EntityModelContext context = new EntityModelContext())  
  2. {  
  3.     Employee emp = context.Employees.Where(p => p.Id == 5).FirstOrDefault();  
  4.     emp.Name = "Meera";  
  5.     context.Entry(emp).Property("CreatedDate").CurrentValue = DateTime.Now;  
  6.     context.SaveChanges();  
  7. }  

 
Summary
 
Shadow Properties are properties which are not present in our entity model class. The values can be changed and maintained by the Change Tracker API. Using fluent API, we can create shadow Properties, however we cannot create it using data annotations.