Я сейчас читаю книгу, и я застрял прямо в начале конкретной главы. Из-за того, где я застрял, контекст можно найти в Интернете, не нарушая платный доступ. Соответствующий кодовый блок таков:
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; }
}
Немного, где я отстой, здесь
modelBuilder.Entity<Customer>(entity =>
{
entity.ToTable("Customer");
entity.Property(e => e.Name).IsRequired(); // Column is not nullable
});
Мои вопросы заключаются в следующем:
modelBuilder.Entity<Customer> выглядит как вызов функции. Что означает размещение лямбда-выражения внутри вызова функции?entity и почему это в рамках? Если это просто свободная переменная, то где в документации для modelBuilder.Entity<T> сказано, что она может принимать на вход такую конструкцию?Property(e => e.Name) фрагмента кода.modelBuilder.Entity<Customer> не вызов функции, то что это?Это означает, что функция ожидает в качестве параметра действие (для 1 и 2) или функцию (для 3 и 4), которые являются специальными типами делегатов. Лямбда-выражение — это всего лишь короткий способ создания встроенной функции и назначения ее делегату. Лямбда-выражения — это просто «синтаксический сахар».
@CristianRusanu Итак, хотя ничто не говорит о том, что лямбда-выражение с оператором, следующее за entity =>, является Action, компилятор волшебным образом знает, что это одно? Как?
@CristianRusanu Я убежден в 1 и 2, но никакая перегрузка entity.Property не принимает делегата (см. learn.microsoft.com/en-us/dotnet/api/… ), поэтому я все еще не понимаю entity.Property(e => e.Name).IsRequired().
Для свойства вы связываете документы для EntityTypeBuilder, но в коде используется производный тип EntityTypeBuilder<T>. У него есть перегрузка для Property, которая принимает Experssion<Func<T, TProperty>>, это тот, который используется. Теперь лямбда-функции НЕ являются просто синтаксическим сахаром для делегатов, потому что в зависимости от контекста они могут быть преобразованы либо в делегат, либо в дерево выражений. В этом случае Property ожидает дерево выражений (тип Expression<...>), поэтому лямбда преобразуется в него. Вы можете прочитать в Интернете о деревьях выражений в C#.
В общем, чтобы прояснить эти моменты, вы можете прочитать документацию: learn.microsoft.com/en-us/dotnet/csharp/language-reference/…
@Evk Единственная перегрузка для Property, которую я вижу, которая принимает Expression<Func<TEntity,TProperty>>, это Property<TProperty>(Expression<Func<TEntity,TProperty>>). Откуда вы знаете, что звонок Property на самом деле звонок Property<TProperty>? Я не вижу <> рядом с вызовом Property в коде, который я опубликовал.
В этом случае компилятор может вывести перегрузку из контекста. Вы передаете ему лямбду с одним аргументом, который возвращает строку. Единственная перегрузка, которая может это принять, — это перегрузка с Expression. Затем компилятор может вывести оттуда общие аргументы, поэтому вам не нужно указывать их явно.
@Evk Разве вы не можете найти ссылку на документацию, подтверждающую это? Это новая идея для меня. Если это так, у вас есть совершенно твердый ответ.
Как именно работает разрешение перегрузки, очень сложный вопрос. Вы можете прочитать документы, например, здесь: learn.microsoft.com/en-us/dotnet/csharp/language-reference/… . Однако на практике это почти всегда работает так, как вы ожидаете, по возможности находя лучшего кандидата, поэтому вам нужно копаться в деталях только в случае любопытства. Я имею в виду, что в вашей ситуации нет двусмысленности, и может совпадать только одна перегрузка, поэтому неудивительно, что она выбрана.
На самом деле это вопрос об основах программирования на C# и .NET, но он маскируется под вопрос EF. Подобные вопросы можно задать любому API, использующему лямбда-выражения, и в целом они не добавляют ценности.





Да modelBuilder.Entity<Customer> это функция с такой подписью Entity<TEntity>(Action<EntityTypeBuilder<TEntity>>) поэтому в основном она принимает другую "функцию" (здесь это действие делегата)
Из документации:
Эта перегрузка позволяет настроить тип объекта в строка в вызове метода, а не цепочка после вызова
Entity<TEntity>(). Это позволяет дополнительную настройку модели уровень для привязки после настройки типа сущности.
Таким образом, это в основном настраивает сущность, которую вы передали.
entity это имя параметра EntityTypeBuilder<TEntity> это не free переменная. Этот параметр будет передан внутри лямбды, указанной вами во время выполнения EF.
Здесь с помощью e => e.Name вы выбираете, какое свойство вы хотите изменить.
entity.Property(e => e.Name).IsRequired();
modelBuilder.Entity<Customer> Это вызов функции
Какая перегрузка Property вызывается? learn.microsoft.com/en-us/dotnet/api/…
Это просто перегруженный метод/конфигурация типа объекта, который принимает действие или функцию в качестве делегата.
В более ранней версии EF мы делаем это с помощью цепочки:
modelBuilder.Entity<Customer>().ToTable("Customer").Property(e => e.Name).IsRequired();
То же самое и с
modelBuilder.Entity<Customer>(entity =>
{
entity.ToTable("Customer");
entity.Property(e => e.Name).IsRequired(); // Column is not nullable
});
Но, как говорится в документации, это позволяет нам добавить дополнительную конфигурацию на уровне модели, которая будет связана после конфигурации для типа сущности.
Но, как правило, он делает то же самое, он настраивает модель, которая была обнаружена из типов сущностей, представленных в свойствах DBSet: ваша сущность Customer находится в таблице Customer из вашей базы данных, и требуется свойство Name.
Чтобы лучше понять и ответить на ваши вопросы № 1 и № 2, подумайте об этом:
Action<EntityTypeBuilder<Customer>> expression = (entity =>
{
entity.ToTable("Customer");
entity.Property(e => e.Name).IsRequired(); // Column is not nullable
});
modelBuilder.Entity<Customer>(expression);
Метод Entity ожидает делегата, лямбда-выражение просто используется для создания делегата Action.
Я не самый продвинутый парень в дотнете, но с точки зрения нуба я отвечу на то, что вы хотите, простой фразой: Scaffolding это пример мой друг
Когда мы используем скаффолдинг с командной строкой, EntityFramework генерирует в вашем файле контекста этот фрагмент кода.
Здесь modelBuilder.Entity — это набор DBSet, который я объявил AOC в своем файле контекста.
Теперь объект представляет собой таблицу, которую я создал в своем локальном SQL, и хочу, чтобы она была создана здесь, в коде (сначала ревер-инжиниринг базы данных, а затем код)
Свойство (e => e.Opt)
Свойство означает доступ к свойствам, которые у вас есть в вашей таблице
e => e.Opt : выражение ссылки означает, что этот объект AOC (таблица)
modelBuilder.Entity<Customer> конфигурация данной сущности/таблицы Customer
modelBuilder.Entity<AOC>(entity =>
{
entity.ToTable("AOC");
entity.Property(e => e.Opt)
.HasMaxLength(2)
.IsUnicode(false);
});
Подпись метода (modelBuilder.Entity<Customer>) следующая (Источник: MSDN):
public virtual Microsoft.EntityFrameworkCore.ModelBuilder Entity<TEntity> (Action<Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder<TEntity>> buildAction) where TEntity : class;
Для нас актуален аргумент buildAction. Это тип Action<EntityTypeBuilder<TEntity>>.
Action<T>определяется какpublic delegate void Action<in T>(T obj);
Это означает, что Action — это просто функция, которая принимает аргумент типа T и ничего не возвращает.
С лямбда-выражениями мы можем создавать анонимные функции, поэтому с entity => { ... } мы просто создаем анонимную функцию, которая принимает аргумент, который мы решили назвать entity, и его областью действия будет тело нашего выражения (итак, что внутри {...}) .
Тип аргумента entity выводится компилятором, поскольку он знает, что, как указано выше, аргумент modelBuilder.Entity<Customer> — это Action<EntityTypeBuilder<TEntity>>. Итак, entity будет экземпляром EntityTypeBuilder<TEntity>, поэтому мы можем использовать такие методы, как ToTable
С Property(e => e.Name) все немного сложнее...
Сигнатура метода следующая (Источник: MSDN):
public virtual Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder<TProperty> Property<TProperty> (System.Linq.Expressions.Expression<Func<TEntity,TProperty>> propertyExpression);
Опять же, для нас важен аргумент propertyExpression. Это тип Expression<Func<TEntity,TProperty>>.
Это означает, что это Выражение , описывающее функцию (очень похожую на Действие, описанное выше, но вместо того, чтобы ничего не возвращать, оно возвращает значение), которая принимает аргумент (который в нашем случае мы произвольно называем e ) типа TEntity и возвращает значение типа TProperty.
И TEntity, и TProperty выводятся компилятором: мы сказали выше, что entity является экземпляром EntityTypeBuilder<TEntity>, поэтому вывод, сделанный выше компилятором для определения TEntity, все еще действителен, потому что мы вызываем метод экземпляра entity.
TProperty выводится из лямбда-выражения e => e.Name. Это выражение эквивалентно e => { return e.Name; }.
По сути, это лямбда-выражение представляет собой функцию, которая принимает экземпляр Customer и возвращает его свойство Name. В определении класса Customer мы объявили Name как строку, поэтому компилятор знает, что TProperty — это string.
Но помните, что параметр, который мы передаем методу Property(Property(e => e.Name)), — это просто Expression, а не функция. По сути, код e.Name никогда не будет запущен, потому что это не скомпилированная функция. Компилятор выдаст дерево выражений, описывающее то, что мы на самом деле написали (см. раздел «Примечания» класса Expression ).
Затем Entity Framework проанализирует выражение, чтобы определить свойство вашего класса, которое необходимо отобразить в модели.
c# волшебным образом сохранит все, что нужно лямбда-функции в области видимости.