- A Parte 1 abordou como instalar e configurar o Couchbase no Windows
- A Parte 2 abordou alguns termos do Couchbase que você precisará conhecer
- A Parte 3 mostrou o exemplo mais simples de uso do Couchbase no ASP.NET
Nesta postagem do blog, vou me basear na parte 3, apresentando Linq2Couchbase. Também moverei o Couchbase para fora do Controller e o colocarei em uma versão muito básica do repositório aula. Meu objetivo nesta postagem do blog é fazer com que você se sinta confortável com os conceitos básicos do Couchbase e do Linq2Couchbase e possa começar a aplicá-los em seu aplicativo da Web.
Movendo o Couchbase para fora do controlador
O trabalho do controlador é direcionar o tráfego: receber solicitações, entregá-las a um modelo e, em seguida, fornecer os resultados à visualização. Para acompanhar o Princípios SOLID (especificamente o Princípio da Responsabilidade Única), o acesso aos dados deve estar em algum lugar em um "modelo" e não no controlador.
A primeira etapa é refatorar o código existente. Podemos manter o "exemplo realmente simples" da Parte 3, mas ele deve ser movido para um método em outra classe. Aqui está o HomeController refatorado e o novo PersonRepository:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
público classe HomeController : Controlador { privado somente leitura PersonRepository _personRepo; público HomeController(PersonRepository pessoaRepo) { _personRepo = pessoaRepo; } público Resultado da ação Índice() { var pessoa = _personRepo.GetPersonByKey("foo::123"); retorno Conteúdo("Name: " + pessoa.nome + ", Endereço: " + pessoa.endereço); } } público classe PersonRepository { privado somente leitura IBucket _bucket; público PersonRepository(IBucket balde) { _bucket = balde; } público dinâmico GetPersonByKey(string chave) { retorno _bucket.Obter(chave).Valor; } } |
Alguns aspectos a serem observados:
- O HomeController não depende mais diretamente do Couchbase. Se a API do Couchbase fosse alterada, por exemplo, teríamos que fazer alterações apenas no PersonRepository, não no HomeController.
- Não foi necessário informar explicitamente ao StructureMap como instanciar o PersonRepository. Ele acha que o PersonRepository é "auto-vinculado". Se, em vez disso, eu usasse uma interface (como IPersonRepository), teria que fazer uma alteração no DefaultRegistry para informar isso ao StructureMap. Se você estiver usando um contêiner IoC diferente, a situação pode ser diferente.
Refatoração para usar uma classe de pessoa
No exemplo acima, estou usando um dinâmico
objeto. dinâmico
é ótimo para algumas situações, mas, nesse caso, seria uma boa ideia criar uma definição mais concreta do que é uma "Pessoa". Posso fazer isso com uma classe C#.
1 2 3 4 5 6 |
público classe Pessoa { público string Nome { obter; definir; } público string Endereço { obter; definir; } } |
Também atualizarei o PersonRepository para usar essa classe.
1 2 3 4 5 |
público Pessoa GetPersonByKey(string chave) { retorno _bucket.Obter(chave).Valor; } |
Enquanto isso, vou tomar algumas medidas para tornar esse aplicativo mais parecido com um MVC adequado. Em vez de retornar Content(), farei com que a ação Index retorne um View e passarei a ele um lista de objetos Person. Criarei um arquivo Index.cshtml, que será delegado a uma parte de _person.cshtml. Também vou inserir um layout que usa o Bootstrap. Essa última parte é totalmente gratuita, mas fará com que as capturas de tela fiquem um pouco mais bonitas.
Nova ação de índice:
1 2 3 4 5 6 7 |
público Resultado da ação Índice() { var pessoa = _personRepo.GetPersonByKey("foo::123"); var lista = novo Lista {pessoa}; retorno Ver(lista); } |
Index.cshtml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@modelo Lista<CouchbaseAspNetExample2.Modelos.Pessoa> @{ Bolsa de visualização.Título = "Página inicial: Exemplo de Couchbase e ASP.NET"; } @se (!Modelo.Qualquer()) { <p>Lá são não pessoas ainda.</p> } @antes de (var item em Modelo) { @Html.Parcial("_person", item) } |
person.cshtml:
1 2 3 4 5 6 7 8 9 |
@modelo CouchbaseAspNetExample2.Modelos.Pessoa <div <extensão classe="hljs-keyword">classe</extensão>=<extensão classe="hljs-string">"panel panel-default"</extensão>> <div <extensão classe="hljs-keyword">classe</extensão>=<extensão classe="hljs-string">"Título do painel"</extensão>> <h2 <extensão classe="hljs-keyword">classe</extensão>=<extensão classe="hljs-string">"título do painel"</extensão>>@Modelo.Nome</h2> </div> <div <extensão classe="hljs-keyword">classe</extensão>=<extensão classe="hljs-string">"painel-corpo"</extensão>> @Html.Bruto(Modelo.Endereço) </div> </div><código> |
Agora está um pouco mais bonito. Além disso, poderemos mostrar uma lista completa de documentos pessoais mais adiante na demonstração.
Apresentando o Linq2Couchbase
O Couchbase Server oferece suporte a uma linguagem de consulta conhecida como N1QL. É um superconjunto de SQL e permite que você aproveite seu conhecimento existente de SQL para construir consultas muito poderosas sobre documentos JSON no Couchbase. O Linq2Couchbase leva isso um passo adiante e converte consultas Linq em consultas N1QL (da mesma forma que o Entity Framework converte consultas Linq em consultas SQL).
O Linq2Couchbase faz parte do Laboratórios Couchbasee ainda não faz parte da biblioteca principal e suportada do Couchbase .NET SDK. No entanto, se você estiver acostumado com o Entity Framework, o NHibernate.Linq ou qualquer outro provedor Linq, essa é uma ótima maneira de se apresentar ao Couchbase. Para algumas operações, você ainda precisará usar o núcleo do Couchbase .NET SDK, mas há muitas coisas que podemos fazer com o Linq2Couchbase.
Comece adicionando o Linq2Couchbase com o NuGet (se ainda não o fez).
O N1QL (e, portanto, o Linq2Couchbase) depende do balde que está sendo indexado. Acesse o Console do Couchbase, clique na guia "Query" (Consulta) e crie um índice primário no hello-couchbase
balde.
Se você não tiver um índice, o Linq2Couchbase fornecerá uma mensagem de erro útil como "No primary index on keyspace hello-couchbase. Use CREATE PRIMARY INDEX para criar um."
Para usar o Linq2Couchbase de forma mais eficaz, precisamos começar a dar aos documentos do Couchbase um campo de "tipo". Dessa forma, podemos diferenciar entre um documento "pessoa" e um documento "local", por exemplo. Neste exemplo, terei apenas documentos de "pessoa", mas é uma boa ideia fazer isso desde o início. Vou criar um campo Type e defini-lo como "Person". Também colocarei um atributo na classe C# para que o Linq2Couchbase entenda que essa classe se destina a um determinado tipo de documento.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
usando Couchbase.Linq.Filtros; [Filtro DocumentType("Pessoa")] público classe Pessoa { público Pessoa() { Tipo = "Pessoa"; } público string Tipo { obter; definir; } público string Nome { obter; definir; } público string Endereço { obter; definir; } } |
Se você fizer essas alterações, seu aplicativo continuará funcionando. Isso ocorre porque ainda estamos recuperando o documento por sua chave. Mas agora vamos alterar a ação Index para tentar obter TODOS os documentos pessoais.
1 2 3 4 5 6 |
público Resultado da ação Índice() { var lista = _personRepo.Obter tudo(); retorno Ver(lista); } |
Precisaremos implementar esse novo método de repositório GetAll:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
usando Sistema.Coleções.Genéricos; usando Sistema.Linq; usando Couchbase.Núcleo; usando Couchbase.Linq; usando Couchbase.Linq.Extensões; usando Couchbase.N1QL; público classe PersonRepository { privado somente leitura IBucket _bucket; privado somente leitura IBucketContext _contexto; público PersonRepository(IBucket balde, IBucketContext contexto) { _bucket = balde; _contexto = contexto; } público Lista Obter tudo() { retorno _contexto.Consulta() .Consistência de varredura(Consistência de varredura.RequestPlus) .Ordem(p => p.Nome) .ToList(); } } |
Neste exemplo, estou dizendo ao Couchbase para ordenar todos os resultados por Nome. Se quiser, você pode experimentar os métodos Linq normais com os quais está acostumado: Where, Select, Take, Skip e assim por diante.
Ignore esse ScanConsistency por enquanto: Falarei mais sobre isso mais tarde. Mas e quanto ao IBucketContext? O IBucketContext é semelhante ao DbContext do Entity Framework ou ao ISession do NHibernate. Para obter esse IBucketContext, precisaremos atualizar o DefaultRegistry.
1 2 3 4 5 |
Para().Singleton().Uso("Obter um bucket do Couchbase", x => Ajudante de cluster.GetBucket("hello-couchbase", "senha!")); Para().HttpContextScoped().Uso("Obter um contexto de balde do Couchbase", x => novo BucketContext(x.GetInstance())); |
Isso está dizendo ao StructureMap que quero criar um novo BucketContext e quero que ele tenha o escopo de cada solicitação HTTP. Se você usar HttpContextScoped no StructureMap, também precisará usar HttpContextLifecycle.DisposeAndClearAll()
no Application_EndRequest. Se você estiver usando um contêiner IoC diferente, terá de gerenciá-lo de forma diferente.
Agora, se você compilar e executar o aplicativo Web novamente, ele exibirá a mensagem "There are no people yet" (Ainda não há pessoas). Ei, onde eu fui parar?! Eu não apareci porque o documento "foo::123" ainda não tem um campo "type". Vá para o Console do Couchbase e adicione-o.
Depois de fazer isso, atualize sua página da Web e a pessoa aparecerá novamente.
Uma observação rápida sobre o ScanConsistency
O Linq2Couchbase depende de um índice para gerar e executar consultas. Quando você adiciona novos documentos, o índice precisa ser atualizado. Até que o índice seja atualizado, os documentos ainda não indexados não serão retornados pelo Linq2Couchbase (por padrão). Ao adicionar o ScanConsistency do RequestPlus (Consulte a documentação do Couchbase para obter detalhes sobre a consistência da varredura), o Linq2Couchbase aguardará efetivamente até que o índice seja atualizado antes de executar uma consulta e retornar uma resposta. Essa é uma troca que você terá de considerar ao projetar seu aplicativo. O que é mais importante: velocidade bruta ou precisão total?
Como um exemplo simples, digamos que você esteja criando um sistema de gerenciamento de conteúdo:
- Se estiver criando ferramentas de administração, provavelmente valoriza mais a precisão total do que o desempenho.
- Os administradores precisam saber exatamente o que há nos dados para gerenciá-los com eficiência.
- Os recursos administrativos são usados com pouca frequência em comparação com os recursos públicos, portanto, alguma latência é aceitável.
- Se você estiver criando uma página pública que liste todo o conteúdo, a velocidade bruta provavelmente será mais importante.
- Se uma nova página de conteúdo demorar um ou dois segundos a mais para aparecer para o público, não há problema.
- A parte pública de um site será acessada com muita frequência, portanto, o desempenho é um fator importante.
- Este é apenas um exemplo: o tipo de consistência de varredura que você deve usar depende de você e de seus casos de uso.
Conclusão
O Linq2Couchbase é uma ferramenta poderosa para trabalhar com o Couchbase de forma familiar. Ela é de código aberto, mas ainda não é oficialmente suportada pelo Couchbase. Disponibilizei todo o código para esta postagem do blog em Github.
No próximo post, mostrarei como usar o Linq2Couchbase para criar, atualizar e excluir documentos. Também analisaremos a diferença de flexibilidade que o Couchbase pode oferecer em comparação com um RDBMS tradicional, como o SQL Server.
Tem dúvidas? Algo não está funcionando como você esperava? Por favor, deixe um comentário, me envie uma mensagem no Twitterou envie um e-mail para mim (matthew.groves AT couchbase DOT com) e eu o ajudarei!