Что инициализирует DbSet?
Да. В EF Core, есть несколько классов, который за это отвечают.
DbSetInitializer (исходник): это агрегирующий класс, который используется в конструкторе DbContext . Самый интересный для нас участок кода:
private readonly IDbSetFinder _setFinder; private readonly IDbSetSource _setSource; // . public virtual void InitializeSets(DbContext context) < // тут поиск подходящих свойств foreach (var setInfo in _setFinder.FindSets(context.GetType()).Where(p =>p.Setter != null)) < // тут установка свойства setInfo.Setter.SetClrValue( context, // тут создание DbSet ((IDbSetCache)context).GetOrAddSet(_setSource, setInfo.ClrType)); >>
DbSetInitializer использует два дополнительный класса: DbSetFinder и DbSetSource .
DbSetFinder (исходник): ищет для нас подходящие свойства через reflection (имеющие тип DbSet<> ).
DbSetSource (исходник): он и создает наш DbSet объект:
private static Func CreateSetFactory() where TEntity : class => c => new InternalDbSet(c);
Также в DbSetInitializer используется ClrPropertySetterFactory (исходник), который через Expression Trees создает динамический метод для того, чтобы присвоить наш DbSet в нужное свойство (что-то очень близкое к context => context.DbSet = value ).
В итоге получается примерно следующий алгоритм:
- DbContext в конструкторе вызывает инициализацию свойств
- DbSetFinder находит нужные свойства
- DbSetSource создает нужный DbSet объект
- И через лямбду из ClrPropertySetterFactory мы сохраняем DbSet (полученный на предыдущем шаге) в нужно свойство.
Dbset c что это
Данное руководство устарело. Актуальное руководство: Руководство по Entity Framework Core
Последнее обновление: 24.10.2018
Чтобы непосредственно начать работать с Entity Framework, создадим первое приложение. Для этого нам нужна будет, во-первых, среда разработки. В качестве среды разработки выберем Visual Studio 2017.
В окне создания проекта в левой части выберем секцию Visual C#->Windows Desktop и в центральной части окна в качестве типа проекта выберем Console App (.NET Framework) .

Теперь первым делом добавим новый класс, который будет описывать данные. Пусть наше приложение будет посвящено работе с пользователями. Поэтому добавим в проект новый класс User:
public class User < public int Id < get; set; >public string Name < get; set; >public int Age < get; set; >>
Это обычный класс, который содержит некоторое количество автосвойств. Каждое свойство будет сопоставляться с отдельным столбцом в таблице из бд.
Надо отметить, что Entity Framework при работе с Code First требует определения ключа элемента для создания первичного ключа в таблице в бд. По умолчанию при генерации бд EF в качестве первичных ключей будет рассматривать свойства с именами Id или [Имя_класса]Id (то есть UserId). Если же мы хотим назвать ключевое свойство иначе, то нам нужно будет внести дополнительную логику на c#.
Теперь для взаимодействия с бд нам нужен контекст данных. Это своего рода посредник между бд и классами, описывающими данные. Но, у нас по умолчанию еще не добавлена библиотека для EF. Чтобы ее добавить, нажмем на проект правой кнопкой мыши и выберем в контекстном меню Manage NuGet Packages.

Затем в появившемся окне управления NuGet-пакетами в окне поиска введем слово «Entity» и выберем пакет собственно Entity Framework и установим его:

После установки пакета добавим в проект новый класс UserContext:
using System; using System.Collections.Generic; using System.Data.Entity; namespace FirstEF6App < class UserContext : DbContext < public UserContext() :base("DbConnection") < >public DbSet Users < get; set; >> >
Основу функциональности Entity Framework составляют классы, находящиеся в пространстве имен System.Data.Entity. Среди всего набора классов этого пространства имен следует выделить следующие:
- DbContext : определяет контекст данных, используемый для взаимодействия с базой данных.
- DbModelBuilder : сопоставляет классы на языке C# с сущностями в базе данных.
- DbSet/DbSet : представляет набор сущностей, хранящихся в базе данных
В любом приложении, работающим с БД через Entity Framework, нам нужен будет контекст (класс производный от DbContext) и набор данных DbSet, через который мы сможем взаимодействовать с таблицами из БД. В данном случае таким контекстом является класс UserContext.
В конструкторе этого класса вызывается конструктор базового класса, в который передается строка «DbConnection» — это имя будущей строки подключения к базе данных. В принципе мы можем не использовать конструктор, тогда в этом случае строка подключения носила бы имя самого класса контекста данных.
И также в классе определено одно свойство Users, которое будет хранить набор объектов User. В классе контекста данных набор объектов представляет класс DbSet . Через это свойство будет осуществляться связь с таблицей объектов User в бд.
И теперь нам надо установить подключение к базе данных. Для установки подключения обычно используется файл конфигурации приложения. В проектах для десктопных приложений файл конфигурации называется App.config (как в нашем случае), в проектах веб-приложений — web.config . В нашем случае, поскольку у нас консольное приложение, это файл App.config . После добавления Entity Framework он выглядит примерно следующим образом:
Содержимое файла в каждом конкретном случае может отличаться. Но в любом случае после добавления EntityFramework в проект в нем будет содержаться элемент configSections. И после закрывающего тега добавим следующий элемент:
Все подключения к источникам данных устанавливаются в секции connectionStrings , а каждое отдельное подключение представляет элемент add . В конструкторе класса контекста UserContext мы передаем в качестве названия подключения строку «DbConnection», поэтому данное название указывается в атрибуте name=»DBConnection» .
Настройку строки подключения задает атрибут connectionString . В данном случае мы устанавливаем название базы данных, с которой будем взаимодействовать — userstore.
Теперь перейдем к файлу Program.cs и изменим его содержание следующим образом:
using System; namespace FirstEF6App < class Program < static void Main(string[] args) < using(UserContext db = new UserContext()) < // создаем два объекта User User user1 = new User < Name = "Tom", Age = 33 >; User user2 = new User < Name = "Sam", Age = 26 >; // добавляем их в бд db.Users.Add(user1); db.Users.Add(user2); db.SaveChanges(); Console.WriteLine("Объекты успешно сохранены"); // получаем объекты из бд и выводим на консоль var users = db.Users; Console.WriteLine("Список объектов:"); foreach(User u in users) < Console.WriteLine(". - ", u.Id, u.Name, u.Age); > > Console.Read(); > > >
Так как класс UserContext через родительский класс DbContext реализует интерфейс IDisposable , то для работы с UserContext с автоматическим закрытием данного объекта мы можем использовать конструкцию using .
В конструкции using создаются два объекта User и добавляются в базу данных. Для их сохранения нам достаточно использовать метод Add : db.Users.Add(user1)
Чтобы получить список данных из бд, достаточно воспользоваться свойством Users контекста данных: db.Users
В результате после запуска программа выведет на консоль:
Объекты успешно сохранены Список объектов: 1.Tom - 33 2.Sam - 26
Таким образом, Entity Framework обеспечивает простое и удобное управление объектами из базы данных. При том в данном случае нам не надо даже создавать базу данных и определять в ней таблицы. Entity Framework все сделает за нас на основе определения класса контекста данных и классов моделей. И если база данных уже имеется, то EF не будет повторно создавать ее.
Наша задача — только определить модель, которая будет храниться в базе данных, и класс контекста. Поэтому данный подход называется Code First — сначала пишется код, а потом по нему создается база данных и ее таблицы.
Возникает вопрос, а где же находится БД? Чтобы физически увидеть базу данных, мы можем подключиться к ней из Visual Studio через окно View->SQL Server Object Explorer . После этого мы можем увидеть в SQL Server Object Explorer созданную базу данных, посмотреть ее строение, таблицы, открыть и даже изменить данные в таблицах:

Физически база данных по умолчанию будет располагаться в папке пользователя, в частности, у меня она размещена в каталоге C:\Users\Eugene\ , только к ее названию буде добавляться стандартное расширение mdf — userstore.mdf.
Dbset c что это
По умолчанию все типы сущностей, для которых определены в контексте данных наборы DbSet , включаются в модель и в дальнейшем сопоставляются с таблицами в базе данных. Например:
using Microsoft.EntityFrameworkCore; public class ApplicationContext : DbContext < public DbSetUsers < get; set; >= null!; public ApplicationContext() < Database.EnsureDeleted(); Database.EnsureCreated(); >protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) < optionsBuilder.UseSqlite("Data Source=helloapp.db"); >> public class User < public int Id < get; set; >public string? Name < get; set; >public int Age < get; set; >>
В данном случае, поскольку для класса User в классе контекста определено свойство типа DbSet
public DbSet Users < get; set; >= null!;
Таким образом, для сущности User будет создана таблица в бд.
Ссылочные nullable-типы и DbSet
Класс DbSet , как и другие типы, является ссылочным. А, начиная с C# 10 и .NET 6 автоматически применяется функциональность ссылочных nullable-типов. И переменные/свойства тех типов, которые не являются nullable, следует инициализировать некотором значением перед их использованием. Например, если мы напишем:
public DbSet Users
То мы столкнемся с предупреждением:

То есть нам надо инициализировать свойство типа DbSet. Хотя в этом нет большого смысла, так как контструктор базового класса DbContext гарантирует, что все свойства типа DbSet будут инициализированы и соответственно в принципе не будут иметь значение null .
Тем не менее проблема остается, поскольку мы сталкиваемся с предупреждением. Чтобы выйти из этой ситуации мы можем инициализировать свойство с помощью выражения null! , которое говорит, что данное свойство в принципе не будет иметь значение null:
public DbSet Users < get; set; >= null!;
Другое решение — инициализировать свойство значением типа Set :
public DbSet Users => Set();
Включение сущностей в модель без DbSet
Но кроме того, в модель также включаются типы, на которые есть ссылки в сущностях, которые уже включены в модель, например, через свойства DbSet.
Например, пусть у нас определены следующие сущности:
public class User < public int Id < get; set; >public string? Name < get; set; >public int Age < get; set; >// навигационное свойство public Company? Company < get; set; >> public class Company < public int Id < get; set; >public string? Name < get; set; >> public class Country < public int Id < get; set; >public string? Name < get; set; >>
И пусть у нас будет класс контекста данных:
using Microsoft.EntityFrameworkCore; public class ApplicationContext : DbContext < public DbSetUsers < get; set; >= null!; public ApplicationContext() < Database.EnsureDeleted(); Database.EnsureCreated(); >protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) < optionsBuilder.UseSqlite("Data Source=helloapp.db"); >>
После создания базы данных в ней будут созданы две таблицы: Users и Company. А третий класс — Country никак не используется в сущностях User и Company, для Country нет свойства DbSet в классе контекста, поэтому она не будет включена в контекст и для нее не будет создаваться таблица в бд.

Поскольку для типа User определен набор DbSet, то для имени таблицы будет применяться имя этого набора, а для второй таблицы будет использоваться имя класса Company.
Еще один способ включения сущности в модель представляет вызов Entity() объекта ModelBuilder в методе OnModelCreating() :
using Microsoft.EntityFrameworkCore; public class ApplicationContext : DbContext < public DbSetUsers < get; set; >= null!; public ApplicationContext() < Database.EnsureDeleted(); Database.EnsureCreated(); >protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) < optionsBuilder.UseSqlite("Data Source=helloapp.db"); >protected override void OnModelCreating(ModelBuilder modelBuilder) < modelBuilder.Entity(); > >
И если мы сейчас создадим и выполним миграции, то в базе данных будут уже три таблицы для сущностей:

Исключение из модели
Иногда возникают ситуации, когда надо, наоборот, исключить сущность из модели. Например, в примере выше сущность Company ссылается на класс Company, и, допустим, мы не хотим, чтобы в базе данных была таблица Company. В этом случае мы можем использовать Fluent API или аннотации данных.
Применение Fluent API заключается в вызове метода Ignore() :
using Microsoft.EntityFrameworkCore; public class ApplicationContext : DbContext < public DbSetUsers < get; set; >= null!; public ApplicationContext() < Database.EnsureDeleted(); Database.EnsureCreated(); >protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) < optionsBuilder.UseSqlite("Data Source=helloapp.db"); >protected override void OnModelCreating(ModelBuilder modelBuilder) < modelBuilder.Ignore(); > >
Аннотации данных предполагают установку над классом атрибута [NotMapped] :
using System.ComponentModel.DataAnnotations.Schema; public class User < public int Id < get; set; >public string? Name < get; set; >public int Age < get; set; >// навигационное свойство public Company? Company < get; set; >> [NotMapped] public class Company < public int Id < get; set; >public string Name < get; set; >>
При исключении сущности Company в базе данных будет только одна таблица Users, причем она не будет содержать столбца, который бы сопоставлялся со свойством Company класса User:
Db Set Класс
Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification="Name is intentional")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", Justification="Casing is intentional")] public abstract class DbSet : System.Data.Entity.Infrastructure.DbQuery
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification="Name is intentional")] public abstract class DbSet : System.Data.Entity.Infrastructure.DbQuery
type DbSet = class inherit DbQuery
Public MustInherit Class DbSet Inherits DbQuery
Наследование
Конструкторы
Создает экземпляр при вызове DbSet из конструктора производного типа, который будет использоваться в качестве тестового двойника для DbSets. Методы и свойства, которые будут использоваться тестовым двойником, должны быть реализованы тестом double, за исключением AsNoTracking, AsStreaming и Include, где реализация по умолчанию является неоднородной.
Свойства
Тип элемента IQueryable.
String Возвращает представление базового запроса.
Методы
Добавляет заданную сущность к контексту, поддерживающему набор, в добавленном состоянии, в результате чего она будет вставлена в базу данных при вызове метода SaveChanges.
Добавляет указанную коллекцию сущностей в контекст, лежащий в основе набора, при этом каждая сущность помещается в состояние Added таким образом, что она будет вставлена в базу данных при вызове Метода SaveChanges.
Возвращает новый запрос, в котором возвращенные сущности не будут кэшироваться в .DbContext
Является устаревшей.
Возвращает новый запрос, который будет выполнять потоковую передачу результатов вместо буферизации.
Присоединяет заданную сущность к контексту, поддерживающему данный набор. Это означает, что сущность помещается в контекст в неизмененном состоянии, как если бы она была считана из базы данных.
Возвращает эквивалентный универсальный DbSet объект.
Создает новый экземпляр сущности для типа данного набора. Обратите внимание, что этот экземпляр НЕ добавляется или не присоединяется к набору. Возвращаемый экземпляр будет учетной записью-посредником, если базовый контекст настроен для создания посредников, а тип сущности удовлетворяет требованиям для создания учетных записей-посредников.
Создает новый экземпляр сущности для типа данного набора или для типа, производного от типа данного набора. Обратите внимание, что этот экземпляр НЕ добавляется или не присоединяется к набору. Возвращаемый экземпляр будет учетной записью-посредником, если базовый контекст настроен для создания посредников, а тип сущности удовлетворяет требованиям для создания учетных записей-посредников.
Обнаруживает сущность с указанными значениями первичного ключа. Если сущность с указанными значениями первичного ключа содержится в контексте, она возвращается немедленно без выполнения запроса к хранилищу. В противном случае выполняется запрос к хранилищу в поисках сущности с указанными значениями первичного ключа. Если такая сущность обнаружена, она добавляется к контексту и возвращается вызывающей стороне. Если сущность не обнаружена в контексте или в хранилище, возвращается значение NULL.
Асинхронно находит сущность с заданными значениями первичного ключа. Если сущность с указанными значениями первичного ключа содержится в контексте, она возвращается немедленно без выполнения запроса к хранилищу. В противном случае выполняется запрос к хранилищу в поисках сущности с указанными значениями первичного ключа. Если такая сущность обнаружена, она добавляется к контексту и возвращается вызывающей стороне. Если сущность не обнаружена в контексте или в хранилище, возвращается значение NULL.
Асинхронно находит сущность с заданными значениями первичного ключа. Если сущность с указанными значениями первичного ключа содержится в контексте, она возвращается немедленно без выполнения запроса к хранилищу. В противном случае выполняется запрос к хранилищу в поисках сущности с указанными значениями первичного ключа. Если такая сущность обнаружена, она добавляется к контексту и возвращается вызывающей стороне. Если сущность не обнаружена в контексте или в хранилище, возвращается значение NULL.
Задает связанные объекты, включаемые в результаты запроса.
Помечает заданную сущность как удаленную, в результате чего она будет удалена из базы данных при вызове метода SaveChanges. Обратите внимание, что перед вызовом этого метода сущность должна существовать в контексте в каком-либо другом состоянии.
Удаляет указанную коллекцию сущностей из контекста, лежащего в основе набора, при этом каждая сущность помещается в состояние Deleted таким образом, что она будет удалена из базы данных при вызове Метода SaveChanges.
Создает необработанный SQL-запрос, возвращающий сущности в данном наборе. По умолчанию возвращаемые сущности отслеживаются контекстом; Это можно изменить, вызвав AsNoTracking в возвращаемом объекте DbRawSqlQuery . Обратите внимание, что возвращаемые сущности всегда относятся к типу для этого набора и никогда не имеют производного типа. Если запрашиваемая таблица или таблицы могут содержать данные других типов сущностей, SQL-запрос должен быть написан соответствующим образом, обеспечивая возврат только сущностей нужного типа.
Как и в случае с любым API, который принимает SQL, важно параметризовать любые входные данные пользователя для защиты от атак путем внедрения кода SQL. Вы можете включить заполнители параметров в строку ЗАПРОСА SQL, а затем указать значения параметров в качестве дополнительных аргументов. Все значения параметров, которые вы указали, будут автоматически преобразованы в DbParameter. Контексте. Set(typeof(Blog)). SqlQuery(«SELECT * FROM dbo. Posts WHERE Author = @p0», userSuppliedAuthor); Кроме того, можно создать DbParameter и передать его в SqlQuery. Это позволяет использовать именованные параметры в строке SQL-запроса. Контексте. Set(typeof(Blog)). SqlQuery(«SELECT * FROM dbo. Публикует where Author = @author», new SqlParameter(«@author», userSuppliedAuthor));
Возвращает представление String базового запроса.
Явные реализации интерфейса
Возвращает объект , IDbAsyncEnumerator который при перечислении будет выполнять запрос к базе данных.
Возвращает объект , IEnumerator который при перечислении будет выполнять запрос к базе данных.
Вызывает исключение, указывающее, что прямая привязка к запросу к хранилищу не поддерживается. Вместо этого заполните dbSet данными, например с помощью метода расширения Load, а затем привяжите его к локальным данным. Для WPF привязать к DbSet.Local. Для Windows Forms выполняйте привязку к DbSet.Local.ToBindingList().