Почему здесь допустимо размещение лямбда-выражения внутри вызова функции?

Я сейчас читаю книгу, и я застрял прямо в начале конкретной главы. Из-за того, где я застрял, контекст можно найти в Интернете, не нарушая платный доступ. Соответствующий кодовый блок таков:

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
});

Мои вопросы заключаются в следующем:

  1. modelBuilder.Entity<Customer> выглядит как вызов функции. Что означает размещение лямбда-выражения внутри вызова функции?
  2. Что такое entity и почему это в рамках? Если это просто свободная переменная, то где в документации для modelBuilder.Entity<T> сказано, что она может принимать на вход такую ​​конструкцию?
  3. Вышеуказанные два вопроса, но для Property(e => e.Name) фрагмента кода.
  4. Если modelBuilder.Entity<Customer> не вызов функции, то что это?

c# волшебным образом сохранит все, что нужно лямбда-функции в области видимости.

TaW 11.11.2022 22:37

Это означает, что функция ожидает в качестве параметра действие (для 1 и 2) или функцию (для 3 и 4), которые являются специальными типами делегатов. Лямбда-выражение — это всего лишь короткий способ создания встроенной функции и назначения ее делегату. Лямбда-выражения — это просто «синтаксический сахар».

Cristian Rusanu 11.11.2022 22:51

@CristianRusanu Итак, хотя ничто не говорит о том, что лямбда-выражение с оператором, следующее за entity =>, является Action, компилятор волшебным образом знает, что это одно? Как?

J. Mini 12.11.2022 14:56

@CristianRusanu Я убежден в 1 и 2, но никакая перегрузка entity.Property не принимает делегата (см. learn.microsoft.com/en-us/dotnet/api/… ), поэтому я все еще не понимаю entity.Property(e => e.Name).IsRequired().

J. Mini 12.11.2022 15:23
Этот
Mathieu Guindon 15.11.2022 03:21

Для свойства вы связываете документы для EntityTypeBuilder, но в коде используется производный тип EntityTypeBuilder<T>. У него есть перегрузка для Property, которая принимает Experssion<Func<T, TProperty>>, это тот, который используется. Теперь лямбда-функции НЕ являются просто синтаксическим сахаром для делегатов, потому что в зависимости от контекста они могут быть преобразованы либо в делегат, либо в дерево выражений. В этом случае Property ожидает дерево выражений (тип Expression<...>), поэтому лямбда преобразуется в него. Вы можете прочитать в Интернете о деревьях выражений в C#.

Evk 15.11.2022 08:50

В общем, чтобы прояснить эти моменты, вы можете прочитать документацию: learn.microsoft.com/en-us/dotnet/csharp/language-reference/…

Evk 15.11.2022 11:25

@Evk Единственная перегрузка для Property, которую я вижу, которая принимает Expression<Func<TEntity,TProperty>>, это Property<TProperty>(Expression<Func<TEntity,TProperty>>). Откуда вы знаете, что звонок Property на самом деле звонок Property<TProperty>? Я не вижу <> рядом с вызовом Property в коде, который я опубликовал.

J. Mini 15.11.2022 15:25

В этом случае компилятор может вывести перегрузку из контекста. Вы передаете ему лямбду с одним аргументом, который возвращает строку. Единственная перегрузка, которая может это принять, — это перегрузка с Expression. Затем компилятор может вывести оттуда общие аргументы, поэтому вам не нужно указывать их явно.

Evk 15.11.2022 16:19

@Evk Разве вы не можете найти ссылку на документацию, подтверждающую это? Это новая идея для меня. Если это так, у вас есть совершенно твердый ответ.

J. Mini 15.11.2022 21:51

Как именно работает разрешение перегрузки, очень сложный вопрос. Вы можете прочитать документы, например, здесь: learn.microsoft.com/en-us/dotnet/csharp/language-reference/… ‌​. Однако на практике это почти всегда работает так, как вы ожидаете, по возможности находя лучшего кандидата, поэтому вам нужно копаться в деталях только в случае любопытства. Я имею в виду, что в вашей ситуации нет двусмысленности, и может совпадать только одна перегрузка, поэтому неудивительно, что она выбрана.

Evk 16.11.2022 07:29

На самом деле это вопрос об основах программирования на C# и .NET, но он маскируется под вопрос EF. Подобные вопросы можно задать любому API, использующему лямбда-выражения, и в целом они не добавляют ценности.

Kit 23.11.2022 21:31
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
12
168
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Да 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/…

J. Mini 12.11.2022 15:15

Это просто перегруженный метод/конфигурация типа объекта, который принимает действие или функцию в качестве делегата.

В более ранней версии 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 это пример мой друг

  1. строительные леса

Когда мы используем скаффолдинг с командной строкой, EntityFramework генерирует в вашем файле контекста этот фрагмент кода.

  1. После генерации кода командой

Здесь 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 проанализирует выражение, чтобы определить свойство вашего класса, которое необходимо отобразить в модели.

Другие вопросы по теме