Cenários avançados do Entity Framework para um aplicativo Web MVC (10 de 10)

por Tom Dykstra

O aplicativo Web de exemplo da Contoso University demonstra como criar ASP.NET aplicativos MVC 4 usando o Entity Framework 5 Code First e o Visual Studio 2012. Para obter informações sobre a série de tutoriais, consulte o primeiro tutorial da série.

Observação

Se você tiver um problema, não poderá resolve, baixe o capítulo concluído e tente reproduzir o problema. Geralmente, você pode encontrar a solução para o problema comparando seu código com o código concluído. Para obter alguns erros comuns e como resolvê-los, consulte Erros e soluções alternativas.

No tutorial anterior, você implementou o repositório e a unidade de padrões de trabalho. Este tutorial abrange os seguintes tópicos:

  • Executando consultas SQL brutas.
  • Executando consultas sem acompanhamento.
  • Examinando consultas enviadas para o banco de dados.
  • Trabalhando com classes proxy.
  • Desabilitando a detecção automática de alterações.
  • Desabilitando a validação ao salvar alterações.
  • Erros e solução alternativa

Para a maioria desses tópicos, você trabalhará com páginas que já criou. Para usar o SQL bruto para fazer atualizações em massa, você criará uma nova página que atualiza o número de créditos de todos os cursos no banco de dados:

Captura de tela mostrando a página inicial Atualizar Créditos do Curso. O número 2 é inserido no campo de texto.

E para usar uma consulta sem acompanhamento, você adicionará uma nova lógica de validação à página Edição do Departamento:

Captura de tela que mostra a página Editar departamento da Contoso University com uma mensagem de erro de administrador duplicada.

Executando consultas SQL brutas

A Primeira API do Entity Framework Code inclui métodos que permitem que você passe comandos SQL diretamente para o banco de dados. Você tem as seguintes opções:

  • Use o método DbSet.SqlQuery para consultas que retornam tipos de entidade. Os objetos retornados devem ser do tipo esperado pelo DbSet objeto e são rastreados automaticamente pelo contexto do banco de dados, a menos que você desative o rastreamento. (Consulte a seção a seguir sobre o AsNoTracking método .)
  • Use o Database.SqlQuery método para consultas que retornam tipos que não são entidades. Os dados retornados não são controlados pelo contexto de banco de dados, mesmo se esse método é usado para recuperar tipos de entidade.
  • Use Database.ExecuteSqlCommand para comandos que não são de consulta.

Uma das vantagens de usar o Entity Framework é que ele evita vincular o código de forma muito próxima a um método específico de armazenamento de dados. Ele faz isso pela geração de consultas SQL e comandos para você, que também libera você da necessidade de escrevê-los. Mas há cenários excepcionais quando você precisa executar consultas SQL específicas que você criou manualmente e esses métodos possibilitam que você lide com essas exceções.

Como é sempre verdadeiro quando você executa comandos SQL em um aplicativo Web, é necessário tomar precauções para proteger o site contra ataques de injeção de SQL. Uma maneira de fazer isso é usar consultas parametrizadas para garantir que as cadeias de caracteres enviadas por uma página da Web não possam ser interpretadas como comandos SQL. Neste tutorial, você usará consultas parametrizadas ao integrar a entrada do usuário a uma consulta.

Chamando uma consulta que retorna entidades

Suponha que você queira que a GenericRepository classe forneça flexibilidade adicional de filtragem e classificação sem exigir que você crie uma classe derivada com métodos adicionais. Uma maneira de fazer isso seria adicionar um método que aceite uma consulta SQL. Em seguida, você pode especificar qualquer tipo de filtragem ou classificação desejada no controlador, como uma Where cláusula que depende de uma junção ou uma subconsulta. Nesta seção, você verá como implementar esse método.

Crie o GetWithRawSql método adicionando o seguinte código a GenericRepository.cs:

public virtual IEnumerable<TEntity> GetWithRawSql(string query, params object[] parameters)
{
    return dbSet.SqlQuery(query, parameters).ToList();
}

Em CourseController.cs, chame o novo método do Details método , conforme mostrado no exemplo a seguir:

public ActionResult Details(int id)
{
    var query = "SELECT * FROM Course WHERE CourseID = @p0";
    return View(unitOfWork.CourseRepository.GetWithRawSql(query, id).Single());
}

Nesse caso, você poderia ter usado o GetByID método , mas está usando o GetWithRawSql método para verificar se o GetWithRawSQL método funciona.

Execute a página Detalhes para verificar se a consulta de seleção funciona (selecione a guia Curso e, em seguida, Detalhes de um curso).

Captura de tela que mostra a página Detalhes da Contoso University.

Chamando uma consulta que retorna outros tipos de objetos

Anteriormente, você criou uma grade de estatísticas de alunos para a página Sobre que mostrava o número de alunos para cada data de registro. O código que faz isso em HomeController.cs usa LINQ:

var data = from student in db.Students
           group student by student.EnrollmentDate into dateGroup
           select new EnrollmentDateGroup()
           {
               EnrollmentDate = dateGroup.Key,
               StudentCount = dateGroup.Count()
           };

Suponha que você queira escrever o código que recupera esses dados diretamente no SQL em vez de usar LINQ. Para fazer isso, você precisa executar uma consulta que retorna algo diferente de objetos de entidade, o que significa que você precisa usar o Database.SqlQuery método .

Em HomeController.cs, substitua a instrução LINQ no About método pelo seguinte código:

var query = "SELECT EnrollmentDate, COUNT(*) AS StudentCount "
    + "FROM Person "
    + "WHERE EnrollmentDate IS NOT NULL "
    + "GROUP BY EnrollmentDate";
var data = db.Database.SqlQuery<EnrollmentDateGroup>(query);

Execute a página Sobre. Ela exibe os mesmos dados que antes.

Captura de tela que mostra a página Sobre a Contoso University.

Chamando uma consulta de atualização

Suponha que os administradores da Contoso University queiram realizar alterações em massa no banco de dados, como alterar o número de créditos para cada curso. Se a universidade tiver uma grande quantidade de cursos, poderá ser ineficiente recuperá-los como entidades e alterá-los individualmente. Nesta seção, você implementará uma página da Web que permite que o usuário especifique um fator pelo qual alterar o número de créditos para todos os cursos e fará a alteração executando uma instrução SQL UPDATE . A página da Web será semelhante à seguinte ilustração:

Captura de tela que mostra a página inicial Atualizar Créditos do Curso. O número 2 é inserido no campo de texto.

No tutorial anterior, você usou o repositório genérico para ler e atualizar Course entidades no Course controlador. Para essa operação de atualização em massa, você precisa criar um novo método de repositório que não esteja no repositório genérico. Para fazer isso, você criará uma classe dedicada CourseRepository derivada da GenericRepository classe .

Na pasta DAL , crie CourseRepository.cs e substitua o código existente pelo seguinte código:

using System;
using ContosoUniversity.Models;

namespace ContosoUniversity.DAL
{
    public class CourseRepository : GenericRepository<Course>
    {
        public CourseRepository(SchoolContext context)
            : base(context)
        {
        }

        public int UpdateCourseCredits(int multiplier)
        {
            return context.Database.ExecuteSqlCommand("UPDATE Course SET Credits = Credits * {0}", multiplier);
        }

    }
}

Em UnitOfWork.cs, altere o Course tipo de repositório de GenericRepository<Course> para CourseRepository:

private CourseRepository courseRepository;
public CourseRepository CourseRepository
{
    get
    {

        if (this.courseRepository == null)
        {
            this.courseRepository = new CourseRepository(context);
        }
        return courseRepository;
    }
}

Em CourseController.cs, adicione um UpdateCourseCredits método:

public ActionResult UpdateCourseCredits(int? multiplier)
{
    if (multiplier != null)
    {
        ViewBag.RowsAffected = unitOfWork.CourseRepository.UpdateCourseCredits(multiplier.Value);
    }
    return View();
}

Esse método será usado para e HttpGetHttpPost. Quando o HttpGetUpdateCourseCredits método for executado, a multiplier variável será nula e a exibição exibirá uma caixa de texto vazia e um botão enviar, conforme mostrado na ilustração anterior.

Quando o botão Atualizar for clicado e o HttpPost método for executado, multiplier o valor será inserido na caixa de texto. Em seguida, o código chama o método de repositório UpdateCourseCredits , que retorna o número de linhas afetadas e esse valor é armazenado no ViewBag objeto . Quando a exibição recebe o número de linhas afetadas no ViewBag objeto, ela exibe esse número em vez da caixa de texto e o botão enviar, conforme mostrado na ilustração a seguir:

Captura de tela que mostra a página Créditos do Curso de Atualização da Contoso University afetada.

Crie uma exibição na pasta Views\Course para a página Atualizar Créditos do Curso:

Captura de tela que mostra a caixa de diálogo Adicionar Exibição. Atualizar Créditos do Curso é inserido no campo Texto do nome de exibição.

Em Views\Course\UpdateCourseCredits.cshtml, substitua o código do modelo pelo seguinte código:

@model ContosoUniversity.Models.Course

@{
    ViewBag.Title = "UpdateCourseCredits";
}

<h2>Update Course Credits</h2>

@if (ViewBag.RowsAffected == null)
{
    using (Html.BeginForm())
    {
        <p>
            Enter a number to multiply every course's credits by: @Html.TextBox("multiplier")
        </p>
        <p>
            <input type="submit" value="Update" />
        </p>
    }
}
@if (ViewBag.RowsAffected != null)
{
    <p>
        Number of rows updated: @ViewBag.RowsAffected
    </p>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>

Execute o método UpdateCourseCredits selecionando a guia Cursos, adicionando, em seguida, "/UpdateCourseCredits" ao final da URL na barra de endereços do navegador (por exemplo: http://localhost:50205/Course/UpdateCourseCredits). Insira um número na caixa de texto:

Captura de tela mostrando a página inicial Atualizar Créditos do Curso com o número 2 inserido no campo de texto.

Clique em Atualizar. O número de linhas afetadas é exibido:

Captura de tela que mostra a página Atualizar Créditos do Curso com o número de linhas atualizadas.

Clique em Voltar para a Lista para ver a lista de cursos com o número revisado de créditos.

Captura de tela que mostra a página Índice de Cursos. Uma lista de cursos é mostrada com o número revisado de créditos.

Para obter mais informações sobre consultas SQL brutas, consulte Consultas SQL brutas no blog da equipe do Entity Framework.

Consultas sem acompanhamento

Quando um contexto de banco de dados recupera linhas de banco de dados e cria objetos de entidade que as representam, por padrão, ele controla se as entidades na memória estão em sincronia com o que está no banco de dados. Os dados em memória atuam como um cache e são usados quando uma entidade é atualizada. Esse cache costuma ser desnecessário em um aplicativo Web porque as instâncias de contexto são normalmente de curta duração (uma nova é criada e descartada para cada solicitação) e o contexto que lê uma entidade normalmente é descartado antes que essa entidade seja usada novamente.

Você pode especificar se o contexto rastreia objetos de entidade para uma consulta usando o AsNoTracking método . Os cenários típicos em que talvez você deseje fazer isso incluem os seguintes:

  • A consulta recupera um volume tão grande de dados que desativar o controle pode melhorar visivelmente o desempenho.
  • Você deseja anexar uma entidade para atualizá-la, mas recuperou anteriormente a mesma entidade para uma finalidade diferente. Como a entidade já está sendo controlada pelo contexto de banco de dados, não é possível anexar a entidade que você deseja alterar. Uma maneira de impedir que isso aconteça é usar a opção AsNoTracking com a consulta anterior.

Nesta seção, você implementará a lógica de negócios que ilustra o segundo desses cenários. Especificamente, você imporá uma regra de negócios que diz que um instrutor não pode ser o administrador de mais de um departamento.

Em DepartmentController.cs, adicione um novo método que você pode chamar dos Edit métodos e Create para garantir que nenhum dos dois departamentos tenha o mesmo administrador:

private void ValidateOneAdministratorAssignmentPerInstructor(Department department)
{
    if (department.PersonID != null)
    {
        var duplicateDepartment = db.Departments
            .Include("Administrator")
            .Where(d => d.PersonID == department.PersonID)
            .FirstOrDefault();
        if (duplicateDepartment != null && duplicateDepartment.DepartmentID != department.DepartmentID)
        {
            var errorMessage = String.Format(
                "Instructor {0} {1} is already administrator of the {2} department.",
                duplicateDepartment.Administrator.FirstMidName,
                duplicateDepartment.Administrator.LastName,
                duplicateDepartment.Name);
            ModelState.AddModelError(string.Empty, errorMessage);
        }
    }
}

Adicione código no try bloco do HttpPostEdit método para chamar esse novo método se não houver erros de validação. O try bloco agora se parece com o seguinte exemplo:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(
   [Bind(Include = "DepartmentID, Name, Budget, StartDate, RowVersion, PersonID")]
    Department department)
{
   try
   {
      if (ModelState.IsValid)
      {
         ValidateOneAdministratorAssignmentPerInstructor(department);
      }

      if (ModelState.IsValid)
      {
         db.Entry(department).State = EntityState.Modified;
         db.SaveChanges();
         return RedirectToAction("Index");
      }
   }
   catch (DbUpdateConcurrencyException ex)
   {
      var entry = ex.Entries.Single();
      var clientValues = (Department)entry.Entity;

Execute a página Editar Departamento e tente alterar o administrador de um departamento para um instrutor que já é o administrador de um departamento diferente. Você recebe a mensagem de erro esperada:

Captura de tela mostrando a página Editar Departamento com uma mensagem de erro de administrador duplicada.

Agora, execute a página Editar Departamento novamente e, desta vez, altere o valor do Orçamento . Ao clicar em Salvar, você verá uma página de erro:

Captura de tela que mostra a página Editar Departamento com uma mensagem de erro do gerenciador de estado do objeto.

A mensagem de erro de exceção é "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key." Isso aconteceu devido à seguinte sequência de eventos:

  • O Edit método chama o ValidateOneAdministratorAssignmentPerInstructor método , que recupera todos os departamentos que têm Kim Abercrombie como administrador. Isso faz com que o departamento de inglês seja lido. Como esse é o departamento que está sendo editado, nenhum erro é relatado. Como resultado dessa operação de leitura, no entanto, a entidade de departamento em inglês que foi lida do banco de dados agora está sendo rastreada pelo contexto do banco de dados.
  • O Edit método tenta definir o Modified sinalizador na entidade de departamento em inglês criada pelo associador de modelo MVC, mas isso falha porque o contexto já está acompanhando uma entidade para o departamento de inglês.

Uma solução para esse problema é impedir que o contexto acompanhe as entidades de departamento na memória recuperadas pela consulta de validação. Não há desvantagem em fazer isso, pois você não atualizará essa entidade ou a lerá novamente de uma maneira que se beneficiaria de ela ser armazenada em cache na memória.

Em DepartmentController.cs, no ValidateOneAdministratorAssignmentPerInstructor método , não especifique nenhum acompanhamento, conforme mostrado no seguinte:

var duplicateDepartment = db.Departments
   .Include("Administrator")
   .Where(d => d.PersonID == department.PersonID)
   .AsNoTracking()
   .FirstOrDefault();

Repita sua tentativa de editar a quantidade de orçamento de um departamento. Desta vez, a operação é bem-sucedida e o site retorna conforme o esperado para a página Índice de Departamentos, mostrando o valor do orçamento revisado.

Examinando consultas enviadas para o banco de dados

Às vezes, é útil poder ver as consultas SQL reais que são enviadas ao banco de dados. Para fazer isso, você pode examinar uma variável de consulta no depurador ou chamar o método da ToString consulta. Para experimentar isso, você examinará uma consulta simples e examinará o que acontece com ela à medida que adiciona opções como carregamento adiantado, filtragem e classificação.

Em Controllers/CourseController, substitua o Index método pelo seguinte código:

public ViewResult Index()
{
    var courses = unitOfWork.CourseRepository.Get();
    return View(courses.ToList());
}

Agora defina um ponto de interrupção em GenericRepository.cs nas return query.ToList(); instruções e return orderBy(query).ToList(); do Get método . Execute o projeto no modo de depuração e selecione a página Índice do Curso. Quando o código atingir o ponto de interrupção, examine a query variável . Você verá a consulta enviada para SQL Server. É uma instrução simples Select :

{SELECT 
[Extent1].[CourseID] AS [CourseID], 
[Extent1].[Title] AS [Title], 
[Extent1].[Credits] AS [Credits], 
[Extent1].[DepartmentID] AS [DepartmentID]
FROM [Course] AS [Extent1]}

Captura de tela que mostra a guia Repositório Genérico do aplicativo Web de exemplo. A variável de consulta está selecionada.

As consultas podem ser muito longas para serem exibidas nas janelas de depuração no Visual Studio. Para ver a consulta inteira, você pode copiar o valor da variável e colá-lo em um editor de texto:

Captura de tela que mostra o valor da variável com um menu suspenso exibido quando ele é selecionado. A opção Copiar Valor está realçada.

Agora você adicionará uma lista suspensa à página Índice do Curso para que os usuários possam filtrar um departamento específico. Você classificará os cursos por título e especificará o carregamento adiantado para a Department propriedade de navegação. Em CourseController.cs, substitua o Index método pelo seguinte código:

public ActionResult Index(int? SelectedDepartment)
{
    var departments = unitOfWork.DepartmentRepository.Get(
        orderBy: q => q.OrderBy(d => d.Name));
    ViewBag.SelectedDepartment = new SelectList(departments, "DepartmentID", "Name", SelectedDepartment);

    int departmentID = SelectedDepartment.GetValueOrDefault(); 
    return View(unitOfWork.CourseRepository.Get(
        filter: d => !SelectedDepartment.HasValue || d.DepartmentID == departmentID,
        orderBy: q => q.OrderBy(d => d.CourseID),
        includeProperties: "Department"));
}

O método recebe o valor selecionado da lista suspensa no SelectedDepartment parâmetro . Se nada for selecionado, esse parâmetro será nulo.

Uma SelectList coleção que contém todos os departamentos é passada para a exibição da lista suspensa. Os parâmetros passados para o SelectList construtor especificam o nome do campo de valor, o nome do campo de texto e o item selecionado.

Para o Get método do Course repositório, o código especifica uma expressão de filtro, uma ordem de classificação e um carregamento adiantado para a Department propriedade de navegação. A expressão de filtro sempre retornará true se nada estiver selecionado na lista suspensa (ou seja, SelectedDepartment é nulo).

Em Views\Course\Index.cshtml, imediatamente antes da marca de abertura table , adicione o seguinte código para criar a lista suspensa e um botão enviar:

@using (Html.BeginForm())
{
    <p>Select Department: @Html.DropDownList("SelectedDepartment","All")   
    <input type="submit" value="Filter" /></p>
}

Com os pontos de interrupção ainda definidos na GenericRepository classe , execute a página Índice do Curso. Continue nas duas primeiras vezes em que o código atinge um ponto de interrupção, para que a página seja exibida no navegador. Selecione um departamento na lista suspensa e clique em Filtrar:

Captura de tela que mostra a página Índice do Curso com o Departamento de Economia selecionado.

Desta vez, o primeiro ponto de interrupção será para a consulta de departamentos para a lista suspensa. Ignore isso e exiba a query variável na próxima vez que o código atingir o ponto de interrupção para ver a aparência da Course consulta agora. Você verá algo semelhante ao seguinte:

{SELECT 
[Extent1].[CourseID] AS [CourseID], 
[Extent1].[Title] AS [Title], 
[Extent1].[Credits] AS [Credits], 
[Extent1].[DepartmentID] AS [DepartmentID], 
[Extent2].[DepartmentID] AS [DepartmentID1], 
[Extent2].[Name] AS [Name], 
[Extent2].[Budget] AS [Budget], 
[Extent2].[StartDate] AS [StartDate], 
[Extent2].[PersonID] AS [PersonID], 
[Extent2].[Timestamp] AS [Timestamp]
FROM  [Course] AS [Extent1]
INNER JOIN [Department] AS [Extent2] ON [Extent1].[DepartmentID] = [Extent2].[DepartmentID]
WHERE (@p__linq__0 IS NULL) OR ([Extent1].[DepartmentID] = @p__linq__1)}

Você pode ver que a consulta agora é uma JOIN consulta que carrega Department dados junto com os Course dados e que ela inclui uma WHERE cláusula .

Trabalhando com classes proxy

Quando o Entity Framework cria instâncias de entidade (por exemplo, quando você executa uma consulta), ele geralmente as cria como instâncias de um tipo derivado gerado dinamicamente que atua como um proxy para a entidade. Esse proxy substitui algumas propriedades virtuais da entidade para inserir ganchos para executar ações automaticamente quando a propriedade é acessada. Por exemplo, esse mecanismo é usado para dar suporte ao carregamento lento de relações.

Na maioria das vezes, você não precisa estar ciente desse uso de proxies, mas há exceções:

  • Em alguns cenários, talvez você queira impedir que o Entity Framework crie instâncias de proxy. Por exemplo, serializar instâncias não proxy pode ser mais eficiente do que serializar instâncias de proxy.
  • Ao criar uma instância de uma classe de entidade usando o new operador , você não obtém uma instância de proxy. Isso significa que você não obtém funcionalidades como carregamento lento e controle automático de alterações. Normalmente, está tudo bem; você geralmente não precisa de carregamento lento, pois está criando uma nova entidade que não está no banco de dados e, geralmente, não precisa de controle de alterações se estiver marcando explicitamente a entidade como Added. No entanto, se você precisar de carregamento lento e precisar de controle de alterações, poderá criar novas instâncias de entidade com proxies usando o Create método da DbSet classe .
  • Talvez você queira obter um tipo de entidade real de um tipo de proxy. Você pode usar o GetObjectType método da ObjectContext classe para obter o tipo de entidade real de uma instância de tipo de proxy.

Para obter mais informações, consulte Trabalhando com proxies no blog da equipe do Entity Framework.

Desabilitando a detecção automática de alterações

O Entity Framework determina como uma entidade foi alterada (e, portanto, quais atualizações precisam ser enviadas ao banco de dados), comparando os valores atuais de uma entidade com os valores originais. Os valores originais são armazenados quando a entidade foi consultada ou anexada. Alguns dos métodos que causam a detecção automática de alterações são os seguintes:

  • DbSet.Find
  • DbSet.Local
  • DbSet.Remove
  • DbSet.Add
  • DbSet.Attach
  • DbContext.SaveChanges
  • DbContext.GetValidationErrors
  • DbContext.Entry
  • DbChangeTracker.Entries

Se você estiver acompanhando um grande número de entidades e chamar um desses métodos muitas vezes em um loop, poderá obter melhorias significativas de desempenho desativando temporariamente a detecção automática de alterações usando a propriedade AutoDetectChangesEnabled . Para obter mais informações, confira Detectar alterações automaticamente.

Desabilitando a validação ao salvar alterações

Quando você chama o SaveChanges método , por padrão, o Entity Framework valida os dados em todas as propriedades de todas as entidades alteradas antes de atualizar o banco de dados. Se você atualizou um grande número de entidades e já validou os dados, esse trabalho é desnecessário e você pode fazer com que o processo de salvar as alterações leve menos tempo desativando temporariamente a validação. Você pode fazer isso usando a propriedade ValidateOnSaveEnabled . Para obter mais informações, confira Validação.

Resumo

Isso conclui esta série de tutoriais sobre como usar o Entity Framework em um aplicativo ASP.NET MVC. Os links para outros recursos do Entity Framework podem ser encontrados no Mapa de Conteúdo de Acesso a Dados do ASP.NET.

Para obter mais informações sobre como implantar seu aplicativo Web depois de compilá-lo, consulte ASP.NET Mapa de Conteúdo de Implantação no Biblioteca MSDN.

Para obter informações sobre outros tópicos relacionados ao MVC, como autenticação e autorização, consulte os Recursos Recomendados do MVC.

Agradecimentos

  • Tom Dykstra escreveu a versão original deste tutorial e é um gravador de programação sênior na Equipe de Conteúdo da Plataforma Web e Ferramentas da Microsoft.
  • Rick Anderson ( twitter @RickAndMSFT) foi co-autor deste tutorial e fez a maior parte do trabalho atualizando-o para EF 5 e MVC 4. Rick é um gravador de programação sênior da Microsoft com foco no Azure e no MVC.
  • Rowan Miller e outros membros da equipe do Entity Framework ajudaram nas revisões de código e ajudaram a depurar muitos problemas com migrações que surgiram enquanto atualizamos o tutorial para o EF 5.

VB

Quando o tutorial foi originalmente produzido, fornecemos versões C# e VB do projeto de download concluído. Com essa atualização, estamos fornecendo um projeto para download em C# para cada capítulo para facilitar a introdução em qualquer lugar da série, mas devido a limitações de tempo e outras prioridades, não fizemos isso para o VB. Se você criar um projeto de VB usando esses tutoriais e estiver disposto a compartilhá-lo com outras pessoas, informe-nos.

Erros e soluções alternativas

Não é possível criar/copiar sombra

Mensagem de erro:

Não é possível criar/copiar sombra 'DotNetOpenAuth.OpenId' quando esse arquivo já existe.

Solução:

Aguarde alguns segundos e atualize a página.

Update-Database não reconhecido

Mensagem de erro:

O termo 'Update-Database' não é reconhecido como o nome de um cmdlet, função, arquivo de script ou programa operável. Verifique a ortografia do nome ou se um caminho foi incluído, verifique se ele está correto e tente novamente.(Do Update-Database comando no PMC.)

Solução:

Saia do Visual Studio. Reabra o projeto e tente novamente.

Falha na validação

Mensagem de erro:

Falha na validação de uma ou mais entidades. Consulte a propriedade 'EntityValidationErrors' para obter mais detalhes. (Do Update-Database comando no PMC.)

Solução:

Uma das causas desse problema são os erros de validação quando o Seed método é executado. Consulte Propagação e depuração de DBs do EF (Entity Framework) para obter dicas sobre como depurar o Seed método.

Erro HTTP 500.19

Mensagem de erro:

Erro HTTP 500.19 – Erro Interno do Servidor
A página solicitada não pode ser acessada porque os dados de configuração relacionados para a página são inválidos.

Solução:

Uma maneira de obter esse erro é por ter várias cópias da solução, cada uma delas usando o mesmo número de porta. Normalmente, você pode resolver esse problema saindo de todas as instâncias do Visual Studio e reiniciando o projeto em que está trabalhando. Se isso não funcionar, tente alterar o número da porta. Clique com o botão direito do mouse no arquivo de projeto e clique em propriedades. Selecione a guia Web e altere o número da porta na caixa de texto URL do Projeto .

Erro ao localizar a instância do SQL Server

Mensagem de erro:

Ocorreu um erro relacionado à rede ou específico da instância ao estabelecer uma conexão com o SQL Server. O servidor não foi encontrado ou não estava acessível. Verifique se o nome de instância está correto e se o SQL Server está configurado para permitir conexões remotas. (provedor: Adaptadores de Rede do SQL, erro: 26 – Erro ao Localizar Servidor/Instância Especificada)

Solução:

Verifique a cadeia de conexão. Se você tiver excluído manualmente o banco de dados, altere o nome do banco de dados na cadeia de caracteres de construção.