英文:
How does EF Core determine which parametrized entity constructor to use?
问题
I use the feature in EF Core 7.0 that allows using entities with parameterized constructors, described here.
However, I don't understand some behavior of EF Core when it comes to selecting which constructor to use.
For instance, I have this entity, with a readonly Id and thus a creation constructor, which generates the id and a reconstitution constructor with the Id as a parameter.
public class Poll
{
public Guid Id { get; }
public string Name { get; set; }
public DateTime ExpirationDate { 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.
英文:
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.
答案1
得分: 3
GitHub上的源代码显示,使用最少数量的标量参数的构造函数 - 您没有服务,只有标量属性参数。
从ConstructorBindingFactory
类的源代码中,这涉及到实体构造函数的选择:
> // 尝试找到具有最多服务属性的构造函数
> // 接着是最少标量属性参数
源代码中的单元测试演示了这种行为。
在下面的示例中,将选择具有2个参数的构造函数。
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)
{ }
}
关于构造函数中的List<Votes> votes
参数的错误,您之所以会遇到这个错误是因为EF Core无法使用构造函数设置导航属性,正如您链接到的文档中所提到的。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论