День шестьсот шестидесятый. #ЧтоНовенького #EFCore5
Вместе с .NET 5 выпущено множество обновлений. Про новшества в C#9 я писал в постах с тегом #CSharp9. Теперь рассмотрим, что нового в Entity Framework Core 5.0.
Отношение многие-ко-многим
EF Core 5.0 поддерживает отношения многие-ко-многим без явной привязки вспомогательной таблицы. Рассмотрим следующие сущности постов в блоге и тегов:
В отличие от EF6, EF Core также позволяет полностью настраивать вспомогательную таблицу. Например, приведенный ниже код настраивает отношение «многие-ко-многим» через вспомогательный объект, в котором вспомогательный объект также содержит свойство с полезными данными (
Вместе с .NET 5 выпущено множество обновлений. Про новшества в C#9 я писал в постах с тегом #CSharp9. Теперь рассмотрим, что нового в Entity Framework Core 5.0.
Отношение многие-ко-многим
EF Core 5.0 поддерживает отношения многие-ко-многим без явной привязки вспомогательной таблицы. Рассмотрим следующие сущности постов в блоге и тегов:
public class Post {
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Tag> Tags { get; set; }
}
public class Tag {
public int Id { get; set; }
public string Text { get; set; }
public ICollection<Post> Posts { get; set; }
}
Заметьте, что Post содержит коллекцию элементов Tag и наоборот. EF Core 5.0 по соглашению распознает это как отношение многие-ко-многим. То есть добавлять специальный код в OnModelCreating не требуется. Когда для создания базы данных используются миграции (или EnsureCreated), EF Core автоматически создаст вспомогательную таблицу. Например, в SQL Server для этой модели EF Core сгенерирует:CREATE TABLE [Posts] (Создание и связывание экземпляров объектов
[Id] int NOT NULL IDENTITY,
[Name] nvarchar(max) NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([Id])
);
CREATE TABLE [Tag] (
[Id] int NOT NULL IDENTITY,
[Text] nvarchar(max) NULL,
CONSTRAINT [PK_Tag] PRIMARY KEY ([Id])
);
CREATE TABLE [PostTag] (
[PostsId] int NOT NULL,
[TagsId] int NOT NULL,
…
);
Tag и Post приведёт к автоматическому обновлению вспомогательной таблицы. После вставки сообщений и тегов EF автоматически создаст строки во вспомогательной таблице. Для запросов Include и другие операции будут работать так же, как и для любых других отношений.В отличие от EF6, EF Core также позволяет полностью настраивать вспомогательную таблицу. Например, приведенный ниже код настраивает отношение «многие-ко-многим» через вспомогательный объект, в котором вспомогательный объект также содержит свойство с полезными данными (
PublicationDate):protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder
.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
j => j
.HasOne(pt => pt.Tag)
.WithMany()
.HasForeignKey(pt => pt.TagId),
j => j
.HasOne(pt => pt.Post)
.WithMany()
.HasForeignKey(pt => pt.PostId),
j => {
j.Property(pt => pt.PublicationDate)
.HasDefaultValueSql("CURRENT_TIMESTAMP");
j.HasKey(t => new { t.PostId, t.TagId });
});
}
Источник: https://docs.microsoft.com/ru-ru/ef/core/what-is-new/ef-core-5.0/whatsnew#many-to-manyДень шестьсот шестьдесят шестой. #ЗаметкиНаПолях #CSharp9
Атрибуты для Свойств Записей в C# 9
Записи обеспечивают простое создание неизменяемых объектов, особенно при использовании первичного конструктора:
Решение заключается в указании цели, к которой применяется атрибут. Как сказано в документации Microsoft:
Атрибуты могут быть применены к синтезированному автоматическому свойству или его вспомогательному полю, используя указатель цели атрибута property: или field: соответственно для атрибутов, синтаксически применяемых к соответствующему параметру записи.
В итоге мы получим следующую запись:
Теперь можно сериализовать нашу запись и получить желаемый результат:
Атрибуты для Свойств Записей в C# 9
Записи обеспечивают простое создание неизменяемых объектов, особенно при использовании первичного конструктора:
public record User(string Name, DateTime DOB);Это значительно сокращает код. Рассмотрим ситуацию, когда вы хотите сериализовать запись, чтобы получить следующий результат:
{"User":"Jon Smith","DateOfBirth":"1970-01-01T00:00:00"}
Заметьте, что ключи не совпадают с именами свойств записи. Обычно это потребовало бы добавления к свойствам записи атрибута JsonPropertyAttribute. То есть в полной записи это выглядело бы так:public record User {
[JsonProperty("User")]
public string Name{get;init;}
[JsonProperty("DateOfBirth")]
public DateTime DOB{get;init;}
}
При использовании первичного конструктора есть соблазн сделать аналогично:public record User(Но это неверно. В этом случае атрибуты добавятся к параметрам конструктора, а не к свойствам.
[JsonProperty("User")]
string Name,
[JsonProperty("DateOfBirth")]
DateTime DOB
);
Решение заключается в указании цели, к которой применяется атрибут. Как сказано в документации Microsoft:
Атрибуты могут быть применены к синтезированному автоматическому свойству или его вспомогательному полю, используя указатель цели атрибута property: или field: соответственно для атрибутов, синтаксически применяемых к соответствующему параметру записи.
В итоге мы получим следующую запись:
public record User(
[property:JsonProperty("User")]
string Name,
[property:JsonProperty("DateOfBirth")]
DateTime DOB
);
Теперь можно сериализовать нашу запись и получить желаемый результат:
var data = new User("Jon Smith",new DateTime(1970,1,1));
var serializedData = JsonConvert.SerializeObject(data);
// Вывод
{"User":"Jon Smith","DateOfBirth":"1970-01-01T00:00:00"}
Источник: https://www.c-sharpcorner.com/blogs/attributes-for-record-properties-in-c-sharp-9День шестьсот восьмидесятый. #ЧтоНовенького #CSharp9
Ещё Раз про Сопоставления с Образцом
Я уже писал об изменениях в сопоставлении с образцом в C#9. Здесь же хочу привести некое саммари всех изменений с примерами:
1. Шаблоны типа используются для сопоставления с типом. Если тип входных данных соответствует типу, указанному в шаблоне, совпадение считается успешным.
Ещё Раз про Сопоставления с Образцом
Я уже писал об изменениях в сопоставлении с образцом в C#9. Здесь же хочу привести некое саммари всех изменений с примерами:
1. Шаблоны типа используются для сопоставления с типом. Если тип входных данных соответствует типу, указанному в шаблоне, совпадение считается успешным.
object checkType = new int();2. Реляционные шаблоны позволяют сопоставить входные данные с константами, используя знаки
var getType = checkType switch {
string => "string",
int => "int",
_ => "obj"
};
Console.WriteLine(getType);
// Вывод: int
>, < или = (a также >= или <=):var person = new Person("John", 42);
var person2 = new Person("Jane", 8);
var ageInRange = person switch {
//тип Person указан явно
Person(_, < 18) => "меньше 18",
//тип выводится компилятором
(_, > 18) => "больше 18",
(_, 18) => "18!"
};
Console.WriteLine(ageInRange);
// Вывод: больше 18
3. Комбинаторные шаблоны позволяют комбинировать несколько шаблонов в одной строке:var person = new Person("John", 42);
- Конъюнктивные представляют собой логическое «и» двух подшаблонов:var ageInRange = person switch {
(_, < 18) => "меньше 18",
("John", _) and (_, > 18) => "Джону больше 18"
};
Console.WriteLine(ageInRange);
// Вывод: Джону больше 18
- Дизъюнктивные представляют собой логическое «или» двух подшаблонов:var ageInRange = person switch {
(_, < 18) => "меньше 18",
(_, 18) or (_, > 18) => "18 или больше"
};
Console.WriteLine(ageInRange);
// Вывод: 18 или больше
- Отрицательные требуют несовпадения с заданным шаблоном:var isJohn = person switch {
not ("John", 42) => "не Джон!",
_ => "Джон :)"
};
Console.WriteLine(isJohn);
// Вывод: Джон :)
4. В шаблонах допустимо использовать скобки:public record IsNumber(bool IsValid, int Number);Источник: https://www.c-sharpcorner.com/article/c-sharp-9-cheatsheet/
var num = new IsNumber(true, 10);
var zeroToTen = num switch {
((_, >= 0 and <= 5) or (_, > 5 and <= 9))
or (_, 10) => "от 0 до 10",
_ => "больше 10"
};
Console.WriteLine(zeroToTen);
// Вывод: от 0 до 10