One of the new features of Entity Framework Core 1.1 is that it allows mapping to fields. This is extremely useful when properties only have a get accessor. Previously, with properties get and set accessors were required. This article shows how you can implement mapping to fields using EF Core 1.1.
Creating a Model
Let’s start with a model. The type Book
defines the properties BookId
, Title
, and Publisher
. Because the book identifier and the publisher shouldn’t be changed after instantiating of the object, properties with only get accessors are defined. The properties are backed by private fields. Private members can only be accessed from within the class. Of course there’s an exception: EF Core needs to set these fields as well.
The
Title
property contains get and set accessors, there needs to be a way to change the title after the book is instantiated. I’m writing books about Microsoft technologies, and as it happens Microsoft changes product names after release candidates, so the book title changes as well 😉
public class Book { private Book() { } public Book(string title, string publisher) { Title = title; _publisher = publisher; } private int _bookId = 0; public int BookId => _bookId; public string Title { get; set; } private string _publisher; public string Publisher => _publisher; }
The entity type still needs a default constructor to be used with EF Core, but this constructor can be declared with the
private
accessor.
Creating the Context
With the context you need to map the properties with only get accessors to the field. This is done with the HasField
method of the PropertyBuilder
.
public class BooksContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer("server=(localdb)\mssqllocaldb;database=BooksSample;trusted_connection=true"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Book>().Property(b => b.BookId).HasField("_bookId"); modelBuilder.Entity<Book>().Property(b => b.Publisher).HasField("_publisher"); } public DbSet<Book> Books { get; set; } }
Using the Context
With this in place, the EF Core context can be used as expected. Creating the Book
object, the custom constructor can be used to fill in the title and publisher. Reading Book
objects from the database, EF Core instantiates the Book
object using the default constructor and directly sets the fields.
using (var context = new BooksContext()) { context.Database.EnsureCreated(); context.Books.Add(new Book("Professional C# 6 and .NET Core 1.0", "Wrox Press")); context.SaveChanges(); }
using (var context = new BooksContext()) { foreach (var book in context.Books) { Console.WriteLine($"{book.BookId} {book.Title} {book.Publisher}"); } }
You might wonder if readonly fields can be used as well. EF Core is accessing private fields from the outside, what about readonly fields? This would also allow using the new C# 6 syntax for readonly properties. I’Ve checked it – and no, readonly fields are not allowed for mapping. Fields need to be writeable for the mapping.
Mapping Fields without Properties
With this new feature to map columns to fields, it is also possible to not use any properties, and just to map columns from a table to fields. To demonstrate this, I extended the Book
class with a private field _internalState
. There’s no property that allows accessing this field. The field is set within the constructor, and retrieved from the ToString
method.
public class Book { private Book() : this(string.Empty, string.Empty) { } private string _internalState = string.Empty; public Book(string title, string publisher) { Title = title; _publisher = publisher; _internalState = "initialized"; } private int _bookId = 0; public int BookId => _bookId; public string Title { get; set; } private string _publisher; public string Publisher => _publisher; public override string ToString() => $"{Title}, {Publisher}, internal state: {_internalState}"; }
The OnModelCreating
method of the db context is extended calling the method Property
on the EntityTypeBuilder
using the string JustABackingField
. A property with such a name does not exist within the Book
type. The values are stored in the database and map to a column with the name JustABackingField
. Using the HasField
method, this column maps to the field _internalState
.
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Book>().Property(b => b.BookId).HasField("_bookId"); modelBuilder.Entity<Book>().Property(b => b.Publisher).HasField("_publisher"); modelBuilder.Entity<Book>().Property<string>("JustABackingField").HasField("_internalState"); }
The syntax to map fields without properties to columns is the same as for shadow properties that are possible with EF Core since the first version. Contrary to properties, now there’s a representation within the model.
Running the application, you can see a database table created with the column JustABackingField
that has a value from the field _internalState
.
To access the field that is mapped to a database column from outside of the model type, you can use the
ChangeTracker
. This is similar to using shadow properties.
Getting rid of the Field Names
Mapping to fields now allows using getter only properties, and its also possible to use fields without properties. What’s not so nice is that the name of private fields are used outside of the class. If the field name is changed, this leads to a breaking change. Because the field is private, the nameof
expression from C# 6 cannot be used in this case. Changing the field name and not the name in the mapping of the model, the compiler compiles successfully, but during runtime an exception is thrown. However, there’s a way around this. EF Core offers even better features mapping columns to fields.
Instead of using the HasField
method of the PropertyBuilder
, the following code snippet uses the UsePropertyAccessMode
method to specify to use a field during construction. Possible values of the PropertyAccessMode
enumeration are Field
to always use a field, both for read and write access, FieldDuringConstruction
where a property is used for read access, and the field for write access, and Property
to always use properties. The name of the field is retrieved from the property accessor.
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Book>().Property(b => b.Publisher).UsePropertyAccessMode(PropertyAccessMode.FieldDuringConstruction); modelBuilder.Entity<Book>().Property(b => b.BookId).UsePropertyAccessMode(PropertyAccessMode.FieldDuringConstruction); }
Summary
Entity Framework Core 1.1 gets some enhancements, and one of the really great enhancements are the mapping to fields. This allows creating a model with getter-only properties. Being afraid of making breaking changes too easily as the mapping might reference private field names, it is also possible to let EF Core find out the field name.
Sample Code
The sample code is available at GitHub. To build the sample code you need to have Preview 1 of .NET Core 1.1 installed.
Have fun with programming and learning!
Christian
More Information
More information about Entity Framework Core and C# is available in my new book and my C# workshops:
Professional C# 6 and .NET Core 1.0
Image from © Mykola Velychk | Dreamstime.com Golden field
Danke für die Ausführungen mit dem modelBuilder.Entity().Property(b => b.BookId).UsePropertyAccessMode(PropertyAccessMode.FieldDuringConstruction);
Nachdem ich ein EF Video auf channel9 [https://channel9.msdn.com/Shows/Visual-Studio-Toolbox/Entity-Framework-Core] gesehen hatte, dachte ich das Field Mapping funktioniere out-of-the-box und nur bei speziellen Namen bräuchte man eine entsprechende Konfiguration.
LikeLike
Danke auch für den Kommentar 🙂 Es hilft zu wissen welche Infos in meinen Blog Artikel besonders hilfreich sind. Danke!
Christian
LikeLike