Reputation: 1046
How does EF Core instantiate and initialize objects that are retrieved from a database? In the following example, the Person
class has a default constructor and a parameterized constructor. Neither of these constructors is called when my program retrieves the Person
object from the database.
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Person()
{
Console.WriteLine("Default ctor accessed");
}
public Person(string firstName, string lastName)
{
Console.WriteLine("Parameterized ctor accessed");
FirstName = firstName;
LastName = lastName;
}
}
// Program.cs
// Other code omitted for brevity
var personRead = context.Persons.First(p => p.FirstName.Equals("MyName"));
Console.WriteLine($"Firstname: {personRead.FirstName}");
This conflicts with the microsoft documentation:
When EF Core creates instances of these types, such as for the results of a query, it will first call the default parameterless constructor and then set each property to the value from the database. 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
The article was written on 14/10/2020 so it might be out of date. However my question still remains. How does EF core instantiate and initialize these objects? I'm using Microsoft.EntityFrameworkCore version 6.0.8
Upvotes: 2
Views: 760
Reputation: 205929
Neither of these constructors is called when my program retrieves the
Person
object from the database
This can't be true except you have another constructor(s) not shown here.
EF Core definitely uses class constructor for instantiating entities, so the documentation is correct, however it's unclear which one in case there are multiple. All it says is:
Note
Currently, all constructor binding is by convention. Configuration of specific constructors to use is planned for a future release.
The answer is hidden inside the implementation of the ConstructorBindingFactory
class which is responsible for selecting the constructor to be used, and more specifically the following comment inside GetBindings
method:
Trying to find the constructor with the most service properties followed by the least scalar property parameters
Service properties are explained in Injecting services section, and scalar property parameters refer to primitive type constructor arguments explained in Binding to mapped properties.
In your example, there is no constructor with service type arguments, so the one with the least scalar property parameters will be chosen, which in this case is the parameterless constructor (parameters count is 0)
public Person()
{
Console.WriteLine("Default ctor accessed");
}
Again, if you have other constructors, check if some of them is "better". But as it looks, if you have parameterless constructor (doesn't matter if public
, protected
, internal
, private
etc.) and no constructors with service type arguments, then EF Core will always use the parameterless one.
If you want, you can check the entity instantiation info using the metadata API, e.g.
var entityType = dbContext.Model.FindEntityType(typeof(Person));
var info = entityType.ConstructorBinding;
ConstructorBinding
property returns instance of abstract type InstantiationBinding
class. ParameterBindings
property returns information about parameters and corresponding properties, and CreateConstructorExpression
method which
Creates an expression tree that represents creating an entity instance from the given binding information. For example, this might be a
NewExpression
to call a constructor, or aMethodCallExpression
to call a factory method.
Currently there are two implementations of aforementioned abstract type - ConstructorBinding
class for instantiation using constructor, and FactoryMethodBinding
class for instantiation via factory method.
So, currently by convention EF Core uses constructor instantiation, but factory method instantiation might be added in the future (it might even be used currently by proxies extension, but it uses/requires parameterless constructor in the class they are inheriting).
Upvotes: 5