Este artigo foi traduzido por máquina.

O programador

Começando com Carvalho: interação com o banco de dados

Ted Neward

Ted NewardBem-vindo de volta. Eu tenho andado pelos primeiros passos carvalho, uma abordagem dinâmica para desenvolvimento Web que incorpora idéias do mundo Ruby e node. js, ainda mantendo a linguagem c#, a MVC ASP.NET framework e o resto do Microsoft .NET Framework você desfrutar. A essência do carvalho é "feedback rápido, desenvolvimento sem atrito e cerimônia de baixa," nas palavras do criador do projeto Amir Rajan.

Quando eu sai fora, carvalho estava reclamando que não poderia se conectar a um dados­base. Nesta coluna, mostrarei como fazer o seguinte: conectar a uma instância de SQL Server carvalho, adicionar outro tipo relacionado ao sistema (comentários sobre as entradas de blog), construir o banco de dados em carvalho, relacionar os dois tipos juntos usando carvalho e ver como o carvalho e o seu sistema de compilação lidar com interação de banco de dados.

Quando saímos de nosso herói...

A última vez que carvalho estava correndo, ele apareceu uma mensagem de erro e o texto explicativo útil, um trecho do que é mostrado no Figura 1. (Como um lembrete, se você estava acompanhando na última coluna — no msdn.microsoft.com/magazine/dn532208— você precisará reiniciar o servidor e o ajudante runner.) Antes de eu ir muito mais longe, tome nota de como carvalho não é apenas exibir um rastreamento de pilha de exceção e deixando-me a descobrir ou que poderia estar causando o problema de pesquisa. Na verdade está a tentar diagnosticar o problema e sugerir possíveis soluções: Neste caso, "atualizar a configuração de web com sua instância de servidor".

The Oak Project Help Window After an Error Is Thrown
Figura 1 a janela de ajuda do projeto Carvalho após um erro é descartada.

Com certeza, uma rápida olhada no arquivo Web. config que aprendi com o "warm up" gem mostra que um simples espaço reservado está lá, apontando para uma fonte de dados "(local)", que pode funcionar para um monte de configurações, dependendo de como a instância local do SQL Server é configurada. Dito isto, é tão trivial para cair em uma seqüência de caracteres de conexão LocalDB, como está a cair em uma seqüência de caracteres SQL Server remota (ou um Windows Azure SQL Database, se é desejável para manter dados na nuvem). Eu gosto de usar LocalDB, que normalmente é apenas uma seqüência de conexão de "Server = \v11.0;Integrated (localdb) segurança = true" para explorações como este, porque é pequena e limpa facilmente. Qualquer conexão de SQL que você usar, basta ligá-lo na configuração do < > / < connectionStrings > < adicionar > elemento e atualizar a página. Na verdade, o carvalho tem uma tarefa do rake (a ferramenta de compilação Ruby) para fazer isso em toda a base de código para você: "rake update_db_server [\\v11.0 (localdb)]" (a barra invertida deve ser antecedidas).

Uh... Houston?

Infelizmente, quando executar novamente, carvalho age como se nada tivesse mudado. Também, infelizmente, isso é inteiramente verdade. Ajudante não está prestando atenção para alterações em Web. config, ao que parece, somente os arquivos de origem. Considerando como raramente você alterar o arquivo Web. config, este não é o fim do mundo, mas ele coloca uma pequena cãibra no que caso contrário é um processo muito fácil. Para obter o ajudante de reconhecer que o projeto foi alterado, só precisa acionar um save de algum arquivo importante, como HomeController.cs ou algum outro arquivo de fonte. Você pode fazer isso de duas maneiras: mudar para esse arquivo, escolher um lugar aleatório no arquivo, bater a barra de espaço e excluir esse espaço (que convence Visual Studio que o arquivo é "sujo" e as forças de defesa) ou apenas manualmente pontapé ancinho em um prompt de comando.

Depois disso, uma atualização do navegador traz um erro diferente ("nome de objeto inválido 'Blogs'"), e está em quadrado no mundo das migrações de banco de dados.

Dados, dados, dados!

Como seus predecessores conceituais, carvalho quer gerenciar bancos de dados e esquema de banco de dados para você, assim que o banco de dados pode permanecer mais ou menos "escondido" da sua vista. Neste caso, carvalho não quer apenas "automagicamente" criar um banco de dados fora do ar, porque você provavelmente vai ter algumas opiniões sobre como a esquema deve ser — e mesmo se não o fizer, os administradores de banco de dados costumam fazer. (Quem ganha essa batalha, ou quem deve ganhar essa batalha, é uma discussão melhor sobre cervejas — de preferência, muito tempo depois que o projeto foi enviado.)

Em carvalho, preparação do banco de dados é feito usando um controlador específico, o SeedController, encontrado aninhadas ao lado o HomeController em SeedController.cs. Esse arquivo contém uma definição para o SeedController já, mas — mais importante para seus propósitos — também contém uma classe de esquema, o que facilitará as etapas necessárias para construir a esquema e, opcionalmente, colocar alguns dados de exemplo no lugar. Tenha em mente, a propósito, que a convenção padrão de carvalho é que os objetos são armazenados em uma tabela de banco de dados de nome pluralized, então Blog objetos serão armazenados em uma tabela denominada Blogues. Figura 2 mostra a classe de esquema.

Figura 2 o esquema de classe

public class Schema
{
  // This is the method you'll want to alter
  public IEnumerable<Func<dynamic>> Scripts()
  {
    // Replace all content inside of the Scripts() method with this line
    yield return CreateBlogsTable; // Return just the pointer to the function
  }
  public string CreateBlogsTable() // Here is the function definition
  {
    // This is an example, your table name may be different
    // For more information on schema generation check out the Oak wiki
    return Seed.CreateTable("Blogs",
      Seed.Id(),
      new { Name = "nvarchar(255)" },
      new { Body = "nvarchar(max)" }
    );
  }
  public void SampleEntries()
  {
  }
  public Seed Seed { get; set; }
  public Schema(Seed seed) { Seed = seed; }
}

Observe o uso estranho de "rendimento Retorno" no método de Scripts. Para aqueles de vocês que nunca consegui entender o que isso fez no c# 2.0, "yield return" cria um fluxo de"anônimo" de objetos e as mãos de volta um IEnumerable < T > apontando para esse fluxo. Neste caso, é um fluxo de funções, e neste cenário específico, é um fluxo de uma função (CreateBlogsTable). Em essência, você está configurando um fluxo de ações (Func < dinâmica > instâncias) que representa como criar banco de dados e carvalho levará cada ação ou função devolveu a partir deste fluxo e executá-lo. Desta forma, quando uma nova alteração ao esquema entra em jogo, essa mudança pode ser capturada em uma nova função ("CreateComments­tabela," se preferir) e simplesmente adicionado à lista. Se desejar, você pode "versão" do esquema de banco de dados chamando a função primeira Version1, versão2 o segundo e assim por diante. Carvalho wiki tem mais informações sobre as migrações de banco de dados no bit.ly/1bgods5.

(Para aqueles que me lembro de minha série "Com vários paradigmas .NET", que começa no msdn.microsoft.com/magazine/ff955611, sim, esta é uma maneira muito funcional orientada de abordar este problema.)

A função Seed.Id cria a coluna de chave primária canônica: um valor de incremento automático inteiro marcado como uma chave primária. O wiki de Carvalho (bit.ly/1iKfcIb) contém uma referência na criação de um banco de dados usando a classe de sementes para fazê-lo, mas há sempre o fallback ad hoc do SQL direto a fazer se desejar:

public IEnumerable<string> AdHocChange()
{
  var reader = "select * from SampleTable".ExecuteReader();
  while (reader.Read())
  {
    // Do stuff here like yield return strings
  }
    var name = "select top 1 name from sysobjects"
      .ExecuteScalar() as string;
    yield return "drop table SampleTable";
    yield return "drop table AnotherSampleTable";
  }

Adicione o método AdHocChange como um outro método de "rendimento Retorno" para chamar, e carvalho alegremente realizará estes comandos também. (Note que se você perder a noção do que link wiki, carvalho inclui-lo na mensagem de erro/ajuda exibe).

Não posso fazer tijolos sem barro!

A propósito, se o banco de dados precisa de alguns dados de semente, também é parte da responsabilidade da SeedController, e cai novamente para a classe de esquema para fazê-lo, desta vez através do método SampleEntries. Porque este sistema não tem quaisquer dados de verdadeira semente que precisa ser aqui, vou deixar isso em paz.

Uma vez que o SeedController é compilado, ajudante será reimplantar o projeto, mas como a maioria dos controladores não são ativado até sucesso com uma solicitação HTTP. Se você não olhar sobre o SeedController, dê uma olhada rápida. Exporta quatro pontos de extremidade do POST: PurgeDB, exportações, todo e SampleEntries. Enquanto você poderia bater os pontos de extremidade se, este é o tipo de tarefa repetitiva que é melhor deixar para automação — que, no caso de meios Carvalho ancinho. Com certeza, "rake reset" irá descartar todas as tabelas e regenerar o esquema (faz um POST para /seed/PurgeDB e então outro POST/semente / todos), e "amostra de ancinho" irá descartar as tabelas, regenerar a esquema e gerar os dados de exemplo (POST /seed/SampleEntries). Só para completar, a propósito, uma exportação de"rake" (ou um POST /seed/export) retornará as instruções SQL usadas para fazer essas coisas.

(Curl ou Ruby pode fazer fazer um POST de exercício a uma um-linha de linha de comando, a propósito. O arquivo de Rakefile.rb tem exemplos de como fazer o POST usando o Net::HTTP::post_from que são muito fáceis de recortar e colar em outro arquivo RB, se você não quer enterrado dentro o Rakefile. Ou você poderia usar isso como uma droga para aprender Ruby. Ninguém está julgando.)

Está vivo!

Supondo que existem sem erros de digitação, uma vez que ancinho reset é feito, uma atualização no navegador traz uma página de Web de trabalho com um campo de texto simples (para o título do blog) e um botão "enviar". Entrando em um novo título do blog irá gerar um erro, no entanto — apesar de existirem Blogues, existe tipo de suposição (com base no que está no modo de exibição Index.cshtml) que há também algum tipo de coisa associada com Blogs chamado comentários.

O modelo relacional simples que domina o mundo do motor de blogging, blogs têm um relacionamento um-para-muitos comentários. Eu quero que modelo neste sistema, bem como, então, primeiro que eu preciso de um tipo de dados para objetos de comentário, que é simples com morte cerebral, novamente. Na verdade, porque comentários são (como objetos de Blog) objetos essencialmente dinâmicos, com elementos não interessantes para eles, eles ainda não precisam uma definição de classe do modelo — o objeto dinâmico todo estoque (o tipo de gêmeos, para aqueles que se lembram de minha coluna de agosto de 2013, "Indo com o Gemini biblioteca dinâmica," em msdn.microsoft.com/magazine/dn342877) está bem.

(E sim, se você não teve um tipo de "estamos longe de Kansas, Toto" do momento até agora, isto definitivamente é hora de parar e refletir: Você está trabalhando com um objeto de negócios cujo tipo você nunca se importou de definir).

Para descrever a relação de comentários em Blogs, no entanto, você precisa fazer duas coisas. Primeiro, comentários precisa de um repositório (como Blogs fez em minha última coluna):

public class Comments : DynamicRepository
{
}

Mais significativamente, a classe do Blog precisa ser renovada ligeiramente para capturar a relação um-para-muitos, tanto diretamente (Blog possuir uma coleção de objetos de comentário, que neste caso significa ter um objeto de comentários como um campo) e indiretamente (assim Carvalho sabe que Blog e comentário objetos são conectados no banco de dados e como). Isto é feito através da introdução de um novo método, sócios, que descreve a relação, como mostrado na Figura 3.

A Figura 3 descreve a relação um-para-muitos entre Blogs e comentários

public class Blog : DynamicModel
{
  Blogs blogs = new Blogs();
  // Define comments
  Comments comments = new Comments();
  public Blog() { }
  public Blog(object dto) : base(dto) { }
  // Add an Associates method to add the Comments() method
  IEnumerable<dynamic> Associates()
  {
    // And define the association
    // For othere examples of associations check out the Oak wiki
    yield return new HasMany(comments);
  }
}

Como você pode ver, não é realmente tão longe do que eu disse que precisava fazer: O modelo combina com o conceito abstrato de um blog bem de perto. Isto é onde a beleza e o poder de usar técnicas de dinâmicas podem ser vistos. Esta um mudança (o método de associados fazendo um "rendimento Retorno" de um HasMany) realmente desencadeia três novos métodos para ser adicionados ao Blog — comentários, CommentIds e Novo_comentário — para apoiar a relação de comentário objetos para objetos de Blog, os quais são pura andaimes e normalmente exigem que você escrever esses métodos "na marra" você estava usando normal, não dinâmicos c#. Naturalmente, porém, para tudo isto funcionar contra o banco de dados, você precisa guardá-lo com uma descrição do banco de dados da tabela de comentário, o que significa que você está de volta para SeedController (e uma ancinho subseqüente restauração), como mostrado no Figura 4.

Figura 4 Descrição de banco de dados da tabela Comments

public class Schema{
  public IEnumerable<Func<dynamic>> Scripts()
  {
    yield return CreateBlogsTable;
    yield return CreateCommentsTable;
  }
  public string CreateBlogsTable() // Here is the function definition
  {
    return Seed.CreateTable("Blogs",
      Seed.Id(),
      new { Name = "nvarchar(255)" },
      new { Body = "nvarchar(max)" }
    );
  }
  public string CreateCommentsTable() // Here is the function definition
  {
    return Seed.CreateTable("Comments",
      Seed.Id(),
      new { BlogId = "int", ForeignKey = "Blogs(Id)" },
      new { Body = "nvarchar(max)" }
    );
  }
}

Porque eu estou aqui, a propósito, vou acrescentar um par de entradas de blog, só para mostrar como usar o método SampleEntries. É muito mais fácil do que você imagina, como mostrado em Figura 5.

Figura 5 adicionando entradas de Blog

public void SampleEntries(){
  var blog1 = new // Store the ID
  {
    Name = "Hello, Oak Blog",
    Body = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
  }.InsertInto("Blogs");
  new { Body = "great job!", BlogId = blog1 }.InsertInto("Comments");
  new
  {
    Name = "Here are cat pictures!",
    Body = "Meowem hisum collar sit amet, addipisces lick."
  }.InsertInto("Blogs");
}

Mais uma vez, o uso de objetos dinâmicos e métodos de extensão torna quase não parece mais com c#. (Neste caso, você está não criando um objeto dinâmico, tanto como criar uma instância de um objeto anônimo com as propriedades gerada automaticamente título e corpo e em seguida, usando o método de extensão InsertInto para fazer a inserção real.) E, a propósito, a única razão para interceptar o objeto na variável local blog1 é assim que pode ser usado como o valor de ID do blog para o comentário sobre ele.

Vá em frente. Atualizar o banco de dados com uma amostra de ancinho, em seguida, atualizar o navegador.

Próximo: Validação de entrada de usuário

Isso está virando algumas coisas interessantes, se não já era. Você não tem nenhum tipo de modelo definido, ainda tens um armazenamento de banco de dados e modelo de trabalho para isso. Sem scripts SQL estavam envolvidos (embora é justo que sugerem que o uso repetido de métodos ad hoc na classe de esquema poderia facilmente transformar em scripts SQL), e é importante salientar que todo o código que faz tudo isso (ainda) está escondido na forma de fonte na pasta carvalho no projeto scaffolded, no caso de você precisa depurar ou apenas sentir como a navegação.

Existem ainda algumas coisas ainda para fazer, como validar o entrada para certificar-se do usuário é tudo de bom e correto antes de guardar, mas é um bom assunto para a próxima.

Feliz codificação!

Ted Neward é o diretor da Neward & Associates LLC. Ele tem escrito mais de 100 artigos e autor e co-autor de vários livros, incluindo "Professional F # 2.0" (Wrox, 2010). Ele é um F # MVP e fala em conferências em todo o mundo. Ele consulta, mentores regularmente — contatá-lo em ted@tedneward.com se você está interessado em ter-lhe vir trabalhar com sua equipe, ou ler seu blog em blogs.tedneward.com.

Agradecemos ao seguinte especialista técnico pela revisão deste artigo: Amir Ribeiro (criador do projeto de carvalho)
Amir Ribeiro é um membro ativo da comunidade de desenvolvimento, com aparições em .net Rocks, código de pastoreio e Hanselminutes. Ele tem experiência em muitos.NET-com base em frameworks da Web, arquiteturas de resto, Ruby, JavaScript (front-end e NodeJS), iOS e F #. Ele está sempre se esforçando melhorar a indústria através de contribuições de código aberto, independente de consultoria e blogging (amirrajan.net) e pode ser contatado em ar@amirrajan.net. "