Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does EF Core determine which parametrized entity constructor to use?

I use the feature in EF Core 7.0 that allow using entities with parametrized constructors, described here.

However, I don't understand some behaviour of EF Core when it comes to selecting which constructor to use.

For instance I have this entity, with readonly Id, and thus a creation constructor, which generates the id and a reconstitution constructor with the Id as parameter.

public class Poll
{
    public Guid Id { get; }
    public string Name {get;set;}
    public DateTime ExpirationDate { get; set; }
    public List<Vote> Votes { get; set; }
    public List<DateTime> dates {get;set;} = new List<DateTime>();

    public Poll(string name, List<DateTime> dates) : this(
        Guid.NewGuid(), 
        name,
        new List<Vote>(), 
        dates, 
        DateTime.UtcNow.AddMonths(2)
        )
    {
    }

    public Poll(Guid id, string name, List<DateTime> dates, DateTime expirationDate)
    {
        Name = name;
        Id = id;
        Dates = dates;
        ExpirationDate = expirationDate;
    }





}

with the following model binding :

       modelBuilder.Entity<Poll>(entity =>
        {
            entity.HasKey(poll => poll.Id);
            entity
                .Property(poll => poll.Id)
                .ValueGeneratedNever();
            entity.Property(poll => poll.Name);
            entity.Property(poll => poll.Dates);
            entity.Property(poll => poll.ExpirationDate);
        });

When querying this entity, EF Core will call the first (creation) constructor whereas, I would like (and would find it logical, according to the docs) that it would rather use the second (reconstitution) constructor. How come it's not using the constructor with a parameter for each mapped property, as per the docs :

However, if EF Core finds a parameterized constructor with parameter names and types that match those of mapped properties, then it will instead call the parameterized constructor with values for those properties and will not set each property explicitly.

EDIT : Experimented by removing the first constructor and it threw an exception saying that the votes parameter could not be bound to a property. It worked when removing it. However when adding back my first constructor, EF still goes back to using it instead of the other, so the question still stands.

like image 711
Ombrelin Avatar asked Oct 16 '25 16:10

Ombrelin


1 Answers

The source code on GitHub shows that the constructor with the least amount of scalar parameters gets used - you don't have services, only scalar property parameters.

From the source code of the ConstructorBindingFactory class, involved in the entity constructor selection:

// Trying to find the constructor with the most service properties
// followed by the least scalar property parameters

A unit test in the source code demonstrates this behavior.
In below example, the constructor with 2 parameters will be chosen.

public void Binds_to_least_parameters_if_no_services()
{
    var constructorBinding = GetBinding<BlogSeveral>();

    Assert.NotNull(constructorBinding);

    var parameters = constructorBinding.Constructor.GetParameters();
    var bindings = constructorBinding.ParameterBindings;

    Assert.Equal(2, parameters.Length);
    Assert.Equal(2, bindings.Count);

    Assert.Equal("title", parameters[0].Name);
    Assert.Equal("id", parameters[1].Name);

    Assert.Equal("Title", bindings[0].ConsumedProperties.First().Name);
    Assert.Equal("Id", bindings[1].ConsumedProperties.First().Name);
}

private class BlogSeveral : Blog
{
    public BlogSeveral(string title, int id)
    { }

    public BlogSeveral(string title, Guid? shadow, int id)
    { }

    public BlogSeveral(string title, Guid? shadow, bool dummy, int id)
    { }
}

About that error on the constructor with the List<Votes> votes argument, you get that because EF Core cannot set navigation properties using a constructor, as mentioned in the documentation you linked to.

like image 59
pfx Avatar answered Oct 18 '25 09:10

pfx



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!