Shadow Properties In Entity Framework Core

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 that 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 to 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 my Model exposes only two properties: Id and Name; and also, we want to define "CreatedDate" as a Shadow property. Using the following query, I have generated a table and test data.

CREATE TABLE [dbo].[Employee](
    [Id] [int] NOT NULL,
    [Name] [varchar](50) NULL,
    [CreatedDate] [datetime2](7) NOT NULL,
    CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED 
    (
        [Id] ASC
    ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO
SET ANSI_PADDING OFF
GO

INSERT [dbo].[Employee] ([Id], [Name], [CreatedDate]) VALUES (1, N'Jignesh', GETDATE())
INSERT [dbo].[Employee] ([Id], [Name], [CreatedDate]) VALUES (2, N'Rakesh', GETDATE())
INSERT [dbo].[Employee] ([Id], [Name], [CreatedDate]) VALUES (3, N'Tejas', GETDATE())
INSERT [dbo].[Employee] ([Id], [Name], [CreatedDate]) VALUES (4, N'Rajesh', GETDATE())

Employee. cs

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ShadowProperties.Model
{
    [Table("Employee")]
    public class Employee
    {
        [Key]
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

When we query the employee entity, Entity Framework will automatically generate the query. To analyze the query, I just added a console logging provider to DbContextOptionsBuilder.

EntityModelContext.cs

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;

namespace ShadowProperties.Model
{
    public class EntityModelContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(@"Server=(local);Database=Test;user Id=sa; password=Passwd@12;");
            LoggerFactory loggerFactory = new LoggerFactory();
            loggerFactory.AddConsole();
            optionsBuilder.UseLoggerFactory(loggerFactory);
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            
        }

        public DbSet<Employee> Employees { get; set; }
    }
}

To create a shadow property, the first step is to override the "OnModelCreating" event of DbContext and we can define shadow property by using the "Property" method of modelBuilder.Entity class. Here, if the name passed to the "Property" method matches the name of the 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 the Employee class.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Employee>().Property<DateTime>("CreatedDate");
    base.OnModelCreating(modelBuilder);
}

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 the “EF.Property” static method. For example, the following code can be used to retrieve employee list orders by created date.

var data1 = context.Employees
    .OrderBy(b => EF.Property<DateTime>(b, "CreatedDate"))
    .ToList();

Employee entity

We can also retrieve the Shadow property with an anonymous type.

var data1 = (from p in context.Employees
             select new
             {
                 Id = p.Id,
                 Name = p.Name,
                 CreateDate = EF.Property<DateTime>(p, "CreatedDate")
             }).ToList();

Entity model

Insert and Update the value in Shadow property

We can also insert/ update the value in Shadow properties using the “EF.Property” static method.

using (EntityModelContext context = new EntityModelContext())
{
    Employee emp = new Employee
    {
        Id = 5,
        Name = "Vishal",
    };
    context.Entry(emp).Property("CreatedDate").CurrentValue = DateTime.Now;

    context.Add(emp);
    context.SaveChanges();
}

Program file

using (EntityModelContext context = new EntityModelContext())
{
    Employee emp = context.Employees.Where(p => p.Id == 5).FirstOrDefault();
    emp.Name = "Meera";
    context.Entry(emp).Property("CreatedDate").CurrentValue = DateTime.Now;
    context.SaveChanges();
}

Command prompt

Summary

Shadow Properties are properties that 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 them using data annotations.


Similar Articles