I'm currently reading a book and I'm stuck right at the start of a particularly chapter. Because of where I'm stuck, the context can be found online without breaching the paywall. The relevant codeblock is this:
public class NutshellContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
public DbSet<Purchase> Purchases { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>(entity =>
{
entity.ToTable("Customer");
entity.Property(e => e.Name).IsRequired(); // Column is not nullable
});
modelBuilder.Entity<Purchase>(entity =>
{
entity.ToTable("Purchase");
entity.Property(e => e.Date).IsRequired();
entity.Property(e => e.Description).IsRequired();
});
}
}
public class Customer
{
public int ID { get; set; }
public string Name { get; set; }
public virtual List<Purchase> Purchases { get; set; }
= new List<Purchase>();
}
public class Purchase
{
public int ID { get; set; }
public int? CustomerID { get; set; }
public DateTime Date { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public virtual Customer Customer { get; set; }
}
The bit where I'm suck is here
modelBuilder.Entity<Customer>(entity =>
{
entity.ToTable("Customer");
entity.Property(e => e.Name).IsRequired(); // Column is not nullable
});
My questions are as follows:
modelBuilder.Entity<Customer>
appears to be a function call. What does it mean to place a lambda expression inside a function call?entity
and why is it in scope? If it is just a free variable, then where in the documentation for modelBuilder.Entity<T>
does it say that it can take such a construction as an input?Property(e => e.Name)
piece of code.modelBuilder.Entity<Customer>
isn't a function call, then what is it?The signature of the method (modelBuilder.Entity<Customer>
) is the following (Source: MSDN):
public virtual Microsoft.EntityFrameworkCore.ModelBuilder Entity<TEntity> (Action<Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder<TEntity>> buildAction) where TEntity : class;
What's relevant for us is the argument buildAction
. It's type is Action<EntityTypeBuilder<TEntity>>
.
Action<T>
is defined as public delegate void Action<in T>(T obj);
This means that Action is just a function that takes an argument of type T and returns nothing.
With lambda expressions we can create anonymous functions, so with entity => { ... }
we are just creating an anonymous function that takes in an argument that we decide to name entity
and its scope will be the body of our expression (so what's inside of {...}
).
The type of argument entity
is inferred by the compiler because it knows that, as stated above, the argument of modelBuilder.Entity<Customer>
is Action<EntityTypeBuilder<TEntity>>. So entity
will be an instance of EntityTypeBuilder<TEntity> therefore we can use methods like ToTable
About Property(e => e.Name)
things are a little bit more complicated...
The method signature is the following (Source: MSDN):
public virtual Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder<TProperty> Property<TProperty> (System.Linq.Expressions.Expression<Func<TEntity,TProperty>> propertyExpression);
Again, what's relevant for us is the argument propertyExpression
. It's type is Expression<Func<TEntity,TProperty>>
.
This means that it is an Expression that describes a function (very similar to the Action described above, but instead of returning nothing it returns a value) that takes in an argument (that in our case we arbitrarily name e
) of type TEntity
and returns a value of type TProperty
.
Both TEntity
and TProperty
are inferred by the compiler: We said above that entity
is an instance of EntityTypeBuilder<TEntity>
, so the inference made above by the compiler to determine TEntity
is still valid now because we are calling an instance method of entity
.
TProperty
is inferred from the lambda expression e => e.Name
. This expression is equivalent to e => { return e.Name; }
.
Basically this lambda expression is a function that takes in an instance of Customer
and returns its property Name
. In the definition of the class Customer we declared Name as string so the compiler knows that TProperty
is string
.
But remember that the parameter we are passing to the method Property
(Property(e => e.Name)
) is just an Expression
, not actually a function. Basically the code e.Name
will never be run because it is not a compiled function. The compiler will emit an expression tree describing what we actually wrote (see Remarks section of Expression class).
Then Entity Framework will parse the expression to determine the Property of your class that it will need to map in the Model.