Поделиться через


Использование 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. Для тестирования предоставляется объект репозитория, который работает с данными, хранящимися таким образом, которыми можно легко управлять, например переменными класса, определенными как коллекции.

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

Image05

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

Страницы, созданные в этом руководстве, работают с набором Departments сущностей модели данных, созданной в серии учебников начало работы.

Снимок экрана, на котором показано, как должна выглядеть страница

Image02

Обновление базы данных и модели данных

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

Добавление связи в базу данных

В Visual Studio откройте веб-приложение Contoso University, созданное в начало работы с помощью Entity Framework и веб-формы серии учебников, а затем откройте схему SchoolDiagram базы данных.

Если вы посмотрите на таблицу Department на схеме базы данных, вы увидите, что в ней есть Administrator столбец. Этот столбец является внешним ключом для Person таблицы, но связь с внешним ключом в базе данных не определена. Необходимо создать связь и обновить модель данных, чтобы платформа Entity Framework автоматически обрабатывала эту связь.

На схеме базы данных щелкните таблицу правой кнопкой Department мыши и выберите Пункт Связи.

Изображение80

В поле Связи внешних ключей нажмите кнопку Добавить, а затем нажмите кнопку с многоточием для спецификации таблиц и столбцов.

Изображение81

В диалоговом окне Таблицы и столбцы задайте для таблицы и поля Person первичного ключа значение и PersonID, а для таблицы и поля Department внешнего ключа задайте значение и Administrator. (При этом имя связи изменится с FK_Department_Department на FK_Department_Person.)

Изображение82

Нажмите кнопку ОК в поле Таблицы и столбцы , нажмите кнопку Закрыть в поле Связи внешних ключей и сохраните изменения. Если вам будет предложено сохранить Person таблицы и Department , нажмите кнопку Да.

Примечание

Если вы удалили Person строки, соответствующие данным, которые уже находятся в столбце Administrator , вы не сможете сохранить это изменение. В этом случае используйте редактор таблиц в server Обозреватель, чтобы убедиться, что Administrator значение в каждой Department строке содержит идентификатор записи, которая действительно существует в Person таблице.

После сохранения изменения вы не сможете удалить строку из Person таблицы, если этот человек является администратором отдела. В рабочем приложении необходимо предоставить определенное сообщение об ошибке, если ограничение базы данных предотвращает удаление, или указать каскадное удаление. Пример указания каскадного удаления см. в разделе Entity Framework и ASP.NET — начало работы часть 2.

Добавление представления в базу данных

На новой странице Departments.aspx , которую вы будете создавать, необходимо предоставить раскрывающийся список преподавателей с именами в формате "последний, первый", чтобы пользователи могли выбирать администраторов отделов. Чтобы упростить это, создайте представление в базе данных. Представление будет состоять только из данных, необходимых для раскрывающегося списка: полное имя (правильно отформатированный) и ключ записи.

В Обозреватель сервера разверните school.mdf, щелкните правой кнопкой мыши папку Представления и выберите Добавить новое представление.

Image06

Нажмите кнопку Закрыть при появлении диалогового окна Добавление таблицы и вставьте следующую инструкцию SQL в область SQL:

SELECT        LastName + ',' + FirstName AS FullName, PersonID
FROM          dbo.Person
WHERE        (HireDate IS NOT NULL)

Сохраните представление как vInstructorName.

Обновление модели данных

В папке DAL откройте файл SchoolModel.edmx , щелкните правой кнопкой мыши область конструктора и выберите Обновить модель из базы данных.

Изображение07

В диалоговом окне Выбор объектов базы данных перейдите на вкладку Добавить и выберите только что созданное представление.

Image08

Нажмите кнопку Готово.

В конструкторе вы увидите, что средство создало vInstructorName сущность и новую связь между сущностями Department и Person .

Изображение13

Примечание

В окнах Вывод и Список ошибок может появиться предупреждающее сообщение о том, что средство автоматически создало первичный ключ для нового vInstructorName представления. Это ожидаемое поведение.

При ссылке на новую vInstructorName сущность в коде вы не хотите использовать соглашение базы данных о префиксе в нижнем регистре "v". Поэтому необходимо переименовать сущность и набор сущностей в модели.

Откройте обозреватель моделей. vInstructorName Отображается как тип сущности и представление.

Изображение14

В разделе SchoolModel (не SchoolModel.Store) щелкните правой кнопкой мыши vInstructorName и выберите Свойства. В окне Свойства измените свойство Name на "InstructorName", а для свойства Entity Set Name ( Имя набора сущностей ) на "InstructorNames".

Изображение15

Сохраните и закройте модель данных, а затем перестройте проект.

Использование класса репозитория и элемента управления 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.

Запустите страницу, добавьте сведения о новом отделе и щелкните ссылку Вставить .

Image04

Введите значения для другого нового отдела. Введите число больше 1 000 000,00 в поле Бюджет и перейдите к следующему полю. В поле появится звездочка, и если вы наведете на нее указатель мыши, вы увидите сообщение об ошибке, введенное в метаданных для этого поля.

Image03

Нажмите кнопку Вставить, и в нижней части страницы появится сообщение об ошибке, отображаемое элементом ValidationSummary управления.

Изображение12

Затем закройте браузер и откройте страницу 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;
    }
}

При нажатии кнопки Обновить на странице ObjectDataSourceDepartments.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.

Изображение10

Если ввести недопустимую сумму бюджета и нажать кнопку Обновить, вы увидите ту же звездочку и сообщение об ошибке, что и на странице Departments.aspx .

Измените значение поля или выберите другого администратора и нажмите кнопку Обновить. Отобразится изменение.

Снимок экрана: страница

На этом завершается введение в использование ObjectDataSource элемента управления для базовых операций CRUD (создание, чтение, обновление, удаление) в Entity Framework. Вы создали простое n-уровневое приложение, но уровень бизнес-логики по-прежнему тесно связан со уровнем доступа к данным, что усложняет автоматизированное модульное тестирование. В следующем руководстве описано, как реализовать шаблон репозитория для упрощения модульного тестирования.