Использование Entity Framework 4.0 и элемента управления ObjectDataSource, часть 1: начало работы
Эта серия руководств основана на веб-приложении Университета Contoso, созданном начало работы с серией учебников По Entity Framework 4.0. Если вы не выполнили предыдущие руководства, в качестве отправной точки для этого руководства можно скачать созданное приложение . Вы также можете скачать приложение , созданное в полной серии учебников.
Пример веб-приложения Contoso University демонстрирует создание ASP.NET Web Forms приложений с помощью Entity Framework 4.0 и Visual Studio 2010. Пример приложения — это веб-сайт для вымышленного университета Contoso. На нем предусмотрены различные функции, в том числе прием учащихся, создание курсов и назначение преподавателей.
В этом руководстве показаны примеры на C#. Скачиваемый пример содержит код на C# и Visual Basic.
База данных в первую очередь
Существует три способа работы с данными в Entity Framework: Database First, Model First и Code First. Это руководство предназначено для Базы данных в первую очередь. Сведения о различиях между этими рабочими процессами и рекомендации по выбору лучшего из них для вашего сценария см. в разделе Рабочие процессы разработки Entity Framework.
веб-формы
Как и в начало работы, в этом руководстве используется модель ASP.NET Web Forms и предполагается, что вы знаете, как работать с ASP.NET Web Forms в Visual Studio. В противном случае см. раздел начало работы с веб-формы ASP.NET 4.5. Если вы предпочитаете работать с платформой ASP.NET MVC, см. статью начало работы с Entity Framework с помощью ASP.NET MVC.
Версии программного обеспечения
Показано в руководстве Также работает с Windows 7 Windows 8 Visual Studio 2010 Visual Studio 2010 Express для Web. Руководство не тестировалось в более поздних версиях Visual Studio. Выбор меню, диалоговых окон и шаблонов имеет много различий. .NET 4 .NET 4.5 имеет обратную совместимость с .NET 4, но это руководство не было протестировано с .NET 4.5. Entity Framework 4 Руководство не тестировалось в более поздних версиях Entity Framework. Начиная с Entity Framework 5, EF использует по умолчанию DbContext API
, появившиеся в EF 4.1. Элемент управления EntityDataSource был разработан для использованияObjectContext
API. Сведения об использовании элемента управления EntityDataSource с API см. вDbContext
этой записи блога.Вопросы
Если у вас есть вопросы, которые не связаны напрямую с руководством, вы можете опубликовать их на форуме ASP.NET Entity Framework, Entity Framework и LINQ to Entities форуме или StackOverflow.com.
Элемент EntityDataSource
управления позволяет создавать приложение очень быстро, но обычно требует сохранения значительного объема бизнес-логики и логики доступа к данным на ASPX-страницах . Если вы ожидаете, что ваше приложение усложняется и требует постоянного обслуживания, вы можете заранее потратить больше времени на разработку, чтобы создать n-уровневую или многоуровневую структуру приложения, более удобную для поддержки. Для реализации этой архитектуры необходимо отделить уровень представления от уровня бизнес-логики (BLL) и уровня доступа к данным (DAL). Одним из способов реализации этой структуры является использование ObjectDataSource
элемента управления вместо EntityDataSource
элемента управления . При использовании ObjectDataSource
элемента управления вы реализуете собственный код доступа к данным, а затем вызываете его на ASPX-страницах с помощью элемента управления, который имеет многие из тех же функций, что и другие элементы управления источником данных. Это позволяет сочетать преимущества n-уровневого подхода с преимуществами использования элемента управления веб-формы для доступа к данным.
Элемент ObjectDataSource
управления обеспечивает большую гибкость и другими способами. Так как вы пишете собственный код доступа к данным, проще выполнять больше, чем просто читать, вставлять, обновлять или удалять определенные типы сущностей, которые предназначены для выполнения элементом EntityDataSource
управления. Например, можно вести журнал каждый раз при обновлении сущности, архивировать данные при удалении сущности или автоматически проверка и обновлять связанные данные при необходимости при вставке строки со значением внешнего ключа.
Классы бизнес-логики и репозитория
Элемент ObjectDataSource
управления работает путем вызова класса, который вы создаете. Класс включает методы, которые извлекают и обновляют данные, а имена этих методов предоставляются элементу ObjectDataSource
управления в разметке. Во время отрисовки или обратной ObjectDataSource
обработки вызывает указанные методы.
Помимо базовых операций CRUD, классу, который вы создаете для использования с элементом ObjectDataSource
управления, может потребоваться выполнить бизнес-логику при чтении или обновлении ObjectDataSource
данных. Например, при обновлении отдела может потребоваться убедиться, что ни один другой отдел не имеет одного администратора, так как один человек не может быть администратором нескольких отделов.
В некоторых ObjectDataSource
документах, таких как общие сведения о классе ObjectDataSource, элемент управления вызывает класс, называемый бизнес-объектом , который включает как бизнес-логику, так и логику доступа к данным. В этом руководстве вы создадите отдельные классы для бизнес-логики и логики доступа к данным. Класс, инкапсулирующий логику доступа к данным, называется репозиторием. Класс бизнес-логики включает как методы бизнес-логики, так и методы доступа к данным, но методы доступа к данным вызывают репозиторий для выполнения задач доступа к данным.
Вы также создадите уровень абстракции между BLL и DAL, который упрощает автоматическое модульное тестирование BLL. Этот уровень абстракции реализуется путем создания интерфейса и использования интерфейса при создании экземпляра репозитория в классе бизнес-логики. Это позволяет предоставить классу бизнес-логики ссылку на любой объект, реализующий интерфейс репозитория. Для нормальной работы предоставляется объект репозитория, который работает с Entity Framework. Для тестирования предоставляется объект репозитория, который работает с данными, хранящимися таким образом, которыми можно легко управлять, например переменными класса, определенными как коллекции.
На следующем рисунке показана разница между классом бизнес-логики, который включает логику доступа к данным без репозитория, и классом, использующим репозиторий.
Для начала вы создадите веб-страницы, на которых ObjectDataSource
элемент управления привязан непосредственно к репозиторию, так как он выполняет только основные задачи доступа к данным. В следующем руководстве вы создадите класс бизнес-логики с логикой проверки и привяжите ObjectDataSource
элемент управления к данному классу, а не к классу репозитория. Вы также создадите модульные тесты для логики проверки. В третьем учебнике этой серии вы добавите в приложение функции сортировки и фильтрации.
Страницы, созданные в этом руководстве, работают с набором Departments
сущностей модели данных, созданной в серии учебников начало работы.
Обновление базы данных и модели данных
Вы начнете работу с этого руководства с внесения двух изменений в базу данных, оба из которых требуют соответствующих изменений в модели данных, созданной в начало работы с помощью Entity Framework и веб-формы руководствах. В одном из этих руководств вы вручную внесли изменения в конструктор, чтобы синхронизировать модель данных с базой данных после изменения базы данных. В этом руководстве вы будете использовать средство конструктора Обновление модели из базы данных для автоматического обновления модели данных.
Добавление связи в базу данных
В Visual Studio откройте веб-приложение Contoso University, созданное в начало работы с помощью Entity Framework и веб-формы серии учебников, а затем откройте схему SchoolDiagram
базы данных.
Если вы посмотрите на таблицу Department
на схеме базы данных, вы увидите, что в ней есть Administrator
столбец. Этот столбец является внешним ключом для Person
таблицы, но связь с внешним ключом в базе данных не определена. Необходимо создать связь и обновить модель данных, чтобы платформа Entity Framework автоматически обрабатывала эту связь.
На схеме базы данных щелкните таблицу правой кнопкой Department
мыши и выберите Пункт Связи.
В поле Связи внешних ключей нажмите кнопку Добавить, а затем нажмите кнопку с многоточием для спецификации таблиц и столбцов.
В диалоговом окне Таблицы и столбцы задайте для таблицы и поля Person
первичного ключа значение и PersonID
, а для таблицы и поля Department
внешнего ключа задайте значение и Administrator
. (При этом имя связи изменится с FK_Department_Department
на FK_Department_Person
.)
Нажмите кнопку ОК в поле Таблицы и столбцы , нажмите кнопку Закрыть в поле Связи внешних ключей и сохраните изменения. Если вам будет предложено сохранить Person
таблицы и Department
, нажмите кнопку Да.
Примечание
Если вы удалили Person
строки, соответствующие данным, которые уже находятся в столбце Administrator
, вы не сможете сохранить это изменение. В этом случае используйте редактор таблиц в server Обозреватель, чтобы убедиться, что Administrator
значение в каждой Department
строке содержит идентификатор записи, которая действительно существует в Person
таблице.
После сохранения изменения вы не сможете удалить строку из Person
таблицы, если этот человек является администратором отдела. В рабочем приложении необходимо предоставить определенное сообщение об ошибке, если ограничение базы данных предотвращает удаление, или указать каскадное удаление. Пример указания каскадного удаления см. в разделе Entity Framework и ASP.NET — начало работы часть 2.
Добавление представления в базу данных
На новой странице Departments.aspx , которую вы будете создавать, необходимо предоставить раскрывающийся список преподавателей с именами в формате "последний, первый", чтобы пользователи могли выбирать администраторов отделов. Чтобы упростить это, создайте представление в базе данных. Представление будет состоять только из данных, необходимых для раскрывающегося списка: полное имя (правильно отформатированный) и ключ записи.
В Обозреватель сервера разверните school.mdf, щелкните правой кнопкой мыши папку Представления и выберите Добавить новое представление.
Нажмите кнопку Закрыть при появлении диалогового окна Добавление таблицы и вставьте следующую инструкцию SQL в область SQL:
SELECT LastName + ',' + FirstName AS FullName, PersonID
FROM dbo.Person
WHERE (HireDate IS NOT NULL)
Сохраните представление как vInstructorName
.
Обновление модели данных
В папке DAL откройте файл SchoolModel.edmx , щелкните правой кнопкой мыши область конструктора и выберите Обновить модель из базы данных.
В диалоговом окне Выбор объектов базы данных перейдите на вкладку Добавить и выберите только что созданное представление.
Нажмите кнопку Готово.
В конструкторе вы увидите, что средство создало vInstructorName
сущность и новую связь между сущностями Department
и Person
.
Примечание
В окнах Вывод и Список ошибок может появиться предупреждающее сообщение о том, что средство автоматически создало первичный ключ для нового vInstructorName
представления. Это ожидаемое поведение.
При ссылке на новую vInstructorName
сущность в коде вы не хотите использовать соглашение базы данных о префиксе в нижнем регистре "v". Поэтому необходимо переименовать сущность и набор сущностей в модели.
Откройте обозреватель моделей. vInstructorName
Отображается как тип сущности и представление.
В разделе SchoolModel (не SchoolModel.Store) щелкните правой кнопкой мыши vInstructorName и выберите Свойства. В окне Свойства измените свойство Name на "InstructorName", а для свойства Entity Set Name ( Имя набора сущностей ) на "InstructorNames".
Сохраните и закройте модель данных, а затем перестройте проект.
Использование класса репозитория и элемента управления ObjectDataSource
Создайте файл класса в папке DAL , назовите его SchoolRepository.cs и замените существующий код следующим кодом:
using System;
using System.Collections.Generic;
using System.Linq;
using ContosoUniversity.DAL;
namespace ContosoUniversity.DAL
{
public class SchoolRepository : IDisposable
{
private SchoolEntities context = new SchoolEntities();
public IEnumerable<Department> GetDepartments()
{
return context.Departments.Include("Person").ToList();
}
private bool disposedValue = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposedValue)
{
if (disposing)
{
context.Dispose();
}
}
this.disposedValue = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
Этот код предоставляет единый GetDepartments
метод, который возвращает все сущности в наборе сущностей Departments
. Так как известно, что вы будете получать доступ к свойству Person
навигации для каждой возвращаемой строки, вы указываете неотложную загрузку для этого свойства с помощью Include
метода . Класс также реализует IDisposable
интерфейс для обеспечения освобождения подключения к базе данных при удалении объекта .
Примечание
Распространенной практикой является создание класса репозитория для каждого типа сущности. В этом руководстве используется один класс репозитория для нескольких типов сущностей. Дополнительные сведения о шаблоне репозитория см. в записях в блоге команды Entity Framework и в блоге Джули Лерман.
Метод GetDepartments
возвращает объект, IEnumerable
а не IQueryable
объект , чтобы гарантировать возможность использования возвращаемой коллекции даже после удаления самого объекта репозитория. Объект IQueryable
может вызывать доступ к базе данных при каждом обращении к нему, но объект репозитория может быть удален при попытке элемента управления отрисовки данных. Вы можете вернуть другой тип коллекции, например IList
объект , а не IEnumerable
объект . Однако возврат IEnumerable
объекта гарантирует, что вы сможете выполнять типичные задачи обработки списков только для чтения, такие как foreach
циклы и запросы LINQ, но вы не сможете добавлять или удалять элементы в коллекции, что может означать, что такие изменения будут сохранены в базе данных.
Создайте страницу Departments.aspx, которая использует страницу master Site.Master, и добавьте следующую разметку Content
в элемент управления с именем Content2
:
<h2>Departments</h2>
<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server"
TypeName="ContosoUniversity.DAL.SchoolRepository"
DataObjectTypeName="ContosoUniversity.DAL.Department"
SelectMethod="GetDepartments" >
</asp:ObjectDataSource>
<asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
DataSourceID="DepartmentsObjectDataSource" >
<Columns>
<asp:CommandField ShowEditButton="True" ShowDeleteButton="True"
ItemStyle-VerticalAlign="Top">
</asp:CommandField>
<asp:DynamicField DataField="Name" HeaderText="Name" SortExpression="Name" ItemStyle-VerticalAlign="Top" />
<asp:DynamicField DataField="Budget" HeaderText="Budget" SortExpression="Budget" ItemStyle-VerticalAlign="Top" />
<asp:DynamicField DataField="StartDate" HeaderText="Start Date" ItemStyle-VerticalAlign="Top" />
<asp:TemplateField HeaderText="Administrator" SortExpression="Person.LastName" ItemStyle-VerticalAlign="Top" >
<ItemTemplate>
<asp:Label ID="AdministratorLastNameLabel" runat="server" Text='<%# Eval("Person.LastName") %>'></asp:Label>,
<asp:Label ID="AdministratorFirstNameLabel" runat="server" Text='<%# Eval("Person.FirstMidName") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Эта разметка создает элемент ObjectDataSource
управления, использующий только что созданный класс репозитория, и GridView
элемент управления для отображения данных. Элемент GridView
управления указывает команды "Изменить" и "Удалить ", но вы еще не добавили код для их поддержки.
В нескольких столбцах используются DynamicField
элементы управления, позволяющие воспользоваться функциями автоматического форматирования и проверки данных. Чтобы они работали, необходимо вызвать EnableDynamicData
метод в обработчике Page_Init
событий. (DynamicControl
Элементы управления не используются в поле, Administrator
так как они не работают со свойствами навигации.)
Атрибуты Vertical-Align="Top"
станут важными позже при добавлении столбца с вложенным GridView
элементом управления в сетку.
Откройте файл Departments.aspx.cs и добавьте следующую using
инструкцию:
using ContosoUniversity.DAL;
Затем добавьте следующий обработчик для события страницы Init
:
protected void Page_Init(object sender, EventArgs e)
{
DepartmentsGridView.EnableDynamicData(typeof(Department));
}
В папке DAL создайте файл класса с именем Department.cs и замените существующий код следующим кодом:
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.DAL
{
[MetadataType(typeof(DepartmentMetaData))]
public partial class Department
{
}
public class DepartmentMetaData
{
[DataType(DataType.Currency)]
[Range(0, 1000000, ErrorMessage = "Budget must be less than $1,000,000.00")]
public Decimal Budget { get; set; }
[DisplayFormat(DataFormatString="{0:d}",ApplyFormatInEditMode=true)]
public DateTime StartDate { get; set; }
}
}
Этот код добавляет метаданные в модель данных. Он указывает, что Budget
свойство сущности Department
фактически представляет валюту, хотя его тип данных — Decimal
, и указывает, что значение должно быть в диапазоне от 0 до 1 000 000,000 долл. США. Он также указывает, что StartDate
свойство должно быть отформатировано как дата в формате мм/дд/гггг.
Запустите страницу Departments.aspx .
Обратите внимание, что, хотя вы не указали строку формата в разметке страницы Departments.aspx для столбцов Бюджет или Дата начала , к ним DynamicField
применено форматирование валюты и даты по умолчанию элементами управления с использованием метаданных, указанных в файле Department.cs .
Добавление функций вставки и удаления
Откройте файл SchoolRepository.cs и добавьте следующий код, чтобы создать Insert
метод и Delete
метод . Код также включает метод с именем GenerateDepartmentID
, который вычисляет следующее доступное значение ключа записи для использования методом Insert
. Это необходимо, так как база данных не настроена на автоматическое вычисление Department
для таблицы.
public void InsertDepartment(Department department)
{
try
{
department.DepartmentID = GenerateDepartmentID();
context.Departments.AddObject(department);
context.SaveChanges();
}
catch (Exception ex)
{
//Include catch blocks for specific exceptions first,
//and handle or log the error as appropriate in each.
//Include a generic catch block like this one last.
throw ex;
}
}
public void DeleteDepartment(Department department)
{
try
{
context.Departments.Attach(department);
context.Departments.DeleteObject(department);
context.SaveChanges();
}
catch (Exception ex)
{
//Include catch blocks for specific exceptions first,
//and handle or log the error as appropriate in each.
//Include a generic catch block like this one last.
throw ex;
}
}
private Int32 GenerateDepartmentID()
{
Int32 maxDepartmentID = 0;
var department = (from d in GetDepartments()
orderby d.DepartmentID descending
select d).FirstOrDefault();
if (department != null)
{
maxDepartmentID = department.DepartmentID + 1;
}
return maxDepartmentID;
}
Метод Attach
Метод DeleteDepartment
вызывает Attach
метод , чтобы восстановить связь, которая поддерживается в диспетчере состояния объектов контекста объекта, между сущностью в памяти и строкой базы данных, которую она представляет. Это должно произойти до того, как метод вызовет SaveChanges
метод .
Термин контекст объекта относится к классу Entity Framework, который является производным от ObjectContext
класса, который используется для доступа к наборам сущностей и сущностям. В коде для этого проекта класс называется SchoolEntities
, а его экземпляр всегда context
называется . Диспетчер состояния объектов контекста объекта — это класс, производный от ObjectStateManager
класса . Объектный контакт использует диспетчер состояний объектов для хранения объектов сущностей и отслеживания синхронизации каждого из них с соответствующей строкой таблицы или строками в базе данных.
При чтении сущности контекст объекта сохраняет ее в диспетчере состояний объектов и отслеживает, синхронизировано ли это представление объекта с базой данных. Например, при изменении значения свойства устанавливается флаг, указывающий, что измененное свойство больше не синхронизировано с базой данных. Затем при вызове SaveChanges
метода контекст объекта знает, что делать в базе данных, так как диспетчер состояний объектов точно знает, что отличается между текущим состоянием сущности и состоянием базы данных.
Однако этот процесс обычно не работает в веб-приложении, так как экземпляр контекста объекта, который считывает сущность, а также все содержимое в диспетчере состояний объектов, удаляется после отрисовки страницы. Экземпляр контекста объекта, который должен применять изменения, является новым экземпляром, который создается для обратной обработки. В случае DeleteDepartment
метода ObjectDataSource
элемент управления повторно создает исходную версию сущности из значений в состоянии представления, но эта повторно созданная Department
сущность не существует в диспетчере состояний объектов. Если вы вызвали DeleteObject
метод для этой повторно созданной сущности, вызов завершится ошибкой, так как контекст объекта не знает, синхронизирована ли сущность с базой данных. Однако вызов Attach
метода повторно устанавливает то же самое отслеживание между повторно созданной сущностью и значениями в базе данных, которое изначально выполнялось автоматически при чтении сущности в более раннем экземпляре контекста объекта.
Бывают случаи, когда контекст объекта не хочет отслеживать сущности в диспетчере состояний объектов, и вы можете установить флаги, чтобы предотвратить это. Примеры этого приведены в последующих руководствах этой серии.
Метод SaveChanges
Этот простой класс репозитория иллюстрирует основные принципы выполнения операций CRUD. В этом примере SaveChanges
метод вызывается сразу после каждого обновления. В рабочем приложении может потребоваться вызвать SaveChanges
метод из отдельного метода, чтобы обеспечить больший контроль над обновлением базы данных. (В конце следующего руководства вы найдете ссылку на технический документ, в котором рассматривается шаблон единицы работы, который является одним из подходов к координации связанных обновлений.) Обратите внимание, DeleteDepartment
что в примере метод не включает код для обработки конфликтов параллелизма; код для этого будет добавлен в следующем руководстве этой серии.
Получение имен преподавателей для выбора при вставке
При создании новых отделов пользователи должны иметь возможность выбрать администратора из списка преподавателей в раскрывающемся списке. Поэтому добавьте следующий код в файл SchoolRepository.cs , чтобы создать метод для получения списка преподавателей с помощью созданного ранее представления:
public IEnumerable<InstructorName> GetInstructorNames()
{
return context.InstructorNames.OrderBy("it.FullName").ToList();
}
Создание страницы для вставки отделов
Создайте страницу DepartmentsAdd.aspx , которая использует страницу Site.Master , и добавьте следующую разметку Content
в элемент управления с именем Content2
:
<h2>Departments</h2>
<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server"
TypeName="ContosoUniversity.DAL.SchoolRepository" DataObjectTypeName="ContosoUniversity.DAL.Department"
InsertMethod="InsertDepartment" >
</asp:ObjectDataSource>
<asp:DetailsView ID="DepartmentsDetailsView" runat="server"
DataSourceID="DepartmentsObjectDataSource" AutoGenerateRows="False"
DefaultMode="Insert" OnItemInserting="DepartmentsDetailsView_ItemInserting">
<Fields>
<asp:DynamicField DataField="Name" HeaderText="Name" />
<asp:DynamicField DataField="Budget" HeaderText="Budget" />
<asp:DynamicField DataField="StartDate" HeaderText="Start Date" />
<asp:TemplateField HeaderText="Administrator">
<InsertItemTemplate>
<asp:ObjectDataSource ID="InstructorsObjectDataSource" runat="server"
TypeName="ContosoUniversity.DAL.SchoolRepository"
DataObjectTypeName="ContosoUniversity.DAL.InstructorName"
SelectMethod="GetInstructorNames" >
</asp:ObjectDataSource>
<asp:DropDownList ID="InstructorsDropDownList" runat="server"
DataSourceID="InstructorsObjectDataSource"
DataTextField="FullName" DataValueField="PersonID" OnInit="DepartmentsDropDownList_Init">
</asp:DropDownList>
</InsertItemTemplate>
</asp:TemplateField>
<asp:CommandField ShowInsertButton="True" />
</Fields>
</asp:DetailsView>
<asp:ValidationSummary ID="DepartmentsValidationSummary" runat="server"
ShowSummary="true" DisplayMode="BulletList" />
Эта разметка создает два ObjectDataSource
элемента управления: один для вставки новых Department
сущностей, а второй для получения имен преподавателей DropDownList
для элемента управления, используемого для выбора администраторов отделов. Разметка создает DetailsView
элемент управления для ввода новых отделов и задает обработчик для события элемента управления ItemInserting
, чтобы можно было задать Administrator
значение внешнего ключа. В конце находится элемент управления для ValidationSummary
отображения сообщений об ошибках.
Откройте Файл DepartmentsAdd.aspx.cs и добавьте следующую using
инструкцию:
using ContosoUniversity.DAL;
Добавьте следующую переменную класса и методы:
private DropDownList administratorsDropDownList;
protected void Page_Init(object sender, EventArgs e)
{
DepartmentsDetailsView.EnableDynamicData(typeof(Department));
}
protected void DepartmentsDropDownList_Init(object sender, EventArgs e)
{
administratorsDropDownList = sender as DropDownList;
}
protected void DepartmentsDetailsView_ItemInserting(object sender, DetailsViewInsertEventArgs e)
{
e.Values["Administrator"] = administratorsDropDownList.SelectedValue;
}
Метод Page_Init
включает функциональные возможности динамических данных. Обработчик события DropDownList
элемента управления Init
сохраняет ссылку на элемент управления, а обработчик события элемента управления Inserting
использует ее для DetailsView
получения PersonID
значения выбранного преподавателя и обновления Administrator
свойства внешнего ключа сущностиDepartment
.
Запустите страницу, добавьте сведения о новом отделе и щелкните ссылку Вставить .
Введите значения для другого нового отдела. Введите число больше 1 000 000,00 в поле Бюджет и перейдите к следующему полю. В поле появится звездочка, и если вы наведете на нее указатель мыши, вы увидите сообщение об ошибке, введенное в метаданных для этого поля.
Нажмите кнопку Вставить, и в нижней части страницы появится сообщение об ошибке, отображаемое элементом ValidationSummary
управления.
Затем закройте браузер и откройте страницу Departments.aspx . Добавьте возможность удаления на страницу Departments.aspx , добавив DeleteMethod
атрибут в ObjectDataSource
элемент управления и DataKeyNames
атрибут в элемент управления GridView
. Открывающие теги для этих элементов управления теперь будут выглядеть следующим образом:
<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server"
TypeName="ContosoUniversity.DAL.SchoolRepository"
DataObjectTypeName="ContosoUniversity.DAL.Department"
SelectMethod="GetDepartments"
DeleteMethod="DeleteDepartment" >
<asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
DataSourceID="DepartmentsObjectDataSource" DataKeyNames="DepartmentID" >
Запустите страницу.
Удалите отдел, добавленный при запуске страницы DepartmentsAdd.aspx .
Добавление функциональных возможностей обновления
Откройте файл SchoolRepository.cs и добавьте следующий Update
метод:
public void UpdateDepartment(Department department, Department origDepartment)
{
try
{
context.Departments.Attach(origDepartment);
context.ApplyCurrentValues("Departments", department);
context.SaveChanges();
}
catch (Exception ex)
{
//Include catch blocks for specific exceptions first,
//and handle or log the error as appropriate in each.
//Include a generic catch block like this one last.
throw ex;
}
}
При нажатии кнопки Обновить на странице ObjectDataSource
Departments.aspx элемент управления создает две Department
сущности для передачи в UpdateDepartment
метод . Один из них содержит исходные значения, сохраненные в состоянии представления, а другой — новые значения, введенные в элементе GridView
управления . Код в методе UpdateDepartment
передает Department
сущность, которая имеет исходные значения, методу Attach
, чтобы установить отслеживание между сущностью и тем, что находится в базе данных. Затем код передает Department
в метод сущность с новыми значениями ApplyCurrentValues
. Контекст объекта сравнивает старые и новые значения. Если новое значение отличается от старого, контекст объекта изменяет значение свойства. Затем SaveChanges
метод обновляет только измененные столбцы в базе данных. (Однако если функция обновления для этой сущности была сопоставлена с хранимой процедурой, вся строка будет обновлена независимо от того, какие столбцы были изменены.)
Откройте файл Departments.aspx и добавьте в элемент управления следующие атрибуты DepartmentsObjectDataSource
:
UpdateMethod="UpdateDepartment"
ConflictDetection="CompareAllValues"
Это приводит к тому, что старые значения сохраняются в состоянии представления, чтобы их можно было сравнить с новыми значениями в методеUpdate
.OldValuesParameterFormatString="orig{0}"
Это сообщает элементу управления, что имя параметра исходных значений —origDepartment
.
Разметка для открывающего тега элемента управления теперь похожа ObjectDataSource
на следующий пример:
<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server"
TypeName="ContosoUniversity.DAL.SchoolRepository"
DataObjectTypeName="ContosoUniversity.DAL.Department"
SelectMethod="GetDepartments" DeleteMethod="DeleteDepartment"
UpdateMethod="UpdateDepartment"
ConflictDetection="CompareAllValues"
OldValuesParameterFormatString="orig{0}" >
OnRowUpdating="DepartmentsGridView_RowUpdating"
Добавьте атрибут в GridView
элемент управления . Этот параметр используется для задания Administrator
значения свойства на основе строки, выбранной пользователем в раскрывающемся списке. Открывающий GridView
тег теперь похож на следующий пример:
<asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
DataSourceID="DepartmentsObjectDataSource" DataKeyNames="DepartmentID"
OnRowUpdating="DepartmentsGridView_RowUpdating">
Добавьте элемент EditItemTemplate
управления для столбца Administrator
в GridView
элемент управления сразу после ItemTemplate
элемента управления для этого столбца:
<EditItemTemplate>
<asp:ObjectDataSource ID="InstructorsObjectDataSource" runat="server" DataObjectTypeName="ContosoUniversity.DAL.InstructorName"
SelectMethod="GetInstructorNames" TypeName="ContosoUniversity.DAL.SchoolRepository">
</asp:ObjectDataSource>
<asp:DropDownList ID="InstructorsDropDownList" runat="server" DataSourceID="InstructorsObjectDataSource"
SelectedValue='<%# Eval("Administrator") %>'
DataTextField="FullName" DataValueField="PersonID" OnInit="DepartmentsDropDownList_Init" >
</asp:DropDownList>
</EditItemTemplate>
Этот EditItemTemplate
элемент управления аналогичен элементу InsertItemTemplate
управления на странице DepartmentsAdd.aspx . Разница заключается в том, что начальное значение элемента управления задается с помощью атрибута SelectedValue
.
Перед элементом GridView
управления добавьте элемент ValidationSummary
управления, как это было на странице DepartmentsAdd.aspx .
<asp:ValidationSummary ID="DepartmentsValidationSummary" runat="server"
ShowSummary="true" DisplayMode="BulletList" />
Откройте файл Departments.aspx.cs и сразу после объявления разделяемого класса добавьте следующий код, чтобы создать частное поле для ссылки на DropDownList
элемент управления:
private DropDownList administratorsDropDownList;
Затем добавьте обработчики DropDownList
для события элемента управления Init
и GridView
события элемента управления RowUpdating
:
protected void DepartmentsDropDownList_Init(object sender, EventArgs e)
{
administratorsDropDownList = sender as DropDownList;
}
protected void DepartmentsGridView_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
e.NewValues["Administrator"] = administratorsDropDownList.SelectedValue;
}
Обработчик события Init
сохраняет ссылку на DropDownList
элемент управления в поле класса . Обработчик события использует ссылку, RowUpdating
чтобы получить введенное пользователем значение и применить его к свойству Administrator
сущности Department
.
Используйте страницу DepartmentsAdd.aspx , чтобы добавить новый отдел, а затем запустите страницу Departments.aspx и щелкните Изменить в добавленной строке.
Примечание
Вы не сможете редактировать строки, которые вы не добавили (т. е. уже находившиеся в базе данных), из-за недопустимых данных в базе данных; Администраторами строк, созданных с помощью базы данных, являются учащиеся. При попытке изменить один из них вы получите страницу с сообщением об ошибке, например 'InstructorsDropDownList' has a SelectedValue which is invalid because it does not exist in the list of items.
Если ввести недопустимую сумму бюджета и нажать кнопку Обновить, вы увидите ту же звездочку и сообщение об ошибке, что и на странице Departments.aspx .
Измените значение поля или выберите другого администратора и нажмите кнопку Обновить. Отобразится изменение.
На этом завершается введение в использование ObjectDataSource
элемента управления для базовых операций CRUD (создание, чтение, обновление, удаление) в Entity Framework. Вы создали простое n-уровневое приложение, но уровень бизнес-логики по-прежнему тесно связан со уровнем доступа к данным, что усложняет автоматизированное модульное тестирование. В следующем руководстве описано, как реализовать шаблон репозитория для упрощения модульного тестирования.