0

I was reading the documentation about how to use Entity Core in a DDD way. This is the documentation.

It is said that I can configure EF to can map an internal private field to a private field, the backing field.

The code is this:

public class Order : Entity
{
    // Using private fields, allowed since EF Core 1.1
    private DateTime _orderDate;
    // Other fields ...

    private readonly List<OrderItem> _orderItems;
    public IReadOnlyCollection<OrderItem> OrderItems => _orderItems;

    protected Order() { }

    public Order(int buyerId, int paymentMethodId, Address address)
    {
        // Initializations ...
    }

    public void AddOrderItem(int productId, string productName,
                             decimal unitPrice, decimal discount,
                             string pictureUrl, int units = 1)
    {
        // Validation logic...

        var orderItem = new OrderItem(productId, productName,
                                      unitPrice, discount,
                                      pictureUrl, units);
        _orderItems.Add(orderItem);
    }
}

// At OrderingContext.cs from eShopOnContainers
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
   // ...
   modelBuilder.ApplyConfiguration(new OrderEntityTypeConfiguration());
   // Other entities' configuration ...
}

// At OrderEntityTypeConfiguration.cs from eShopOnContainers
class OrderEntityTypeConfiguration : IEntityTypeConfiguration<Order>
{
    public void Configure(EntityTypeBuilder<Order> orderConfiguration)
    {
        orderConfiguration.ToTable("orders", OrderingContext.DEFAULT_SCHEMA);
        // Other configuration

        var navigation =
              orderConfiguration.Metadata.FindNavigation(nameof(Order.OrderItems));

        //EF access the OrderItem collection property through its backing field
        navigation.SetPropertyAccessMode(PropertyAccessMode.Field);

        // Other configuration
    }
}

When EF set the access mode of the property, it is said: "//EF access the OrderItem collection property through its backing field".

So EF is accessing to the propery

If I am not wrong, backing fields are used to hide the interal structure and the business logic to outside, but here, EF can map the private field.

And in this case the code of the domain class shows the internal code and I know what is behind the public property, but strictly, if I would give to persistance layer only the public interface of the domain classes, how could EF know that the access mode has to be field if it can't know what there is behind the public property?

In the same documentation, if I continue reading, I can see that when they configure the mapping with fluent API, I can see this code:

class OrderEntityTypeConfiguration : IEntityTypeConfiguration<Order>
{
    public void Configure(EntityTypeBuilder<Order> orderConfiguration)
    {
        orderConfiguration.ToTable("orders", OrderingContext.DEFAULT_SCHEMA);

        orderConfiguration.HasKey(o => o.Id);

        orderConfiguration.Ignore(b => b.DomainEvents);

        orderConfiguration.Property(o => o.Id)
            .UseHiLo("orderseq", OrderingContext.DEFAULT_SCHEMA);

        //Address value object persisted as owned entity type supported since EF Core 2.0
        orderConfiguration
            .OwnsOne(o => o.Address, a =>
            {
                a.WithOwner();
            });

        orderConfiguration
            .Property<int?>("_buyerId")
            .UsePropertyAccessMode(PropertyAccessMode.Field)
            .HasColumnName("BuyerId")
            .IsRequired(false);

        orderConfiguration
            .Property<DateTime>("_orderDate")
            .UsePropertyAccessMode(PropertyAccessMode.Field)
            .HasColumnName("OrderDate")
            .IsRequired();

        orderConfiguration
            .Property<int>("_orderStatusId")
            .UsePropertyAccessMode(PropertyAccessMode.Field)
            .HasColumnName("OrderStatusId")
            .IsRequired();

        orderConfiguration
            .Property<int?>("_paymentMethodId")
            .UsePropertyAccessMode(PropertyAccessMode.Field)
            .HasColumnName("PaymentMethodId")
            .IsRequired(false);

        orderConfiguration.Property<string>("Description").IsRequired(false);

        var navigation = orderConfiguration.Metadata.FindNavigation(nameof(Order.OrderItems));

        // DDD Patterns comment:
        //Set as field (New since EF 1.1) to access the OrderItem collection property through its field
        navigation.SetPropertyAccessMode(PropertyAccessMode.Field);

        orderConfiguration.HasOne<PaymentMethod>()
            .WithMany()
            .HasForeignKey("_paymentMethodId")
            .IsRequired(false)
            .OnDelete(DeleteBehavior.Restrict);

        orderConfiguration.HasOne<Buyer>()
            .WithMany()
            .IsRequired(false)
            .HasForeignKey("_buyerId");

        orderConfiguration.HasOne(o => o.OrderStatus)
            .WithMany()
            .HasForeignKey("_orderStatusId");
    }

For example, when it maps the BuyerId, it tells that has a property BuyerId and use the field _buyerId. How could know the person who implements this mapping that Order has a field named _buyerId? it is a private field.

In sumary, I don't understand completly backing fields and the mapping, how the persistence layer can know what are behind the public prperty.

Thanks.

2
  • 1
    Entity Framework is an implementation of the Repository Pattern and the Unit of Work pattern. I suggest you treat it as such. Commented Aug 9, 2022 at 11:02
  • It's just based on a naming convention. C# has something called reflection, which is a mechanism that allows you to get type information at runtime; private members are visible to reflection. So the EF mapper can discover a private field based on the property name, if the naming follows a certain pattern (e.g. same name as the property, but starts with a lowercase letter, with the _ prefix). See this. You can also specify the backing field yourself. Commented Aug 9, 2022 at 12:06

1 Answer 1

1

If I am not wrong, backing fields are used to hide the interal structure and the business logic to outside, but here, EF can map the private field.

how could EF know that the access mode has to be field if it can't know what there is behind the public property?

You're right that private fields should be private. However, this doesn't mean that no one can access this information, it just means that (in general) they shouldn't.

Reflection is able to work around access modifiers in order to access things that you normally can't. Serializers and deserializers such as Newtonsoft.JSON and EF Core employ reflection in order to (de)serialize the objects you want (de)serialized.

This is how EF Core is able to "see" your private fields.

How could know the person who implements this mapping that Order has a field named _buyerId? it is a private field.

.Property<int?>("_buyerId")
.UsePropertyAccessMode(PropertyAccessMode.Field)

Notice that the code uses a string representation of the field name. If you were to use a real reference, e.g. x => x._buyerId, the compiler would refuse because it is a private field and this cannot be accessed in the syntax.

Reflection finds a way around the compiler, because reflection works at runtime. This explanation is very oversimplified, but reflection will at runtime look into the object's internals to figure out which fields it has, and then it can access the data contained within.

What .Property<int?>("_buyerId").UsePropertyAccessMode(PropertyAccessMode.Field) is instruct EF Core to go looking for a field named _buyerId using reflection during runtime. The compiler doesn't throw an error on this because this doesn't happen during compilation.

In short, EF Core uses a particular way to construct/deconstruct its entities which completely ignores the access modifiers you set on your fields and properties.

Why does EF Core do this? Specifically to make it so that EF Core can do its work without forcing you to make your fields public. Otherwise, you would be forced to make it all public just so EF Core could access it, at the cost of you no longer being able to rely on the privacy of your fields.


That being said, I am of the opinion that you should not be mapping your domain objects straight into EF and you should instead use a different entity on which you map your domain object; but this is not the main question being asked.

3
  • Yes, I was thinking to have persistance classes, and map the domain classes to persistance clases, that in theory is more correct, but it is more work, it is needed to think. At least you confirm that really that my idea that is is possible but no very correct, was right. Commented Aug 9, 2022 at 14:20
  • Mapping domain entities to database entities means making all those private fields public? That’s quite the sacrifice to make for encapsulation. Personally I don’t like configuring private fields on entities either, but for a different reason: it’s fragile because renaming fields does not rename the entity configuration, resulting in runtime errors. It’s a trade off, maybe make the fields internal?
    – Rik D
    Commented Aug 9, 2022 at 22:46
  • @RikD How you map is up for discussion. You could use reflection, provide some kind of map-friendly conversion, have the domain object pass its private data through a repo method (whose interface it defined, meaning the domain is still in control), ...
    – Flater
    Commented Aug 10, 2022 at 10:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.