Given the following method-chain:
modelBuilder
.Entity<Student>()
.HasOne<StudentAddress>(s => s.Address)
.WithOne(ad => ad.Student);
How does HasOne
know about the Student
type (shown in lambda argument) provided by its previous method Entity<Student>
?
Seems to me as if Entity<Student>
somehow passes its designated type parameter Student
to the next method via chaining (return value of Entity<Student>
being the base object for next method HasOne
).
I'm guessing that you are calling this method:
public virtual EntityTypeBuilder<TEntity> Entity<TEntity>() where TEntity : class;
Look closely at the signature. The method takes a generic parameter called TEntity
, and returns a EntityTypeBuilder<TEntity>
, allowing you to chain any calls that can be called on EntityTypeBuilder<TEntity>
at the end of a call to Entity<TEntity>()
call.
In your case, you called Entity<Student>()
, so the signature dictates that the result must be a EntityTypeBuilder<Student>
. Then, you were able to call EntityTypeBuilder<Student>.HasOne
:
public virtual ReferenceNavigationBuilder<TEntity,TRelatedEntity> HasOne<TRelatedEntity> (Expression<Func<TEntity,TRelatedEntity>> navigationExpression = null) where TRelatedEntity : class;
Look at what HasOne
accepts - Expression<Func<TEntity,TRelatedEntity>>
. Because you are calling EntityTypeBuilder<Student>.HasOne
, TEntity
is Student
. The compiler sees your lambda expression and infers that s
must be Student
, because that's the only way that the lambda expression could be converted to Expression<Func<TEntity,TRelatedEntity>>
.
Also note that HasOne
returns a ReferenceNavigationBuilder<TEntity,TRelatedEntity>
, which allows you to chain other calls, and that now you have passed two pieces of type information TEntity
and TRelatedEntity
.
In fact, you don't need to specify the generic parameter for HasOne
, the compiler can infer those as well:
modelBuilder
.Entity<Student>()
.HasOne(s => s.Address)
.WithOne(ad => ad.Student);