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 many 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".
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.