Entity Framework Core Shadow Properties

Shadow properties in Entity Framework Core are properties that do not feature as part of the entity class but can be included in the model and are mapped to database columns.

Shadow properties are useful in a number of scenarios. For example, they can be used for extending a model where you do not have access to the source code of the entity classes. They can also be used in cases where you prefer that the class definitions of your entities don't include "relational artifacts" such as foreign key columns or metadata properties like DateCreated or LastUpdated, or a rowversion property.

Shadow properties can be configured using the Fluent API in the OnModelCreating method. This example illustrates a LastUpdated shadow property being configured on the Contact entity:

public class SampleContext : DbContext
{
    public DbSet<Contact> Contacts { get; set; }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
         modelBuilder.Entity<Contact>()
            .Property<DateTime>("LastUpdated");
    } 
}

public class Contact
{
    public int ContactId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; } 
}

The version of the Property method that takes a string is used, and the data type of the property is specified as a type parameter. The shadow property will be included in migrations, resulting in a column being added to the Contacts table named "LastUpdated".

Entity Framework Core shadow property included in migration

Once a shadow property has been declared, it can be further configured using the Fluent API just like any other property in the model. The following code illustrates a Version shadow property being added to the Author entity in the OnModelCreating method, and then being configured to take part in concurrency management:

public class SampleContext : DbContext
{
    public DbSet<Author> Authors { get; set; }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
         modelBuilder.Entity<Author>()
            .Property<byte[]>("Version")
            .IsRowVersion();
    } 
}

public class Author
{
    public int AuthorId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public ICollection<Book> Books { get; set; }
}

Setting the value of shadow properties

You can access the shadow property via the DbContext.Entry property and set its value via the CurrentValue property:

var context = new SampleContext();
var contact = new Contact { FirstName = "John", LastName = "Doe" };
context.Add(contact);
context.Entry(contact).Property("LastUpdated").CurrentValue = DateTime.UtcNow;
context.SaveChanges();

You can also set values via the ChangeTracker API through its Entries() method. This offers a more logical way to set a LastUpdated value by overriding the SaveChanges method:

public class SampleCpntext:  DbContext
{
    protected override void OnModelBuilding(ModelBuider modelBuilder)
    {
        modelBuilder.Entity<Contact>()
            .Property<DateTime>("LastUpdated");
    }

    public override int SaveChanges()
    {
        ChangeTracker.DetectChanges();
 
        foreach (var entry in ChangeTracker.Entries())
        {
            if(entry.State == EntityState.Added || entry.State == EntityState.Modified)
            {
                entry.Property("LastUpdated").CurrentValue = DateTime.UtcNow;
            }
        }
        return base.SaveChanges();
    }

    public DbSet<Contact> Contacts { get; set; }
}

public class Contact
{
    public int ContactId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; } 
}

Querying with shadow properties

Shadow properties can be referenced in LINQ queries via the static Property method of the EF utility class:

var contacts = context.Contacts.OrderBy(contact => EF.Property<DateTime>(contact, "LastUpdated"));

Or, if you prefer to use the C# 6 using static directive:

using static Microsoft.EntityFrameworkCore.EF;
using static System.Console;

...

var contacts = context.Contacts.OrderBy(contact => Property<DateTime>(contact, "LastUpdated"));

Automatic generation of shadow properties

Shadow properties are generated by EF Core for dependant entities in relationships where there is no foreign key property defined in the dependent entity.


Created:
Last updated: 14/03/2017 16:33:42
Proficiency Level: Beginner