Usando o Entity Framework 4.0 e o controle ObjectDataSource, parte 1: Introdução

por Tom Dykstra

Esta série de tutoriais se baseia no aplicativo Web da Contoso University criado pelo Introdução com a série de tutoriais do Entity Framework 4.0. Se você não concluiu os tutoriais anteriores, como ponto de partida para este tutorial, poderá baixar o aplicativo que teria criado. Você também pode baixar o aplicativo criado pela série de tutoriais completa.

O aplicativo Web de exemplo da Contoso University demonstra como criar aplicativos ASP.NET Web Forms usando o Entity Framework 4.0 e o Visual Studio 2010. O aplicativo de exemplo é um site para uma universidade fictícia da Contoso. Ele inclui funcionalidades como admissão de alunos, criação de cursos e atribuições de instrutor.

O tutorial mostra exemplos em C#. O exemplo para download contém código no C# e no Visual Basic.

Banco de Dados Primeiro

Há três maneiras de trabalhar com dados no Entity Framework: Database First, Model First e Code First. Este tutorial é para o Banco de Dados Primeiro. Para obter informações sobre as diferenças entre esses fluxos de trabalho e diretrizes sobre como escolher o melhor para seu cenário, consulte Fluxos de trabalho de desenvolvimento do Entity Framework.

Web Forms

Assim como a série Introdução, esta série de tutoriais usa o modelo ASP.NET Web Forms e pressupõe que você saiba como trabalhar com ASP.NET Web Forms no Visual Studio. Caso contrário, veja Introdução com ASP.NET Web Forms 4.5. Se você preferir trabalhar com a estrutura MVC ASP.NET, consulte Introdução com o Entity Framework usando ASP.NET MVC.

Versões de software

Mostrado no tutorial Também funciona com
Windows 7 Windows 8
Visual Studio 2010 Visual Studio 2010 Express para Web. O tutorial não foi testado com versões posteriores do Visual Studio. Há muitas diferenças em seleções de menu, caixas de diálogo e modelos.
.NET 4 O .NET 4.5 é compatível com versões anteriores com o .NET 4, mas o tutorial não foi testado com o .NET 4.5.
Entity Framework 4 O tutorial não foi testado com versões posteriores do Entity Framework. A partir do Entity Framework 5, o EF usa por padrão o que foi introduzido com o DbContext API EF 4.1. O controle EntityDataSource foi projetado para usar a ObjectContext API. Para obter informações sobre como usar o controle EntityDataSource com a DbContext API, consulte esta postagem no blog.

Perguntas

Se você tiver perguntas que não estão diretamente relacionadas ao tutorial, poderá postá-las no fórum ASP.NET Entity Framework, no Entity Framework e no fórum LINQ to Entities ou StackOverflow.com.

O EntityDataSource controle permite que você crie um aplicativo muito rapidamente, mas normalmente exige que você mantenha uma quantidade significativa de lógica de negócios e lógica de acesso a dados em suas páginas .aspx . Se você espera que seu aplicativo cresça em complexidade e exija manutenção contínua, você pode investir mais tempo de desenvolvimento antecipadamente para criar uma estrutura de aplicativo em camadas ou n que seja mais mantenedível. Para implementar essa arquitetura, você separa a camada de apresentação da BLL (camada de lógica de negócios) e da DAL (camada de acesso a dados). Uma maneira de implementar essa estrutura é usar o ObjectDataSource controle em vez do EntityDataSource controle. Ao usar o ObjectDataSource controle, você implementa seu próprio código de acesso a dados e o invoca em páginas .aspx usando um controle que tem muitos dos mesmos recursos que outros controles de fonte de dados. Isso permite combinar as vantagens de uma abordagem de n camadas com os benefícios de usar um controle Web Forms para acesso a dados.

O ObjectDataSource controle também oferece mais flexibilidade de outras maneiras. Como você escreve seu próprio código de acesso a dados, é mais fácil fazer mais do que apenas ler, inserir, atualizar ou excluir um tipo de entidade específico, que são as tarefas que o EntityDataSource controle foi projetado para executar. Por exemplo, você pode executar o registro em log sempre que uma entidade for atualizada, arquivar dados sempre que uma entidade for excluída ou marcar automaticamente e atualizar dados relacionados conforme necessário ao inserir uma linha com um valor de chave estrangeira.

Classes de repositório e lógica de negócios

Um ObjectDataSource controle funciona invocando uma classe que você cria. A classe inclui métodos que recuperam e atualizam dados e você fornece os nomes desses métodos para o ObjectDataSource controle na marcação. Durante o processamento de renderização ou postback, o ObjectDataSource chama os métodos que você especificou.

Além das operações CRUD básicas, a classe que você cria para usar com o ObjectDataSource controle pode precisar executar a lógica de negócios quando os ObjectDataSource dados forem lidos ou atualizados. Por exemplo, quando você atualiza um departamento, talvez seja necessário validar que nenhum outro departamento tem o mesmo administrador porque uma pessoa não pode ser administrador de mais de um departamento.

Em algumas ObjectDataSource documentações, como a visão geral da Classe ObjectDataSource, o controle chama uma classe conhecida como um objeto de negócios que inclui lógica de negócios e lógica de acesso a dados. Neste tutorial, você criará classes separadas para lógica de negócios e para lógica de acesso a dados. A classe que encapsula a lógica de acesso a dados é chamada de repositório. A classe lógica de negócios inclui métodos de lógica de negócios e métodos de acesso a dados, mas os métodos de acesso a dados chamam o repositório para executar tarefas de acesso a dados.

Você também criará uma camada de abstração entre a BLL e a DAL que facilita o teste de unidade automatizado da BLL. Essa camada de abstração é implementada criando uma interface e usando a interface quando você instancia o repositório na classe de lógica de negócios. Isso possibilita que você forneça à classe de lógica de negócios uma referência a qualquer objeto que implemente a interface do repositório. Para a operação normal, você fornece um objeto de repositório que funciona com o Entity Framework. Para teste, você fornece um objeto de repositório que funciona com dados armazenados de uma maneira que você pode manipular facilmente, como variáveis de classe definidas como coleções.

A ilustração a seguir mostra a diferença entre uma classe de lógica de negócios que inclui a lógica de acesso a dados sem um repositório e uma que usa um repositório.

Imagem05

Você começará criando páginas da Web nas quais o ObjectDataSource controle está associado diretamente a um repositório porque ele executa apenas tarefas básicas de acesso a dados. No próximo tutorial, você criará uma classe lógica de negócios com lógica de validação e associará o ObjectDataSource controle a essa classe em vez de à classe de repositório. Você também criará testes de unidade para a lógica de validação. No terceiro tutorial desta série, você adicionará a funcionalidade de classificação e filtragem ao aplicativo.

As páginas criadas neste tutorial funcionam com o Departments conjunto de entidades do modelo de dados que você criou na série de tutoriais Introdução.

Uma captura de tela que mostra a aparência da página Departamentos.

Imagem02

Atualizando o banco de dados e o modelo de dados

Você começará este tutorial fazendo duas alterações no banco de dados, que exigem alterações correspondentes no modelo de dados que você criou no Introdução com o Entity Framework e os tutoriais de Web Forms. Em um desses tutoriais, você fez alterações no designer manualmente para sincronizar o modelo de dados com o banco de dados após uma alteração de banco de dados. Neste tutorial, você usará a ferramenta Atualizar Modelo do Banco de Dados do designer para atualizar o modelo de dados automaticamente.

Adicionando uma relação ao banco de dados

No Visual Studio, abra o aplicativo Web da Contoso University que você criou no Introdução com o Entity Framework e Web Forms série de tutoriais e abra o diagrama de SchoolDiagram banco de dados.

Se você examinar a Department tabela no diagrama de banco de dados, verá que ela tem uma Administrator coluna. Essa coluna é uma chave estrangeira para a Person tabela, mas nenhuma relação de chave estrangeira é definida no banco de dados. Você precisa criar a relação e atualizar o modelo de dados para que o Entity Framework possa lidar automaticamente com essa relação.

No diagrama de banco de dados, clique com o botão direito do mouse na Department tabela e selecione Relações.

Imagem80

Na caixa Relações de Chave Estrangeira , clique em Adicionar e, em seguida, clique nas reticências para Especificação de Tabelas e Colunas.

Imagem81

Na caixa de diálogo Tabelas e Colunas , defina a tabela e o campo de chave primária como Person e PersonIDe defina a tabela e o campo de chave estrangeira como Department e Administrator. (Quando você fizer isso, o nome da relação mudará de FK_Department_Department para FK_Department_Person.)

Imagem82

Clique em OK na caixa Tabelas e Colunas , clique em Fechar na caixa Relações de Chave Estrangeira e salve as alterações. Se for perguntado se deseja salvar as Person tabelas e Department , clique em Sim.

Observação

Se você excluiu Person linhas que correspondem aos dados que já estão na Administrator coluna, não será possível salvar essa alteração. Nesse caso, use o editor de tabela no Server Explorer para garantir que o Administrator valor em cada Department linha contenha a ID de um registro que realmente existe na Person tabela.

Depois de salvar a alteração, você não poderá excluir uma linha da Person tabela se essa pessoa for um administrador do departamento. Em um aplicativo de produção, você forneceria uma mensagem de erro específica quando uma restrição de banco de dados impede uma exclusão ou especificaria uma exclusão em cascata. Para obter um exemplo de como especificar uma exclusão em cascata, consulte The Entity Framework and ASP.NET – Introdução Part 2.

Adicionando uma exibição ao banco de dados

Na nova página Departments.aspx que você criará, você deseja fornecer uma lista suspensa de instrutores, com nomes no formato "last, first" para que os usuários possam selecionar administradores de departamento. Para facilitar isso, você criará uma exibição no banco de dados. A exibição consistirá apenas nos dados necessários para a lista suspensa: o nome completo (formatado corretamente) e a chave de registro.

Em Servidor Explorer, expanda School.mdf, clique com o botão direito do mouse na pasta Exibições e selecione Adicionar Novo Modo de Exibição.

Image06

Clique em Fechar quando a caixa de diálogo Adicionar Tabela for exibida e cole a seguinte instrução SQL no painel SQL:

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

Salve o modo de exibição como vInstructorName.

Atualizando o modelo de dados

Na pasta DAL , abra o arquivo SchoolModel.edmx , clique com o botão direito do mouse na superfície de design e selecione Atualizar Modelo no Banco de Dados.

Imagem07

Na caixa de diálogo Escolher Objetos de Banco de Dados , selecione a guia Adicionar e selecione a exibição que você acabou de criar.

Imagem08

Clique em Concluir.

No designer, você verá que a ferramenta criou uma vInstructorName entidade e uma nova associação entre as Department entidades e Person .

Imagem13

Observação

Nas janelas Saída e Lista de Erros , você pode ver uma mensagem de aviso informando que a ferramenta criou automaticamente uma chave primária para o novo vInstructorName modo de exibição. Este comportamento é esperado.

Quando você se refere à nova vInstructorName entidade no código, não deseja usar a convenção de banco de dados de prefixar um "v" de minúsculas nela. Portanto, você renomeará a entidade e a entidade definidas no modelo.

Abra o Navegador de Modelos. Você vê vInstructorName listado como um tipo de entidade e uma exibição.

Imagem14

Em SchoolModel (não SchoolModel.Store), clique com o botão direito do mouse em vInstructorName e selecione Propriedades. Na janela Propriedades , altere a propriedade Name para "InstructorName" e altere a propriedade Nome do Conjunto de Entidades para "InstructorNames".

Imagem15

Salve e feche o modelo de dados e recompile o projeto.

Usando uma classe de repositório e um controle ObjectDataSource

Crie um novo arquivo de classe na pasta DAL , nomeie-o SchoolRepository.cs e substitua o código existente pelo seguinte código:

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);
        }

    }
}

Esse código fornece um único GetDepartments método que retorna todas as entidades no Departments conjunto de entidades. Como você sabe que acessará a Person propriedade de navegação para cada linha retornada, especifique o carregamento ansioso para essa propriedade usando o Include método . A classe também implementa a IDisposable interface para garantir que a conexão de banco de dados seja liberada quando o objeto for descartado.

Observação

Uma prática comum é criar uma classe de repositório para cada tipo de entidade. Neste tutorial, uma classe de repositório para vários tipos de entidade é usada. Para obter mais informações sobre o padrão do repositório, consulte as postagens no blog da equipe do Entity Framework e no blog de Julie Lerman.

O GetDepartments método retorna um IEnumerable objeto em vez de um IQueryable objeto para garantir que a coleção retornada seja utilizável mesmo depois que o próprio objeto do repositório for descartado. Um IQueryable objeto pode causar acesso ao banco de dados sempre que ele for acessado, mas o objeto do repositório pode ser descartado quando um controle de entrada de dados tenta renderizar os dados. Você pode retornar outro tipo de coleção, como um IList objeto em vez de um IEnumerable objeto . No entanto, retornar um IEnumerable objeto garante que você possa executar tarefas típicas de processamento de lista somente leitura, como foreach loops e consultas LINQ, mas não é possível adicionar ou remover itens na coleção, o que pode implicar que essas alterações seriam mantidas no banco de dados.

Crie uma página Departments.aspx que usa a página master Site.Master e adicione a seguinte marcação no Content controle chamado 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>

Essa marcação cria um ObjectDataSource controle que usa a classe de repositório que você acabou de criar e um GridView controle para exibir os dados. O GridView controle especifica comandos Editar e Excluir , mas você ainda não adicionou código para dar suporte a eles.

Várias colunas usam DynamicField controles para que você possa aproveitar a funcionalidade automática de formatação e validação de dados. Para que funcionem, você precisará chamar o EnableDynamicData método no Page_Init manipulador de eventos. (DynamicControl os controles não são usados no Administrator campo porque não funcionam com propriedades de navegação.)

Os Vertical-Align="Top" atributos se tornarão importantes posteriormente quando você adicionar uma coluna que tenha um controle aninhado GridView à grade.

Abra o arquivo Departments.aspx.cs e adicione a seguinte using instrução:

using ContosoUniversity.DAL;

Em seguida, adicione o seguinte manipulador para o evento da Init página:

protected void Page_Init(object sender, EventArgs e)
{
    DepartmentsGridView.EnableDynamicData(typeof(Department));
}

Na pasta DAL , crie um novo arquivo de classe chamado Department.cs e substitua o código existente pelo seguinte código:

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; }

    }
}

Esse código adiciona metadados ao modelo de dados. Ele especifica que a Budget propriedade da entidade realmente representa a Department moeda, embora seu tipo de dados seja Decimale especifica que o valor deve estar entre 0 e US$ 1.000.000,00. Ele também especifica que a StartDate propriedade deve ser formatada como uma data no formato mm/dd/yyyy.

Execute a página Departments.aspx .

Uma captura de tela que mostra a página Departamentos quando ela foi executada.

Observe que, embora você não tenha especificado uma cadeia de caracteres de formato na marcação de página Departments.aspx para as colunas Orçamento ou Data de Início , a formatação padrão de moeda e data foi aplicada a elas pelos DynamicField controles, usando os metadados fornecidos no arquivo Department.cs .

Adicionando a funcionalidade Inserir e Excluir

Abra SchoolRepository.cs, adicione o código a seguir para criar um Insert método e um Delete método. O código também inclui um método chamado GenerateDepartmentID que calcula o próximo valor de chave de registro disponível para uso pelo Insert método . Isso é necessário porque o banco de dados não está configurado para calcular isso automaticamente para a Department tabela.

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;
}

O método Attach

O DeleteDepartment método chama o Attach método para restabelecer o link mantido no gerenciador de estado do objeto do contexto do objeto entre a entidade na memória e a linha de banco de dados que ela representa. Isso deve ocorrer antes que o método chame o SaveChanges método .

O contexto do objeto de termo refere-se à classe Entity Framework derivada da ObjectContext classe que você usa para acessar seus conjuntos de entidades e entidades. No código deste projeto, a classe é chamada SchoolEntitiese uma instância dela é sempre chamada contextde . O gerenciador de estado do objeto do contexto do objeto é uma classe derivada da ObjectStateManager classe . O contato do objeto usa o gerenciador de estado do objeto para armazenar objetos de entidade e para acompanhar se cada um está sincronizado com sua linha de tabela ou linhas correspondentes no banco de dados.

Quando você lê uma entidade, o contexto de objeto a armazena no gerenciador de estado do objeto e controla se essa representação do objeto está em sincronia com o banco de dados. Por exemplo, se você alterar um valor de propriedade, um sinalizador será definido para indicar que a propriedade alterada não está mais sincronizada com o banco de dados. Em seguida, quando você chama o SaveChanges método , o contexto do objeto sabe o que fazer no banco de dados porque o gerenciador de estado do objeto sabe exatamente o que é diferente entre o estado atual da entidade e o estado do banco de dados.

No entanto, esse processo normalmente não funciona em um aplicativo Web, pois a instância de contexto de objeto que lê uma entidade, juntamente com tudo em seu gerenciador de estado de objeto, é descartada depois que uma página é renderizada. A instância de contexto do objeto que deve aplicar alterações é uma nova instância para processamento de postback. No caso do DeleteDepartment método , o ObjectDataSource controle recria a versão original da entidade para você a partir de valores no estado de exibição, mas essa entidade recriada Department não existe no gerenciador de estado do objeto. Se você chamou o DeleteObject método nessa entidade recriada, a chamada falhará porque o contexto do objeto não sabe se a entidade está em sincronia com o banco de dados. No entanto, chamar o Attach método restabeleça o mesmo acompanhamento entre a entidade recriada e os valores no banco de dados que foi originalmente feito automaticamente quando a entidade foi lida em uma instância anterior do contexto do objeto.

Há momentos em que você não deseja que o contexto do objeto rastreie entidades no gerenciador de estado do objeto e você pode definir sinalizadores para impedir que ele faça isso. Exemplos disso são mostrados em tutoriais posteriores nesta série.

O método SaveChanges

Essa classe de repositório simples ilustra os princípios básicos de como executar operações CRUD. Neste exemplo, o SaveChanges método é chamado imediatamente após cada atualização. Em um aplicativo de produção, talvez você queira chamar o SaveChanges método de um método separado para fornecer mais controle sobre quando o banco de dados é atualizado. (No final do próximo tutorial, você encontrará um link para um white paper que discute a unidade de padrão de trabalho que é uma abordagem para coordenar atualizações relacionadas.) Observe também que, no exemplo, o DeleteDepartment método não inclui código para lidar com conflitos de simultaneidade; o código para fazer isso será adicionado em um tutorial posterior nesta série.

Recuperando nomes de instrutor para selecionar ao inserir

Os usuários devem ser capazes de selecionar um administrador em uma lista de instrutores em uma lista suspensa ao criar novos departamentos. Portanto, adicione o seguinte código a SchoolRepository.cs para criar um método para recuperar a lista de instrutores usando a exibição que você criou anteriormente:

public IEnumerable<InstructorName> GetInstructorNames()
{
    return context.InstructorNames.OrderBy("it.FullName").ToList();
}

Criando uma página para inserir departamentos

Crie uma página DepartmentsAdd.aspx que usa a página Site.Master e adicione a seguinte marcação no Content controle chamado 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"  />

Essa marcação cria dois ObjectDataSource controles, um para inserir novas Department entidades e outro para recuperar nomes de instrutor para o DropDownList controle usado para selecionar administradores de departamento. A marcação cria um DetailsView controle para inserir novos departamentos e especifica um manipulador para o evento do ItemInserting controle para que você possa definir o valor da Administrator chave estrangeira. No final, há um ValidationSummary controle para exibir mensagens de erro.

Abra DepartmentsAdd.aspx.cs e adicione a seguinte using instrução:

using ContosoUniversity.DAL;

Adicione a seguinte variável de classe e métodos:

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;
}

O Page_Init método habilita a funcionalidade de Dados Dinâmicos. O manipulador DropDownList do evento do Init controle salva uma referência ao controle e o manipulador DetailsView do evento do Inserting controle usa essa referência para obter o PersonID valor do instrutor selecionado e atualizar a Administrator propriedade de chave estrangeira da Department entidade.

Execute a página, adicione informações para um novo departamento e clique no link Inserir .

Imagem04

Insira valores para outro novo departamento. Insira um número maior que 1.000.000,00 no campo Orçamento e tab para o próximo campo. Um asterisco aparece no campo e, se você segurar o ponteiro do mouse sobre ele, poderá ver a mensagem de erro que você inseriu nos metadados desse campo.

Imagem03

Clique em Inserir e você verá a mensagem de erro exibida pelo ValidationSummary controle na parte inferior da página.

Imagem12

Em seguida, feche o navegador e abra a página Departments.aspx . Adicione a funcionalidade delete à página Departments.aspx adicionando um DeleteMethod atributo ao ObjectDataSource controle e um DataKeyNames atributo ao GridView controle. As marcas de abertura para esses controles agora serão semelhantes ao seguinte exemplo:

<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" >

Execute a página.

Uma captura de tela que mostra a página Departamentos depois que ela foi executada.

Exclua o departamento que você adicionou quando executou a página DepartmentsAdd.aspx .

Adicionando funcionalidade de atualização

Abra SchoolRepository.cs e adicione o seguinte Update método:

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;
    }
}

Quando você clica em Atualizar na página Departments.aspx , o ObjectDataSource controle cria duas Department entidades para passar para o UpdateDepartment método . Um contém os valores originais que foram armazenados no estado de exibição e o outro contém os novos valores que foram inseridos no GridView controle . O código no UpdateDepartment método passa a Department entidade que tem os valores originais para o Attach método para estabelecer o acompanhamento entre a entidade e o que está no banco de dados. Em seguida, o código passa a Department entidade que tem os novos valores para o ApplyCurrentValues método . O contexto do objeto compara os valores antigos e novos. Se um novo valor for diferente de um valor antigo, o contexto do objeto alterará o valor da propriedade. Em SaveChanges seguida, o método atualiza apenas as colunas alteradas no banco de dados. (No entanto, se a função de atualização dessa entidade fosse mapeada para um procedimento armazenado, toda a linha seria atualizada independentemente de quais colunas foram alteradas.)

Abra o arquivo Departments.aspx e adicione os seguintes atributos ao DepartmentsObjectDataSource controle :

  • UpdateMethod="UpdateDepartment"
  • ConflictDetection="CompareAllValues"
    Isso faz com que os valores antigos sejam armazenados no estado de exibição para que possam ser comparados com os novos valores no Update método .
  • OldValuesParameterFormatString="orig{0}"
    Isso informa ao controle que o nome do parâmetro de valores originais é origDepartment .

A marcação para a marca de abertura do ObjectDataSource controle agora se assemelha ao seguinte exemplo:

<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}" >

Adicione um OnRowUpdating="DepartmentsGridView_RowUpdating" atributo ao GridView controle . Você usará isso para definir o valor da Administrator propriedade com base na linha selecionada pelo usuário em uma lista suspensa. A GridView marca de abertura agora é semelhante ao seguinte exemplo:

<asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="DepartmentsObjectDataSource" DataKeyNames="DepartmentID"
        OnRowUpdating="DepartmentsGridView_RowUpdating">

Adicione um EditItemTemplate controle para a Administrator coluna ao GridView controle , imediatamente após o ItemTemplate controle dessa coluna:

<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>

Esse EditItemTemplate controle é semelhante ao InsertItemTemplate controle na página DepartmentsAdd.aspx . A diferença é que o valor inicial do controle é definido usando o SelectedValue atributo .

Antes do GridView controle, adicione um ValidationSummary controle como você fez na página DepartmentsAdd.aspx .

<asp:ValidationSummary ID="DepartmentsValidationSummary" runat="server" 
        ShowSummary="true" DisplayMode="BulletList"  />

Abra Departments.aspx.cs e, imediatamente após a declaração de classe parcial, adicione o seguinte código para criar um campo privado para fazer referência ao DropDownList controle:

private DropDownList administratorsDropDownList;

Em seguida, adicione manipuladores para o DropDownList evento do Init controle e o GridView evento do RowUpdating controle:

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;
}

O manipulador do Init evento salva uma referência ao DropDownList controle no campo de classe. O manipulador do RowUpdating evento usa a referência para obter o valor que o usuário inseriu e aplicá-lo à Administrator propriedade da Department entidade.

Use a página DepartmentsAdd.aspx para adicionar um novo departamento, execute a página Departments.aspx e clique em Editar na linha que você adicionou.

Observação

Você não poderá editar linhas que não adicionou (ou seja, que já estavam no banco de dados), devido a dados inválidos no banco de dados; os administradores das linhas que foram criadas com o banco de dados são alunos. Se você tentar editar um deles, receberá uma página de erro que relata um erro como 'InstructorsDropDownList' has a SelectedValue which is invalid because it does not exist in the list of items.

Imagem10

Se você inserir um valor de Orçamento inválido e clicar em Atualizar, verá o mesmo asterisco e a mesma mensagem de erro que viu na página Departments.aspx .

Altere um valor de campo ou selecione um administrador diferente e clique em Atualizar. A alteração é exibida.

Uma captura de tela que mostra a página Departamentos.

Isso conclui a introdução ao uso do ObjectDataSource controle para operações CRUD básicas (criar, ler, atualizar, excluir) com o Entity Framework. Você criou um aplicativo simples de n camadas, mas a camada de lógica de negócios ainda está firmemente acoplada à camada de acesso a dados, o que complica o teste de unidade automatizado. No tutorial a seguir, você verá como implementar o padrão de repositório para facilitar o teste de unidade.