Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
por Tom Dykstra
Esta série de tutoriais baseia-se na aplicação web da Contoso University, criada pela série de tutoriais Getting Started with the Entity Framework 4.0 . Se não completaste os tutoriais anteriores, como ponto de partida para este tutorial podes descarregar a aplicação que terias criado. Também pode descarregar a aplicação criada pela série completa de tutoriais.
A aplicação web de exemplo da Universidade Contoso demonstra como criar ASP.NET aplicações Web Forms usando o Entity Framework 4.0 e o Visual Studio 2010. A aplicação de exemplo é um site de uma Universidade Contoso fictícia. Inclui funcionalidades como admissão de alunos, criação de cursos e tarefas de instrutores.
O tutorial mostra exemplos em C#. O exemplo descarregável contém código tanto em C# como em Visual Basic.
Base de Dados Primeiro
Existem três formas de trabalhar com dados no Entity Framework: Base de Dados Primeiro, Modelo Primeiro e Código Primeiro. Este tutorial é para o Database First. Para informações sobre as diferenças entre estes fluxos de trabalho e orientações sobre como escolher o melhor para o seu cenário, consulte Workflows de Desenvolvimento do Entity Framework.
Web Forms
Tal como a série Getting Started, esta série de tutoriais utiliza o modelo ASP.NET Web Forms e assume que sabes trabalhar com ASP.NET Web Forms no Visual Studio. Se não o fizer, consulte Introdução aos Formulários Web do ASP.NET 4.5. Se preferir trabalhar com o framework ASP.NET MVC, veja Começar 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. Existem muitas diferenças na seleção de menus, caixas de diálogo e modelos. .NET 4 O .NET 4.5 é compatível retroativamente com o .NET 4, mas o tutorial não foi testado com o .NET 4.5. Estrutura da Entidade 4 O tutorial não foi testado com versões posteriores do Entity Framework. A partir do Entity Framework 5, o EF utiliza por defeito o DbContext APIque foi introduzido com o EF 4.1. O controlo EntityDataSource foi concebido para usar aObjectContextAPI. Para informações sobre como usar o controlo EntityDataSource com aDbContextAPI, consulte este artigo do blogue.Perguntas
Se tiver perguntas que não estejam diretamente relacionadas com o tutorial, pode partilhá-las no fórum ASP.NET Entity Framework, no fórum Entity Framework e LINQ to Entities, ou StackOverflow.com.
O EntityDataSource controlo permite-lhe criar uma aplicação rapidamente, mas geralmente requer que mantenha uma quantidade significativa de lógica de negócio e de acesso a dados nas suas páginas .aspx. Se espera que a sua aplicação cresça em complexidade e exija manutenção contínua, pode investir mais tempo de desenvolvimento desde o início para criar uma estrutura de aplicação n-tier ou em camadas que seja mais sustentável. Para implementar esta arquitetura, separa-se a camada de apresentação da camada de lógica de negócio (BLL) e da camada de acesso a dados (DAL). Uma forma de implementar esta estrutura é usar o controlo ObjectDataSource em vez do controlo EntityDataSource . Quando usa o ObjectDataSource controlo, implementa o seu próprio código de acesso aos dados e depois invoca-o em .aspx páginas usando um controlo que tem muitas das mesmas funcionalidades de outros controlos de fonte de dados. Isto permite-lhe combinar as vantagens de uma abordagem n-tier com as vantagens de usar um controlo Web Forms para o acesso aos dados.
O ObjectDataSource controlo dá-te mais flexibilidade noutras formas também. Como escreves o teu próprio código de acesso a dados, é mais fácil fazer mais do que apenas ler, inserir, atualizar ou eliminar um tipo específico de entidade, que são as tarefas para as quais o EntityDataSource controlo foi concebido. Por exemplo, pode realizar registos sempre que uma entidade é atualizada, arquivar dados sempre que uma entidade é eliminada, ou verificar e atualizar automaticamente os dados relacionados conforme necessário ao inserir uma linha com valor de chave estrangeira.
Lógica de Negócio e Classes de Repositório
Um ObjectDataSource controlo funciona invocando uma classe que crias. A classe inclui métodos que recuperam e atualizam dados, e tu forneces os nomes desses métodos ao controlo ObjectDataSource em markup. Durante o processamento de renderização ou de postback, o ObjectDataSource chama os métodos que especificaste.
Para além das operações básicas CRUD, a classe que criaste para usar com o controlo ObjectDataSource pode precisar de executar lógica de negócio quando o ObjectDataSource lê ou atualiza os dados. Por exemplo, ao atualizar um departamento, pode ser necessário validar que nenhum outro departamento tem o mesmo administrador porque uma pessoa não pode ser administrador de mais do que um departamento.
Em alguma ObjectDataSource documentação, como a visão geral da classe ObjectDataSource, o controlo chama uma classe referida como objeto de negócio que inclui tanto a lógica de negócio como a lógica de acesso a dados. Neste tutorial irá criar classes separadas para lógica de negócio e para lógica de acesso a dados. A classe que encapsula a lógica de acesso a dados chama-se repositório. A classe de lógica de negócio inclui tanto métodos de lógica de negócio como métodos de acesso a dados, mas os métodos de acesso a dados chamam o repositório para realizar tarefas de acesso a dados.
Também irá criar uma camada de abstração entre o seu BLL e o DAL que facilita o teste unitário automatizado do BLL. Esta camada de abstração é implementada criando uma interface e utilizando a interface quando se instancia o repositório na classe de lógica de negócios. Isto permite fornecer à classe de lógica de negócio uma referência a qualquer objeto que implemente a interface do repositório. Para funcionamento normal, fornece um objeto repositório que funciona com o Entity Framework. Para testes, forneces um objeto repositório que trabalha com dados armazenados de forma que podes manipular facilmente, como variáveis de classe definidas como coleções.
A ilustração seguinte mostra a diferença entre uma classe de lógica de negócios que inclui lógica de acesso a dados sem repositório e uma que utiliza um repositório.
Começará por criar páginas web em que o controlo ObjectDataSource está diretamente vinculado a um repositório porque apenas realiza tarefas básicas de acesso a dados. No próximo tutorial vais criar uma classe de lógica de negócio com lógica de validação e atribuir o ObjectDataSource controlo a essa classe em vez de à classe repositório. Também irá criar testes unitários para a lógica de validação. No terceiro tutorial desta série, irá adicionar funcionalidades de ordenação e filtragem à aplicação.
As páginas que crias neste tutorial trabalham com o Departments conjunto de entidades do modelo de dados que criaste na série de tutoriais Getting Started.
Atualização da Base de Dados e do Modelo de Dados
Vai começar este tutorial fazendo duas alterações à base de dados, ambas exigindo alterações correspondentes ao modelo de dados que criou nos tutoriais Getting Started with the Entity Framework e Web Forms . Num desses tutoriais, fazias alterações manualmente no designer para sincronizar o modelo de dados com a base de dados após uma alteração na base de dados. Neste tutorial, irá usar a ferramenta Atualizar Modelo da Base de Dados do designer para atualizar automaticamente o modelo de dados.
Adicionar uma Relação à Base de Dados
No Visual Studio, abra a aplicação web da Contoso University que criou na série de tutoriais Getting Started with the Entity Framework e Web Forms e depois abra o diagrama SchoolDiagram da base de dados.
Se olhar para a Department tabela no diagrama da base de dados, verá que tem uma Administrator coluna. Esta coluna é uma chave estrangeira da Person tabela, mas não existe uma relação de chave estrangeira definida na base de dados. É necessário criar a relação e atualizar o modelo de dados para que o Entity Framework possa gerir automaticamente esta relação.
No diagrama da base de dados, clique com o botão direito na Department tabela e selecione Relações.
Na caixa Relações de Chave Estrangeira , clique em Adicionar, depois clique na elipse para Especificação de Tabelas e Colunas.
Na caixa de diálogo Tabelas e Colunas , defina a tabela de chaves primárias e o campo para Person e PersonID, e defina a tabela e campo de chave estrangeira para Department e Administrator. (Quando fazes isto, o nome da relação muda de FK_Department_Department para FK_Department_Person.)
Clique em OK na caixa Tabelas e Colunas , clique em Fechar na caixa Relações de Chave Estrangeira e guarde as alterações. Se lhe perguntarem se quer guardar as tabelas Person e Department, clique em Sim.
Observação
Se apagaste Person linhas que correspondem a dados que já estão na Administrator coluna, não conseguirás guardar esta alteração. Nesse caso, use o editor de tabelas no Server Explorer para garantir que o Administrator valor em cada Department linha contém o ID de um registo que realmente existe na Person tabela.
Depois de guardar a alteração, não poderá apagar uma linha da Person tabela se essa pessoa for administrador de departamento. Numa aplicação de produção, forneceria uma mensagem de erro específica quando uma restrição de base de dados impediria a eliminação, ou especificaria uma eliminação em cascata. Para um exemplo de como especificar uma eliminação em cascata, consulte The Entity Framework and ASP.NET – Getting Started Parte 2.
Adicionar uma Vista à Base de Dados
Na nova Departments.aspx página que irá criar, deve fornecer uma lista suspensa de instrutores, com nomes no formato "sobrenome, primeiro nome", para que os utilizadores possam selecionar os administradores do departamento. Para facilitar isso, você irá criar uma vista na base de dados. A visualização consistirá apenas nos dados necessários para a lista suspensa: o nome completo (corretamente formatado) e a chave de registo.
No Explorador de Servidores, expanda School.mdf, clique com o botão direito na pasta Exibições e selecione Adicionar Nova Exibição.
Clique em Fechar quando aparecer a caixa de diálogo Adicionar Tabela e cole a seguinte instrução SQL no painel SQL:
SELECT LastName + ',' + FirstName AS FullName, PersonID
FROM dbo.Person
WHERE (HireDate IS NOT NULL)
Guardar a visualização como vInstructorName.
Atualização do Modelo de Dados
Na pasta DAL , abra o ficheiro SchoolModel.edmx , clique com o botão direito na superfície de design e selecione Atualizar Modelo a partir da Base de Dados.
Na caixa de diálogo Escolher Objetos da Base de Dados, selecione a aba Adicionar e selecione a vista que acabou de criar.
Clique em Concluir.
No designer, vê-se que a ferramenta criou uma entidade vInstructorName e uma nova associação entre as entidades Department e Person.
Observação
Nas janelas de Saída e Lista de Erros pode ver uma mensagem de aviso a informar que a ferramenta criou automaticamente uma chave primária para a nova vInstructorName vista. Este é um comportamento esperado.
Quando te referes à nova vInstructorName entidade no código, não queres usar a convenção da base de dados de prefixar um "v" minúsculo. Portanto, irá renomear a entidade e o conjunto de entidades no modelo.
Abre o Navegador de Modelos. Vê vInstructorName listados como um tipo de entidade e uma visualização.
Em SchoolModel (não SchoolModel.Store), clique com o botão direito em vInstructorName e selecione Propriedades. Na janela de Propriedades , altere a propriedade Nome para "NomeInstrutor" e altere a propriedade Nome do Conjunto de Entidades para "NomesInstrutores".
Guarda e fecha o modelo de dados, e depois reconstrói o projeto.
Usando uma classe repositório e um controlo ObjectDataSource
Crie um novo ficheiro de classe na pasta DAL , nomei-o SchoolRepository.cs e substitua o código existente pelo seguinte:
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);
}
}
}
Este código fornece um único GetDepartments método que devolve todas as entidades do Departments conjunto de entidades. Como sabes que irás aceder a Person propriedade de navegação para cada linha devolvida, especificas carregamento ávido para essa propriedade usando o método Include. A classe também implementa a IDisposable interface para garantir que a ligação à base de dados é libertada quando o objeto é eliminado.
Observação
Uma prática comum é criar uma classe repositório para cada tipo de entidade. Neste tutorial, é usada uma classe repositório para múltiplos tipos de entidades. Para mais informações sobre o padrão de repositório, consulte as publicações no blogue da equipa do Entity Framework e no blogue da Julie Lerman.
O GetDepartments método devolve um IEnumerable objeto em vez de um IQueryable objeto para garantir que a coleção devolvida é utilizável mesmo depois de o próprio objeto repositório ser eliminado. Um objeto IQueryable pode acionar acesso à base de dados sempre que for acedido, mas o objeto do repositório pode ser libertado quando um controlo vinculado a dados tenta apresentar os dados. Pode devolver outro tipo de coleção, como um IList objeto em vez de um IEnumerable objeto. No entanto, devolver um objeto IEnumerable garante que pode realizar tarefas típicas de processamento de listas apenas de leitura, como consultas LINQ e ciclos foreach, mas não pode adicionar ou remover itens na coleção, o que pode implicar que tais alterações não seriam persistidas na base de dados.
Crie uma página Departments.aspx que utilize a página-mestre Site.Master e adicione a seguinte marcação no Content controlo chamada 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>
Esta marcação cria um ObjectDataSource controlo que usa a classe repositório que acabou de criar, e um GridView controlo para mostrar os dados. O GridView controlo especifica comandos Editar e Eliminar , mas ainda não adicionaste código para os suportar.
Várias colunas utilizam DynamicField controles para que possa tirar partido da funcionalidade automática de formatação e validação de dados. Para que estes funcionem, terá de chamar o método EnableDynamicData no manipulador de eventos Page_Init. (DynamicControl os controlos não são usados no Administrator campo porque não funcionam com propriedades de navegação.)
Os Vertical-Align="Top" atributos tornar-se-ão importantes mais tarde, quando adicionares uma coluna que tenha um controlo aninhado GridView na grelha.
Abra o ficheiro Departments.aspx.cs e adicione a seguinte using instrução:
using ContosoUniversity.DAL;
Depois, adicione o seguinte handler para o evento da página Init:
protected void Page_Init(object sender, EventArgs e)
{
DepartmentsGridView.EnableDynamicData(typeof(Department));
}
Na pasta DAL , crie um novo ficheiro de classe chamado Department.cs e substitua o código existente pelo seguinte:
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; }
}
}
Este código adiciona metadados ao modelo de dados. Especifica que a Budget propriedade da Department entidade representa efetivamente a moeda, embora o seu tipo de dado seja Decimal, e especifica que o valor deve estar entre 0 e $1.000.000,00. Também especifica que a StartDate propriedade deve ser formatada como uma data no formato mm/dd/yyyy.
Executa a página Departments.aspx .
Note que, embora não tenha especificado uma cadeia de formato na marcação da página Departments.aspx para as colunas Orçamento ou Data de Início, foi aplicada a formatação de moeda e data por defeito pelos DynamicField controlos, usando os metadados que forneceu no ficheiro Department.cs.
Adicionar funcionalidades de Inserir e Eliminar
Abrir SchoolRepository.cs, adicionar o código seguinte para criar um Insert método e um Delete método. O código inclui também um método chamado GenerateDepartmentID que calcula o próximo valor da chave de registo disponível para uso pelo Insert método. Isto é necessário porque a base de dados não está configurada para calcular isto 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 método DeleteDepartment chama o método Attach para restabelecer a ligação mantida pelo gestor de estado de objetos no contexto do objeto, entre a entidade na memória e a linha da base de dados que ela representa. Isto deve acontecer antes de o método chamar o SaveChanges método.
O termo contexto de objeto refere-se à classe do Entity Framework que deriva da ObjectContext classe que você utiliza para aceder aos seus conjuntos de entidades e entidades. No código deste projeto, a classe é chamada SchoolEntities, e uma instância dela é sempre chamada context. O gestor de estado do objeto no contexto do objeto é uma classe que deriva da ObjectStateManager classe. O contacto do objeto utiliza o gestor de estado do objeto para armazenar objetos de entidade e para registar se cada um está sincronizado com a sua linha ou linhas da tabela correspondentes na base de dados.
Quando lês uma entidade, o contexto do objeto armazena-a no gestor de estados do objeto e regista se essa representação do objeto está sincronizada com a base de dados. Por exemplo, se alterar o valor de uma propriedade, uma flag é definida para indicar que a propriedade que alterou já não está sincronizada com a base de dados. Depois, quando chamas o SaveChanges método, o contexto do objeto sabe o que fazer na base de dados porque o gestor de estado do objeto sabe exatamente o que é diferente entre o estado atual da entidade e o estado da base de dados.
No entanto, este processo normalmente não funciona numa aplicação web, porque a instância de contexto do objeto que lê uma entidade, juntamente com tudo no seu gestor de estado de objetos, é eliminada após a renderização de uma página. A instância de contexto do objeto que tem de aplicar alterações é uma nova que é instanciada para processamento pós-retorno. No caso do DeleteDepartment método, o controlo ObjectDataSource recria a versão original da entidade para ti a partir dos valores no estado de vista, mas essa entidade recriada Department não existe no gestor de estados do objeto. Se chamar o DeleteObject método nesta entidade recriada, a chamada falharia porque o contexto do objeto não sabe se a entidade está sincronizada com a base de dados. No entanto, chamar o Attach método restabelece o mesmo rastreio entre a entidade recriada e os valores na base de dados que foi originalmente feito automaticamente quando a entidade foi lida numa instância anterior do contexto do objeto.
Há momentos em que não queres que o contexto do objeto acompanhe entidades no gestor de estado do objeto, e podes definir flags para impedir isso. Exemplos disto são mostrados em tutoriais posteriores desta série.
O Método SaveChanges
Esta classe simples de repositório ilustra princípios básicos de como realizar operações CRUD. Neste exemplo, o SaveChanges método é chamado imediatamente após cada atualização. Numa aplicação de produção, pode querer chamar o SaveChanges método a partir de um método separado para ter mais controlo sobre quando a base de dados é atualizada. (No final do próximo tutorial encontrará um link para um white paper que discute o padrão de unidade de trabalho, que é uma abordagem para coordenar atualizações relacionadas.) Note também que, no exemplo, o DeleteDepartment método não inclui código para lidar com conflitos de concorrência; o código para isso será adicionado num tutorial posterior desta série.
Recuperar os nomes dos instrutores para seleção durante a inserção
Os usuários devem ser capazes de selecionar um administrador a partir de uma lista de instrutores num menu suspenso ao criar novos departamentos. Portanto, adicione o seguinte código à SchoolRepository.cs para criar um método que recupere a lista de instrutores usando a vista que criou anteriormente:
public IEnumerable<InstructorName> GetInstructorNames()
{
return context.InstructorNames.OrderBy("it.FullName").ToList();
}
Criação de uma Página para Inserir Departamentos
Crie uma página DepartmentsAdd.aspx que utilize a página Site.Master e adicione a seguinte marcação no Content controlo chamada 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" />
Esta marcação cria dois ObjectDataSource controlos, um que insere novas Department entidades e outro que obtém nomes de instrutores para o DropDownList controlo que é usado para selecionar os administradores de departamento. A marcação cria um DetailsView controlo para a introdução de novos departamentos e especifica um gestor para o evento do ItemInserting controlo para que possa definir o valor da Administrator chave estrangeira. No final há um ValidationSummary controlo para mostrar mensagens de erro.
Abra DepartmentsAdd.aspx.cs e adicione a seguinte using declaraçã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 permite a funcionalidade de Dados Dinâmicos. O handler do controlo DropDownList do evento Init guarda uma referência ao controlo, e o handler do controlo DetailsView do evento Inserting usa essa referência para obter o valor PersonID do instrutor selecionado e atualizar a propriedade da chave estrangeira Administrator da entidade Department.
Abra a página, adicione informações para um novo departamento e depois clique no link Inserir .
Insira valores para outro novo departamento. Introduza um número superior a 1.000.000,00 no campo Orçamento e avance para o campo seguinte. Aparece um asterisco no campo e, se mantiveres o ponteiro do rato sobre ele, podes ver a mensagem de erro que introduziste nos metadados desse campo.
Clica em Inserir e vês a mensagem de erro apresentada pelo ValidationSummary controlo no final da página.
De seguida, fecha o navegador e abre a página Departments.aspx . Adicione a capacidade de eliminar à página Departments.aspx adicionando um DeleteMethod atributo ao ObjectDataSource controle e um DataKeyNames atributo ao GridView controle. As etiquetas de abertura para estes controlos vão agora assemelhar-se 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" >
Executar a página.
Apaga o departamento que adicionaste quando executaste a página DepartmentsAdd.aspx .
Adicionar 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 clicas em Atualizar na página Departments.aspx , o controlo ObjectDataSource cria duas Department entidades para passar ao UpdateDepartment método. Um contém os valores originais que foram armazenados no estado de visualização, e o outro contém os novos valores que foram introduzidos no GridView controlo. O código do UpdateDepartment método passa a Department entidade que tem os valores originais para o Attach método para estabelecer o rastreio entre a entidade e o que está na base de dados. Depois, 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 altera o valor da propriedade. O SaveChanges método atualiza então apenas as colunas alteradas na base de dados. (No entanto, se a função de atualização desta entidade fosse mapeada para um procedimento armazenado, toda a linha seria atualizada independentemente das colunas alteradas.)
Abra o ficheiro Departments.aspx, e adicione os seguintes atributos ao DepartmentsObjectDataSource controlo.
UpdateMethod="UpdateDepartment"ConflictDetection="CompareAllValues"
Isto faz com que valores antigos sejam armazenados no estado da vista para que possam ser comparados com os novos valores doUpdatemétodo.OldValuesParameterFormatString="orig{0}"
Isto informa o controlo que o nome do parâmetro de valores originais éorigDepartment.
A marcação para a etiqueta de abertura do ObjectDataSource controlo assemelha-se agora 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 controlo. Usará isto para definir o valor da Administrator propriedade com base na linha selecionada pelo utilizador numa lista suspensa. A GridView etiqueta de abertura assemelha-se agora ao seguinte exemplo:
<asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
DataSourceID="DepartmentsObjectDataSource" DataKeyNames="DepartmentID"
OnRowUpdating="DepartmentsGridView_RowUpdating">
Adicione um EditItemTemplate controlo para a coluna Administrator ao controlo GridView, imediatamente após o controlo ItemTemplate para essa 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>
Este EditItemTemplate controlo é semelhante ao InsertItemTemplate controlo na DepartmentsAdd.aspx. A diferença é que o valor inicial do controlo é definido usando o SelectedValue atributo.
Antes do GridView controlo, adiciona um ValidationSummary controlo como fizeste 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 referenciar o DropDownList controlo.
private DropDownList administratorsDropDownList;
Depois adicione manipuladores para o evento do controlo DropDownList e para o evento do controlo GridView.
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 "handler" do evento Init guarda uma referência ao controlo DropDownList num campo da classe. O handler do RowUpdating evento usa a referência para obter o valor inserido pelo utilizador e aplicá-lo à Administrator propriedade da Department entidade.
Usa a página DepartmentsAdd.aspx para adicionar um novo departamento, depois executa a página Departments.aspx e clica em Editar na linha que adicionaste.
Observação
Não poderá editar linhas que não adicionou (ou seja, que já estavam na base de dados), devido a dados inválidos na base de dados; Os administradores das linhas criadas com a base de dados são estudantes. Se tentar editar um deles, aparecerá uma página de erro que reporta um erro como 'InstructorsDropDownList' has a SelectedValue which is invalid because it does not exist in the list of items.
Se introduzir um valor inválido no Orçamento e depois clicar em Atualizar, verá o mesmo asterisco e mensagem de erro que viu na página Departments.aspx .
Altere o valor de um campo ou selecione outro administrador e clique em Atualizar. A alteração é exibida.
Com isto, completa-se a introdução ao uso do control ObjectDataSource para CRUD, operações básicas de criar, ler, atualizar e eliminar com o Entity Framework. Construiu uma aplicação simples de n-camadas, mas a camada de lógica de negócio ainda está fortemente acoplada à camada de acesso a dados, o que complica os testes unitários automatizados. No tutorial seguinte verá como implementar o padrão de repositório para facilitar os testes unitários.