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 aObjectContext
API. Para obter informações sobre como usar o controle EntityDataSource com aDbContext
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.
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.
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.
Na caixa Relações de Chave Estrangeira , clique em Adicionar e, em seguida, clique nas reticências para Especificação de Tabelas e Colunas.
Na caixa de diálogo Tabelas e Colunas , defina a tabela e o campo de chave primária como Person
e PersonID
e 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
.)
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.
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.
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.
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
.
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.
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".
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 Decimal
e 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 .
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 SchoolEntities
e uma instância dela é sempre chamada context
de . 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 .
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.
Clique em Inserir e você verá a mensagem de erro exibida pelo ValidationSummary
controle na parte inferior da página.
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.
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 noUpdate
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.
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.
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.