Использование Contains по коллекции в памяти. Постоянная перекомпиляция некоторых запросов

Обучение предполагает наличие знаний по работе с ASP.NET MVC в Visual Studio, в противном случае хорошее место для начала обучения ASP.NET MVC Tutorial . Если вы предпочитаете работать с ASP.NET Web Forms, обратите внимание на Getting Started with the Entity Framework и Continuing with the Entity Framework .

Перед началом удостоверьтесь в том, что у вас установлено следующее ПО:

  • Visual Studio 2010 SP1 или Visual Web Developer Express 2010 SP1 (если вы используете для установки одну из этих ссылок, следующий софт установится автоматически)
The Contoso University Приложение, которые вы разработаете, является простым вебсайтом университета.

Пользователи смогут просматривать и обновлять данные о студентах, курсах и преподавателях. Несколько скриншотов того, что получится, ниже.

UI близок по стилю к тому, что генерируется шаблонами по умолчанию, поэтому акцент будет на вопросах использования Entity Framework.

Подходы к разработке с Entity Framework Исхоя из диаграммы, имеется три подхода к работе с данными в Entity Framework: Database First , Model First , и Code First.

Database First В случае уже имеющейся базы данных Entity Framework может автоматически создать модель данных, состоящую из классов и свойств, соответствующих объектам базы даных (таким, как таблицы и столбцы). Информация о структуре базы (store schema), модель данных (conceptual model ) и маппинг их друг на друга содержится в XML в файле .edmx . Visual Studio предоставляет графический дизайнер Entity Framework, с помощью которого можно просматривать и редактировать .edmx . Части Getting Started With the Entity Framework и Continuing With the Entity Framework в материалах о Web Forms используют подход Database First.Model First Если базы нет, вы можете начать с создания модели данных, используя дизайнер Entity Framework Visual Studio. После окончания работ над моделью дизайнер сгенерирует DDL (data definition language )-код для создания базы. В этом подходе для хранения информации о модели и маппингах также используется .edmx . What"s New in the Entity Framework 4 включает небольшой пример разработки с использованием данного подхода.Code First Вне зависимости от наличия базы вы можете вручную написать код классов и свойств, соответствующих сущностям в базе и использовать этот код с Entity Framework без использования файла . edmx . Именно поэтому можно порой увидеть, как этот подход называют code only , хотя официальное наименование Code First. Маппинг между store schema и conceptual model в represented by your code is handled by convention and by a special mapping API. Если базы ещё нет, Entity Framework может создать, удалить или пересоздать её в случае изменений в модели.

API доступа к данным, разработанное для Code First, основано на классе DbContext . API может быть использован также и в процессе разработки с подходами Database First и Model First. Для дополнительной информации смотрите When is Code First not code first? В блоге команды разработки Entity Framework.

POCO (Plain Old CLR Objects) По умолчанию для подходов Database First и Model First классы модели данных наследуются от EntityObject , который и предоставляет функциональность Entity Framework. Это значит, что эти классы не являются persistence ignorant и, таким образом, не полностью соответствуют одном из условий domain-driven design . Все подходы к разработке с Entity Framework также могут работать с POCO (plain old CLR objects ), что, в целом, значит, что они являются persistence-ignorant из-за отсутствия наследования EntityObject .Создание веб-приложения MVC Откройте Visual Studio и создайте новый проект "ContosoUniversity", используя ASP.NET MVC 3 Web Application :

В New ASP. NET MVC 3 Project выберите шаблон Internet Application и движок представления Razor , снимите галочку с Create a unit test project и нажмите OK .

Настройка стилей Несколько небольших поправок изменят меню сайта, расположение элементов и домашнюю страницу.

Для настройки меню Contoso University, в Views\ Shared\_ Layout. cshtml замените текст в h 1 и ссылки в меню, как в примере:

@ViewBag.Title Contoso University @Html.Partial("_LogOnPartial")

@RenderBody()
В Views\Home\Index.cshtml удалите всё в теге h2 .

В Controllers\HomeController.cs замените "Welcome to ASP.NET MVC!" на "Welcome to Contoso University!"

В Content\ Site. css для смещения меню влево совершите следующие изменения:

  • В блок #main добавьте clear: both:
#main { clear: both; padding: 30px 30px 15px 30px; background-color: #fff; border-radius: 4px 0 0 0; -webkit-border-radius: 4px 0 0 0; -moz-border-radius: 4px 0 0 0; }
  • В блоках nav и #menucontainer добавьте clear: both; float: left:
nav, #menucontainer { margin-top: 40px; clear: both; float: left; }
Запустите проект.

Создание модели данных Далее создадим первые классы-сущности для Contoso University. Мы начнём со следующих трёх сущностей:

Установлена связь один-ко-многим между сущностями Student и Enrollment , и связь один-ко-многим между Course и Enrollment . Другими словами, студент может посещать любое количество курсов, и курс может иметь любое количество студентов, посещающих его.

В дальнейшем вы создадите классы для каждой из этих сущностей.

Note: компиляция проекта без созданных классов для этих сущностей вызовет ошибки компиляторов.

Сущность Student

В папке Models создайте Student. cs и замените сгенерированный код на:

Using System; using System.Collections.Generic; namespace ContosoUniversity.Models { public class Student { public int StudentID { get; set; } public string LastName { get; set; } public string FirstMidName { get; set; } public DateTime EnrollmentDate { get; set; } public virtual ICollection
Свойство StudentID будет первичным ключом соответствующей таблицы. По умолчанию, Entity Framework воспринимает свойство с ID или classname ID как первичный ключ.

Свойство Enrollments - navigation property . Navigation properties содержат другие сущности, относящиеся к текущей. В данном случае свойство Enrollments содержит в себе все сущности Enrollment , ассоциированные с текущей сущностью Student . Другими словами, если некая запись Student в базе данных имеет связь с двумя записями Enrollment (записей, содержащих значения первичных ключей для студента в поле внешнего ключа StudentID), свойство этой записи Enrollments будет содержать две сущности Enrollment .

Navigation properties обычно помечаются модификатором virtual дабы использовать возможность Entity Framework, называемую . (суть Lazy loading будет объяснена позже, в Reading Related Data) Если navigation property может содержать несколько сущностей (в связях многие-ко-многим и один-ко-многим), его тип должен быть ICollection .

Сущность Enrollment

В папке Models создайте Enrollment. cs со следующим содержанием:

Using System; using System.Collections.Generic; namespace ContosoUniversity.Models { public class Enrollment { public int EnrollmentID { get; set; } public int CourseID { get; set; } public int StudentID { get; set; } public decimal? Grade { get; set; } public virtual Course Course { get; set; } public virtual Student Student { get; set; } } }
Знак вопроса после указания типа decimal указывает на то, что свойство Grade nullable. Оценка, поставленная в null отличная от нулевой оценки- null обозначает то, что оценка еще не выставлена, тогда как 0 – уже значение.

Свойство StudentID является внешним ключом, и соответствующее navigation property Student . Сущность Enrollment ассоциирована с одной сущностью Student , поэтому свойство может содержать только одну сущность указанного типа (в отличие Student . Enrollments).

Свойство CourseID является внешним ключом, и соответствующее navigation property Course . Сущность Enrollment ассоциирована с одной сущностью Course .

Сущность Course

В папке Models создайтеCourse. cs со следующим содержанием:

Using System; using System.Collections.Generic; namespace ContosoUniversity.Models { public class Course { public int CourseID { get; set; } public string Title { get; set; } public int Credits { get; set; } public virtual ICollection Enrollments { get; set; } } }
Свойство Enrollments - navigation property. Сущность Course может ассоциироваться с бесконечным множеством сущностей Enrollment .

Создание Database Context Главный класс, координирующий функциональность Entity Framework для текущей модели данных называется database context . Данный класс наследуется от System . Data . Entity . DbContext . В коде вы определяете, какие сущности включить в модель данных, и также можете определять поведение самого Entity Framework. В нашем коде этот класс имеет название SchoolContext .

Создайте папку DAL и в ней новый класс SchoolContext. cs :

Using System; using System.Collections.Generic; using System.Data.Entity; using ContosoUniversity.Models; using System.Data.Entity.ModelConfiguration.Conventions; namespace ContosoUniversity.Models { public class SchoolContext: DbContext { public DbSet Students { get; set; } public DbSet Enrollments { get; set; } public DbSet Courses { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove(); } } }
Код создаёт свойство DbSet для каждого множества сущностей. В терминологии Entity Framework множество сущностей (entity set) относится к таблице базы данных, и сущность относится к записи в таблице.

Содержимое метода OnModelCreating защищает имена таблиц от плюрализации, и, если вы этого не делаете, то получаете такие имена таблиц, как Students , Courses , Enrollments . В ином случае имена таблиц будут Student , Course , Enrollment . Разработчики спорят на тему того, нужно ли плюрализовывать имена таблиц или нет. Мы используем одиночную форму, но важен тот момент, что вы можете выбрать, включать эту строчку в код или нет.

(Этот класс находится в namespace Models потому, что в некоторых ситуациях подход Code First подразумевает нахождение классов сущностей и контекста в одном и том же namespace.)

Определение Connection String Вам не нужно определять connection string. Если вы не определили эту строку, Entity Framework автоматически создаст базу данных SQL Server Express. Мы, однако, будем работать с SQL Server Compact, и вам необходимо будет создать строку подключения с указанием на это.

Откройте Web. config и добавьте новую строку подключения в коллекцию connectionStrings . (Убедитесь, что вы обновляете Web. config в корне проекта, так как есть еще один Web. config в папке Views, который трогать не надо .)


По умолчанию Entity Framework ищет строку подключения, названную также как object context class. Строка подключения, которую вы добавили, определяет базу данных School.sdf, находящуюся в папке App_data и SQL Server Compact.

Инициализация базы данных с тестовыми данными Entity Framework может автоматически создать базу данных при запуске приложения. Вы можете указать, что это должно выплоняться при каждом запуске приложения или только тогда, когда модель рассинхронизирована с существующей базой. Вы можете также написать класс с методом, который Entity Framework будет автоматически вызывать перед созданием базы для использования её с тестовыми данными. Мы укажем, что база должна удаляться и пересоздаваться при изменении модели.

В папке DAL создайте новый класс SchoolInitializer. cs с кодом, с помощью которого база будет создана при необходимости и заполнена тестовыми данными.

Using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Data.Entity; using ContosoUniversity.Models; namespace ContosoUniversity.DAL { public class SchoolInitializer: DropCreateDatabaseIfModelChanges { protected override void Seed(SchoolContext context) { var students = new List { new Student { FirstMidName = "Carson", LastName = "Alexander", EnrollmentDate = DateTime.Parse("2005-09-01") }, new Student { FirstMidName = "Meredith", LastName = "Alonso", EnrollmentDate = DateTime.Parse("2002-09-01") }, new Student { FirstMidName = "Arturo", LastName = "Anand", EnrollmentDate = DateTime.Parse("2003-09-01") }, new Student { FirstMidName = "Gytis", LastName = "Barzdukas", EnrollmentDate = DateTime.Parse("2002-09-01") }, new Student { FirstMidName = "Yan", LastName = "Li", EnrollmentDate = DateTime.Parse("2002-09-01") }, new Student { FirstMidName = "Peggy", LastName = "Justice", EnrollmentDate = DateTime.Parse("2001-09-01") }, new Student { FirstMidName = "Laura", LastName = "Norman", EnrollmentDate = DateTime.Parse("2003-09-01") }, new Student { FirstMidName = "Nino", LastName = "Olivetto", EnrollmentDate = DateTime.Parse("2005-09-01") } }; students.ForEach(s => context.Students.Add(s)); context.SaveChanges(); var courses = new List { new Course { Title = "Chemistry", Credits = 3, }, new Course { Title = "Microeconomics", Credits = 3, }, new Course { Title = "Macroeconomics", Credits = 3, }, new Course { Title = "Calculus", Credits = 4, }, new Course { Title = "Trigonometry", Credits = 4, }, new Course { Title = "Composition", Credits = 3, }, new Course { Title = "Literature", Credits = 4, } }; courses.ForEach(s => context.Courses.Add(s)); context.SaveChanges(); var enrollments = new List { new Enrollment { StudentID = 1, CourseID = 1, Grade = 1 }, new Enrollment { StudentID = 1, CourseID = 2, Grade = 3 }, new Enrollment { StudentID = 1, CourseID = 3, Grade = 1 }, new Enrollment { StudentID = 2, CourseID = 4, Grade = 2 }, new Enrollment { StudentID = 2, CourseID = 5, Grade = 4 }, new Enrollment { StudentID = 2, CourseID = 6, Grade = 4 }, new Enrollment { StudentID = 3, CourseID = 1 }, new Enrollment { StudentID = 4, CourseID = 1, }, new Enrollment { StudentID = 4, CourseID = 2, Grade = 4 }, new Enrollment { StudentID = 5, CourseID = 3, Grade = 3 }, new Enrollment { StudentID = 6, CourseID = 4 }, new Enrollment { StudentID = 7, CourseID = 5, Grade = 2 }, }; enrollments.ForEach(s => context.Enrollments.Add(s)); context.SaveChanges(); } } }
Метод Seed принимает объект контекста базы как входящий параметр и использует его для добавления новых сущностей в базу. Для каждого типа сущности код создает коллекцию новых сущностей, добавляя их в соответствующее свойство DbSet, и потом сохраняет изменения в базу. Нет необходимости в вызове SaveChanges после каждой группы сущностей, как сделано у нас, но это помогает определить проблему в случае возникновения исключений.

Измените Global. asax. cs для того, чтобы наш код вызывался при каждом запуске приложения:

  • Добавьте using:
using System.Data.Entity; using ContosoUniversity.Models; using ContosoUniversity.DAL;
  • В методе Application_Start вызовите метод Entity Framework, который запускает код инициализации базы:
Database.SetInitializer(new SchoolInitializer());
Приложение настроено таким образом, что при каждом первом обращении к базе данных после запуска приложения Entity Framework сравнивает базу с моделью (класс SchoolContext), и в случае рассинхронизации приложение удаляет и пересоздает базу.

Note при развертывании приложения на продакшн-сервер вы должны удалить весь код, который инициализирует базу тестовыми данными.

Далее вы создадите веб-страницу для отображения данных, и процесс запроса данных автоматически инициирует создание базы. Вы начнете с нового контроллера, но перед этим, соберите проект для того, чтобы модель и контекстные классы стали доступны для MVC controller scaffolding.

Создание контроллера Student Для создание контроллера Student , щелкните на папке Controllers в Solution Explorer , нажмите Add , Controller . В Add Controller совершите следующие действия и изменения и нажмите Add :
  • Controller name: StudentController .
  • Template: Controller with read/write actions and views, using Entity Framework . (по умолчанию.)
  • Model class: Student (ContosoUniversity.Models) . (если этого нет, пересоберите проект)
  • Data context class: SchoolContext (ContosoUniversity.Models) .
  • Views: Razor (CSHTML) . (по умолчанию)

Откройте Controllers\ StudentController. cs , вы увидите созданную переменную, инициализирующую объект контекста базы данных:

Private SchoolContext db = new SchoolContext();
Действие Index собирает список студентов из свойства Students из экземпляра контекста базы данных:

Public ViewResult Index() { return View(db.Students.ToList()); }
Автоматическое scaffolding было создано для множества Student . Для настройки заголовков и последовательности колонок откройте Views\ Student\ Index. cshtml и замените код на:

@model IEnumerable @{ ViewBag.Title = "Students"; } Students

@Html.ActionLink("Create New", "Create")

Last Name First Name Enrollment Date @foreach (var item in Model) { }
@Html.ActionLink("Edit", "Edit", new { id=item.StudentID }) | @Html.ActionLink("Details", "Details", new { id=item.StudentID }) | @Html.ActionLink("Delete", "Delete", new { id=item.StudentID }) @Html.DisplayFor(modelItem => item.LastName) @Html.DisplayFor(modelItem => item.FirstMidName) @Html.DisplayFor(modelItem => item.EnrollmentDate)

Запустите сайт, нажмите на вкладку Students .

Закройте браузер. В Solution Explorer выберите проект ContosoUniversity . Нажмите Show all Files , Refresh и затем разверните папку App_Data .

Два раза щелкните на School. sdf для открытия Server Explorer , и Tables .

Note если у вас возникает ошибка после того, как вы два раза щелкаете на School.sdf, убедитесь, что вы установили Visual Studio 2010 SP1 Tools for SQL Server Compact 4.0 . Если все установлено, перезапустите Visual Studio.

Для каждой таблицы есть свое множество сущностей + одна дополнительная таблица. EdmMetadata используется для определения Entity Framework, когда случилась разсинхронизация модели и базы.

Щелкните на одной из таблиц и Show Table Data чтобы увидеть загруженные классом SchoolInitializer данные.

Закройте подключение, иначе может возникнуть проблема с запуском приложения.

Соглашения Количество кода, нужное для создания Entity Framework базы, минимально из-за использования (conventions) Entity Framework. Некоторые из них уже были упомянуты:
  • Форма множественного числа имен классов сущностей используется в качестве имен таблиц.
  • Имена свойств сущностей используется в качестве имен столбцов.
  • Свойства сущностей с именами ID или classname ID распознаются как первичные ключи.
  • Entity Framework подключается к базе, отыскав строку подключения с таким же именем, как и класс контекста (в данном случае SchoolContext).
Вы видели, что данные соглашения могут быть перекрыты (допустим, плюрализацию можно отключить) и вы можете узнать больше о том, как это делать, из

Самым известным, функциональным и широко используемым ORM в мире .NET является Entity Framework . Для работы с .NET Core была создана версия EF Core . Принцип работы EF Core остался тем же что у его предшественников, но это уже другая технология, так что сейчас ожидать полного набора функционала из Entity Framework 6 не приходится, но разработка проекта продолжается весьма активно. Из данной статьи вы узнаете как быстро приступить к использованию Entity Framework Core в ASP.NET Core проектах.

Для начала работы с EF Core необходимо установить необходимые библиотеки. Добавим пакеты Microsoft.EntityFrameworkCore.SqlServer и Microsoft.EntityFrameworkCore.Tools .

Для начала необходимо определиться с данными, которые будут храниться в базе данных. Я добавлю 2 класса User и Log , которые будут отвечать за данные пользователя и какие-то данные лога.

Public class User { public Guid Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Email { get; set; } } public class Log { public long Id { get; set; } public DateTime UtcTime { get; set; } public string Data { get; set; } }

После этого можно создать контекст для работы с базой данных:

Class TestDbContext: DbContext { public TestDbContext(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity() .HasIndex(b => b.Email); } public DbSet Users { get; set; } public DbSet Logs { get; set; } }

DbContext — данный класс определяет контекст данных, используемый для работы с базой данных.

DbSet — представляет коллекцию всех сущностей указанного типа, которые содержатся в контексте или могут быть запрошены из базы данных.

Сейчас необходимо настроить подключение к базе данных. Для этого откроем файл Startup.cs и в метод ConfigureServices добавим строку:

Services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

Строку подключения зададим в файле конфигурации, по умолчанию это appsettings.json

"ConnectionStrings": { "DefaultConnection": "СТРОКА_ПОДКЛЮЧЕНИЯ_ЗДЕСЬ" }

Создание базы данных

Откроем Package Manager Console

И выполним команду Add-Migration InitialCreate

Это создаст файлы необходимые для создания структуры базы данных. Созданные файлы можно увидеть в созданной директории Migrations .

После этого выполним команду Update-Database и база будет создана.

Добавление логирования запросов

Сейчас давайте добавим логирование для всех API запросов. Для этого добавим класс LoggingMiddleware со следующим содержанием:

Public class LoggingMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; public LoggingMiddleware(RequestDelegate next, ILogger logger) { _next = next; _logger = logger; } public async Task Invoke(HttpContext context, TestDbContext dbcontext) { if (context != null && context.Request.Path.ToString().ToLower().StartsWith("/api")) { using (var loggableResponseStream = new MemoryStream()) { var originalResponseStream = context.Response.Body; context.Response.Body = loggableResponseStream; try { var request = await FormatRequest(context.Request); await _next(context); var response = await FormatResponse(loggableResponseStream); loggableResponseStream.Seek(0, SeekOrigin.Begin); var newLog = new Log { Path = HttpUtility.UrlDecode(context.Request.Path + context.Request.QueryString), UtcTime = DateTime.UtcNow, Data = request, Response = response, StatusCode = context.Response.StatusCode, }; await loggableResponseStream.CopyToAsync(originalResponseStream); await dbcontext.Logs.AddAsync(newLog); dbcontext.SaveChanges(); } catch (Exception ex) { //Здесь можно добавить логирование ошибок throw; } finally { context.Response.Body = originalResponseStream; } } } } private static async Task FormatRequest(HttpRequest request) { request.EnableRewind(); string responseBody = new StreamReader(request.Body).ReadToEnd(); request.Body.Position = 0; return responseBody; } private static async Task FormatResponse(Stream loggableResponseStream) { loggableResponseStream.Position = 0; var buffer = new byte; await loggableResponseStream.ReadAsync(buffer, 0, buffer.Length); return JsonConvert.SerializeObject(Encoding.UTF8.GetString(buffer)); } }

А в файле Startup.cs в методе Configure добавим:

App.UseMiddleware();

После этого все запросы, которые начинаются с /api будут логироваться в базу данных. Здесь стоит заметить, что вариантов логирования может быть множество. Здесь представлен лишь один из вариантов, который легко реализуем и не требует использования сторонних библиотек либо сервисов.

Еще добавим контроллер для работы с пользователями, добавим там метод для получения информации обо всех пользователях.

")] public class UsersController: Controller { private readonly TestDbContext _context; public UsersController(TestDbContext context) { _context = context; } public async Task GetAll() { var users = await _context.Users.ToListAsync(); return Ok(users); } }

Это базовая информация, которая позволит вам быстро начать работать с Entity Framework Core .

Приятного программирования.

Последнее обновление: 31.10.2015

Entity Framework представляет специальную объектно-ориентированную технологию на базе фреймворка.NET для работы с данными. Если традиционные средства ADO.NET позволяют создавать подключения, команды и прочие объекты для взаимодействия с базами данных, то Entity Framework представляет собой более высокий уровень абстракции, который позволяет абстрагироваться от самой базы данных и работать с данными независимо от типа хранилища. Если на физическом уровне мы оперируем таблицами, индексами, первичными и внешними ключами, но на концептуальном уровне, который нам предлагает Entity Framework, мы уже работает с объектами.

Первая версия Entity Framework - 1.0 вышла еще в 2008 году и представляла очень ограниченную функциональность, базовую поддержку ORM (object-relational mapping - отображения данных на реальные объекты) и один единственный подход к взаимодействию с бд - Database First. С выходом версии 4.0 в 2010 году многое изменилось - с этого времени Entity Framework стал рекомендуемой технологией для доступа к данным, а в сам фреймворк были введены новые возможности взаимодействия с бд - подходы Model First и Code First.

Дополнительные улучшения функционала последовали с выходом версии 5.0 в 2012 году. И наконец, в 2013 году был выпущен Entity Framework 6.0, обладающий возможностью асинхронного доступа к данным.

Центральной концепцией Entity Framework является понятие сущности или entity. Сущность представляет набор данных, ассоциированных с определенным объектом. Поэтому данная технология предполагает работу не с таблицами, а с объектами и их наборами.

Любая сущность, как и любой объект из реального мира, обладает рядом свойств. Например, если сущность описывает человека, то мы можем выделить такие свойства, как имя, фамилия, рост, возраст, вес. Свойства необязательно представляют простые данные типа int, но и могут представлять более комплексные структуры данных. И у каждой сущности может быть одно или несколько свойств, которые будут отличать эту сущность от других и будут уникально определять эту сущность. Подобные свойства называют ключами .

При этом сущности могут быть связаны ассоциативной связью один-ко-многим, один-ко-одному и многие-ко-многим, подобно тому, как в реальной базе данных происходит связь через внешние ключи.

Отличительной чертой Entity Framework является использование запросов LINQ для выборки данных из БД. С помощью LINQ мы можем не только извлекать определенные строки, хранящие объекты, из бд, но и получать объекты, связанные различными ассоциативными связями.

Другим ключевым понятием является Entity Data Model . Эта модель сопоставляет классы сущностей с реальными таблицами в БД.

Entity Data Model состоит из трех уровней: концептуального, уровень хранилища и уровень сопоставления (маппинга).

На концептуальном уровне происходит определение классов сущностей, используемых в приложении.

Уровень хранилища определяет таблицы, столбцы, отношения между таблицами и типы данных, с которыми сопоставляется используемая база данных.

Уровень сопоставления (маппинга) служит посредником между предыдущими двумя, определяя сопоставление между свойствами класса сущности и столбцами таблиц.

Таким образом, мы можем через классы, определенные в приложении, взаимодействовать с таблицами из базы данных.

Способы взаимодействия с БД

Entity Framework предполагает три возможных способа взаимодействия с базой данных:

    Database first : Entity Framework создает набор классов, которые отражают модель конкретной базы данных

    Model first : сначала разработчик создает модель базы данных, по которой затем Entity Framework создает реальную базу данных на сервере.

    Code first : разработчик создает класс модели данных, которые будут храниться в бд, а затем Entity Framework по этой модели генерирует базу данных и ее таблицы

Теперь самое время посмотреть, каким образом можно взаимодействовать с Entity Framework. И вполне логично будет начать с вариантов создания Модели данных Entity.

Создание Модели данных Entity (EDM)

Существует несколько подходов создания EDM. Рассмотрим их подробнее.

База данных вначале (Database first)

Данный подход подразумевает, что в первую очередь проектируется и разрабатывается база данных. Это может быть сделано при помощи любых доступных разработчику инструментов. После этого на её основе Entity Framework создаст описание EDM и классы Концептуальной модели.

При таком варианте проектирования архитектуры приложения главная роль отводится структуре базы данных. Классы бизнес логики вынуждены адаптироваться под неё. Однако это позволяет максимально раскрыть потенциал используемой системы управления базами данных.

Чтобы использовать рассматриваемый подход в своем проекте, необходимо выбрать пункт "Add Item" в контекстном меню проекта и добавить описание Модели данных Entity (ADO.NET Entity Data Model).

В появившемся диалоге "Entity Data Model Wizard" нужно выбрать вариант "Generate from a database". После этого потребуется указать базу данных и параметры соединения с ней (выбрать или создать строку соединения). В результате в проект будет добавлен EDMX-файл, который содержит описание EDM в формате XML.

Для редактирования созданного описания используется специальный дизайнер. В качестве примера возьмем базу данных, которая может быть разработана для создаваемого демонстрационного веб-приложения. Вид созданной на её основе EDM представлен на рисунке.

Каждый представленный здесь класс в терминологии Entity Framework называется сущностью. Все их свойства разделены на две группы:

  • Связанные напрямую с полями базы данных: Id , Title , и т. д.
  • Навигационные: Language , Publisher , Tags .
  • Последние не имеют прямых аналогов среди полей базы данных и созданы исходя из анализа связей таблиц. Они позволяют удобно и просто запрашивать связанную c данной сущностью информацию. Например, список книг на определенном языке можно получить используя коллекцию BookDetails у соответствующего экземпляра Language .

    Кроме того, стоит обратить внимание, что между сущностями указаны не только связи, но и их тип. При этом символ "звездочка" обозначает неограниченное число элементов (many). Так в приведенном примере есть следующие варианты:

    • *–1 – тип One-to-many (��дна ко многим). Т.е. любой записи в таблице BookDetails соответствует одна запись в Language. И наоборот, одной записи в таблице Language может соответствовать любое число записей в BookDetails.
    • *–0..1 – также тип One-to-many (одна ко многим), но этом связь не обязательно должна существовать для каждой записи в таблице BookDetails.
    • *–* – этот вариант указывает на связь типа Many-to-many (многие ко многим). В частности любой книге (BookDetails) соответствует любое число ключевых слов (Tag), как и наоборот.
    Модель вначале (Model first)

    Следующий подход к разработке Модели данных Entity называется Модель вначале. Он является противоположностью предыдущего варианта. При этом изначально в дизайнере создается описание EDM, руководствуясь требованиями бизнес-логики. После чего на его основе генерируется база данных.

    Необходимо аккуратно использовать данный подход, т.к. неверные архитектурные решения могут нанести урон производительности базы данных, а значит и приложения в целом.

    Добавление в проект описания EDM практически аналогично. Отличие только в диалоге "Entity Data Model Wizard", где необходимо выбрать пункт "Empty Model". После завершения работ по созданию Модели остается только сгенерировать базу данных. Для этого нужно выбрать пункт "Generate Database from Model" в контекстном меню дизайнера.

    Код вначале (Code First)

    В рассмотренных выше вариантах классы Модели получаются путем их автоматической генерации. А что если классы Модели уже есть? Или, например, удобнее написать их С# код, чем рисовать в дизайнере?

    Начиная с версии 4.1 в Entity Framework еще один подход к разработке описания EDM – Код вначале. С его помощью можно создать базу данных на основе классов C# или Visual Basic. Причем для этого достаточно даже их самого простого варианта – POCO (Plain Old CLR Object).

    Для использования этого подхода достаточно указать Entity Framework используемые типы. Большая часть работы делается на основе соглашений. Однако при необходимости можно использовать атрибуты для задания необходимых параметров.

    Такой подход позволяет очень сильно сократить время разработки на начальном этапе. Например, при проверке некой идеи, разработчик может полностью сосредоточиться на Модели и бизнес-логике, оставив на какое-от время вопрос о базе данных в стороне.

    Этот вариант будет рассмотрен чуть позже на конкретном примере. А сейчас давайте посмотрим, какие возможны варианты сопоставления классов и таблиц.

    Соответствие наследования таблицам в базе данных

    Независимо от используемого способа создания EDM, можно столкнуться с задачей, которая в языках высокого уровня решается с использованием наследования. Но каким образом можно отразить это в базе данных? Существует три подхода для решения этой проблемы.

    Используем пример, чтобы сделать дальнейшее описание более понятным. Предположим, что в Модели автомобильного каталога существует базовый класс Car и два его наследника CivilCar и SportCar .

    Таблица для каждого конкретного типа (Table per concrete type или TPC)

    Это самый простой тип сопоставления, в результате которого будут созданы отдельные самостоятельные таблицы для каждого конкретного типа.

    Для указанных выше классов их будет три: Cars, CivilCars и SportCars. Стоит учесть, что в случае необходимости получить полный список автомобилей, в запросе будут использоваться все эти таблицы.

    Таблица для каждого типа (Table per type или TPT)

    В данном случае будет создано несколько таблиц:

    • одна таблица на основе базового класса, которая будет содержать общие для всех классов поля.
    • несколько вспомогательных, в которых разместиться специфичные для каждого потомка данные.

    Таким образом, в предложенном примере будет основная таблица Cars и две вспомогательные – CivilCars и SportCars. Подобное решение может быть выгодно, если большая часть запросов требует только информацию, соответствующую свойствам класса Car . Например, наиболее часто выводится полный список автомобилей, а уже по выбранным моделям отображаются подробные данные.

    Таблица для иерархий (Table per hierarchy или TPH)

    Последний подход позволяет создать одну общую таблицу для всех классов. Тип, к которому относится конкретная запись, в этом случае указывается в специальным поле. Здесь есть принципиальный момент: необходимо чтобы все свойства, добавленные в классах-наследниках, могли принимать значение null . Дело в том, именно оно будет указываться в полях, у которых нет аналогов в сохраняемом экземпляре.

    В рассматриваемом примере будет создана таблица Cars, содержащая перечень всех свойств всех наследников класса Car .

    Указание строки соединения

    После создания EDM может потребоваться указание строки соединения для Entity Framework. Обратите внимание, что при использовании дизайнера она будет автоматически внесена в конфигурацию приложения. А вот в случае применения подхода Код вначале, её необходимо добавить самостоятельно.

    Использование файла конфигурации (app.config или web.config)

    Строка соединения Entity Framework выглядит несколько отличающейся от привычного варианта, используемого в ADO.NET. Однако понятна с первого взгляда:

    ......... .........

    В данном примере в файл web.config добавляется новая строка соединения с следующими параметрами:

    • name – имя строки соединения. В данном примере это BookCatalogEntities;
    • connectionString – параметры соединения для Entity Framework:
      • metadata – где расположены метаданные EDM. Три указанных файла отвечают за Контестную модель (csdl), Модель хранилища (ssdl) и Модель соотношения (msl).
      • provider – имя провайдера данных для Entity Framework. В данном случае это System.Data.SqlClient для работы с SQL Server или SQL Server Express.
      • provider connection string – параметры соединения. Содержимое этого параметра зависит от выбранного провайдера данных.
    • providerName – имя провайдера данных для приложения. Оно всегда равно System.Data.EntityClient .

    При использовании подхода Код вначале отпадает необходимости в указании EDM. Поэтому строка приобретает более привычный вид:

    Указание строки соединения в коде приложения

    Разумеется, параметры соединения можно указать в коде самого приложения. Это может быть необходимо, если некоторые из параметров не известны на момент компиляции приложения и вводятся пользователем, например, в настройках.

    Есть два способа программного указания строки соединения:

    • создать её как обычную строку, используя string и StringBuilder .
    • воспользоваться специальным классом EntityConnectionStringBuilder , который предоставляет следующие свойства:
      • Metadata – соответствует секции metadata в варианте рассмотренном выше;
      • Provider – указывает используемого провайдера данных;
      • ProviderConnectionString – настраивает параметры соединения провайдера.
      • ConnectionString – строка соединения. Свойство доступно только для чтения.

    Все что остается – создать новый контекст данных ObjectContext с новой строкой соединения. В этом с случае также придется создать самостоятельно все необходимые объекты типа ObjectSet . Цель указанных типов будет пояснена чуть ниже, а пока посмотрим на пример такого кода:

    Var newConnection = new EntityConnectionStringBuilder() { Metadata = @"res://*/DBFirstCatalogModel.csdl|res://*/DBFirstCatalogModel.ssdl|res://*/DBFirstCatalogModel.msl", Provider = @"System.Data.SqlClient", ProviderConnectionString = @"data source=(local);initial catalog=BookCatalog;integrated security=True;multipleactiveresultsets=True;App=EntityFramework" }; using (var context = new ObjectContext(newConnection.ConnectionString)) { var catalog = context.CreateObjectSet(); var languages = context.CreateObjectSet(); var publishers = context.CreateObjectSet(); var tags = context.CreateObjectSet(); // Запросить данные... }

    Выбираем язык запросов

    Как уже было отмечено, для создания запроса клиент может использовать один из двух языков:

    • Entity SQL – использует текстовый для представления запросов. Внешне схож с стандартным SQL, но отличается от него функциями. Необходимо отметить, что только с использованием Entity SQL можно напрямую обращаться к Клиентскому провайдеру данных Entity, минуя слой Службы объектов. В некоторых сценариях это может дать большой прирост в производительности.
    • LINQ to Entities – является диалектом языка запросов LINQ для Entity Framework и использует его синтаксис. Стоит отметить, что поддерживаются не все доступные методы. При этом их использование не является ошибкой на этапе компиляции. Однако в процессе выполнения приложения будет выброшено исключение NotSupportedException .

    Какой язык предпоч��ительнее? Однозначного ответа быть не может. У каждого из них есть свои плюсы. Поэтому данные вопрос, с учетом текущей задачи, каждый должен решить для себя сам.

    Принципы получения и модификации данных

    Как уже было отмечено в прошлой части, сделать запрос можно двумя способами:

  • Обратиться с помощью Entity SQL или LINQ to Entities к слою Служб объектов.
  • Использовать Entity SQL для запроса напрямую к слою Клиентского провайдера данных Entity.
  • Рассмотрим принцип использования только первого варианта.

    Основным объектом, который обеспечивает взаимодействие клиентского кода со слоем Служб объектов является контекст данных. Он представлен классом, унаследованным от ObjectContext или DbContext. Последний используется в подходе Код вначале.

    Стоит отметить, что ObjectContext и DbContext не являются абстрактными. Механизм наследования используется только для того, чтобы добавить в новый класс свойства, отвечающие за доступ к данным. Их тип ObjectSet или DbSet соответственно, а имена совпадают с именами сущностей Модели. Эти свойства позволяют работать с объектами базы данных используя к LINQ to Entities.

    Полученные объекты отслеживаются слоем Служб объектов. Поэтому при вызове метода сохранения изменений, база данных будут соответствующе модифицирована.

    Давайте перейдем от теории к практике и посмотрим как реализована работа с Entity Framework в разрабатываемом демонстрационном веб-приложении.

    Всем доброго времени суток. На связи Алексей Гулынин. В прошлых статьях мы рассмотрели различные подходы по работе с Entity Framework. В данной статье я бы хотел рассказать, как работать с данными в Entity Framework . Рассмотрим следующие операции:

  • Добавление записи
  • Чтение записей
  • Редактирование записи
  • Удаление записи
  • Создадим новый проект. В этот раз тип проекта будет "Приложение Windows Forms":

    Добавим элемент DataGridView на нашу форму. Также добавим 3 кнопки: "Добавление", "Редактирование", "Удаление". Добавим ещё 2 элемента "TextBox", в которые будем выводить информацию о записи, которая в данный момент выделена (эти 2 "TextBox" будут использованы ещё для добавления новой записи). Добавим ещё один "TextBox", в который будет выводиться информация об "ID" записи (это нужно будет для редактирования записи). Также добавим 2 элемента "label". В конечном итоге наша форма будет выглядеть следующим образом:

    Будем использовать подход "Code First". Создадим следующий класс:

    Public class Countries { public int Id { get; set; } public string Country { get; set; } public string Capital { get; set; } }

    Основной код будет в файле "Form1.cs". Сразу приведу весь код, в комментариях он будет подробно рассмотрен:

    Using System; using System.Windows.Forms; using System.Data.Entity; using System.Data.Entity.Migrations; namespace WorkWithDataInEF { public partial class Form1: Form { private MyModel db; public Form1() { // Создаём объект нашего контекста db = new MyModel(); InitializeComponent(); // Загружаем данные из таблицы в кэш db.Countries.Load(); // Привязываем данные к dataGridView dataGridView1.DataSource = db.Countries.Local.ToBindingList(); } // Событие: клик по ячейке таблицы private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e) { // Проверка выборки строк // Если строка не выбрана, то дальше ничего не происходит if (dataGridView1.CurrentRow == null) return; // Получаем выделенную строку и приводим её у типу Countries Countries country = dataGridView1.CurrentRow.DataBoundItem as Countries; // Если мы щёлкаем по пустой строке, то ничего дальше не делаем if (country == null) return; // Выводим данные о стране и её столице в TextBox tb_Country.Text = country.Country; tb_Capital.Text = country.Capital; tB_ID.Text = country.Id.ToString(); } // Добавление записи private void btn_Add_Click(object sender, EventArgs e) { // Проверяем, что в текстовых полях есть данные if (tb_Country.Text == String.Empty || tb_Capital.Text == String.Empty) { MessageBox.Show("Заполните данные о стране!"); return; } // Создаём экземпляр класса Countries, // т.е получаем данные о нашей стране из текстовых полей Countries country = new Countries { Country = tb_Country.Text, Capital = tb_Capital.Text }; // Заносим данные в нашу таблицу db.Countries.Add(country); // Обязательно сохраняем изменения db.SaveChanges(); // Обновляем наш dataGridView, чтобы в нём отобразилась новая страна dataGridView1.Refresh(); // Обнуляем текстовые поля tb_Country.Text = String.Empty; tb_Capital.Text = String.Empty; tB_ID.Text = String.Empty; } // Редактирование записи private void btn_Edit_Click(object sender, EventArgs e) { // Проверяем, что выбрана запись if (tB_ID.Text == String.Empty) return; // Получаем id из текстового поля int id = Convert.ToInt32(tB_ID.Text); // Находим страну по этому id с помощью метода Find() Countries country = db.Countries.Find(id); if (country == null) return; country.Country = tb_Country.Text; country.Capital = tb_Capital.Text; // Добавляем или обновляем запись db.Countries.AddOrUpdate(country); db.SaveChanges(); dataGridView1.Refresh(); } // Удаление записи // Всё аналогично редактирования записи, только используется метод Remove() private void btn_delete_Click(object sender, EventArgs e) { if (tB_ID.Text == String.Empty) return; int id = Convert.ToInt32(tB_ID.Text); Countries country = db.Countries.Find(id); if (country == null) return; country.Country = tb_Country.Text; country.Capital = tb_Capital.Text; db.Countries.Remove(country); db.SaveChanges(); dataGridView1.Refresh(); } } }

    Как мы видим — всё работает.