Uma das features que mais agradam os usuários de forma geral no WebMatrix é a facilidade para o acesso e a manipulação dos dados. Evidentemente que esta observação passa pelo ótimo gerenciador visual disponibilizado pela ferramenta, entretanto, hoje não falarei especificamente do gerenciador de bancos de dados do WebMatrix, mas sim, de uma de suas formas de acesso a dados (internals).
WebMatrix.Data
Com o lançamento do primeiro beta do WebMatrix a Microsoft trouxe um container simplificado e eficiente de acesso a dados – “WebMatrix.Data”. Através desta biblioteca conseguimos com poucas linhas de comando acessar os dados e impor ações sobre eles, entretanto, se você é como eu e procura sempre escrever códigos mais elaborados, que visem a reutilização dos mesmos e evite o retrabalho, com certeza encontrará alguns problemas na utilização de “WebMatrix.Data”. Mas antes de apontarmos estes problemas, para fins de contextualização, considere o código apresentado pela Listagem 1.
[csharp]
@{
var Banco = Database.Open(“WebmatrixDataStrongly”);
string SentencaSQL = “SELECT * FROM Tabela1 WHERE CPF = 123456789”;
var Dados = Banco.Query(SentencaSQL);
foreach(var Cli in Dados)
{
@Cli.CPF;
}
}
[/csharp]
Listagem 1: Acessando banco de dados
A Listagem 1 apresenta um trecho de código utilizando razor syntax que tem a função de acessar um banco de dados (WebmatrixDataStrongly.sdf) e exibir os dados retornados pela consulta SQL. Esta forma simplificada de acesso a dados é proporcionada pela biblioteca “WebMatrix.Data”, nativa no set de recursos do WebMatrix.
Muito embora a abordagem seja simples (e este é o objetivo do WebMatrix), desenvolvedores mais “chatos” podem encontrar problemas com esta abordagem. Para justificar esta afirmação, considere o código apresentado na linha 4 na Listagem 1.
[csharp]
var Dados = Banco.Query(SentencaSQL);
[/csharp]
Note, o método “Query()” retorna um objeto IEnumerable<dynamic>, assim, é possível acessar a informação “CPF” de forma direta. Uma observação importante é que, “CPF” é uma coluna definida na especificação do banco de dados, portanto, o acesso ao dado é realizado em tempo de execução, assim, perdemos os benefícios do compilation time. Outro problema a ser apontado por esta abordagem: imagine que se queira adicionar um comportamento avançado, como por exemplo, validar o CPF que está chegando. Com esta abordagem, não seria possível fazer. Além disso, a dificuldade de manutenção, pois, como as chamadas a consultas são isoladas, a medida que a aplicação cresce, os problemas para manter estas chamadas aumentam em igual proporção.
Uma alternativa interessante solucionar os problemas mencionados, é a implementação de retornos de dados tipados. Assim, considere o código apresentado pela Listagem 2.
[csharp]
using System;
using System.Collections.Generic;
using System.Web;
public class ClienteType
{
public string CPF { get; set; }
public string RG { get; set; }
public string CPF_RG
{
get
{
return CPF + ” – ” + RG;
};
}
}
[/csharp]
Listagem 2: Definição do objeto “ClienteType”
O que estamos fazendo na Listagem 2 é, definir os elementos de objetos “ClienteType”. Assim, podemos fazer uma chamada tipada para obter os dados (nos assegurando de que os dados não conterão erros e estarão centralizados em apenas um local. A chamada a qual nos referimos pode ser visualizada na Listagem 3.
[csharp]
@{
var Banco = Database.Open(“WebmatrixDataStrongly”);
var Dados = Banco.Query<ClienteType>(“SELECT * FROM Tabela1 WHERE CPF = 123456789”);
foreach(var Cli in Dados)
{
@Cli.CPF_RG;
}
}
[/csharp]
Listagem 3: Retornando dados de forma tipada
A diferença em relação a Listagem 1 é sutil visualmente, entretanto, é enorme conceitualmente. Agora, estamos garantindo que os dados retornados sejam do tipo “ClienteType”. Assim, temos a segurança da informação sendo mapeada e não corremos o risco de nos depararmos com erros em tempo de execução, além de passarmos a ter o conforto de visualizar os elementos do objeto via intellesense. Outra observação importante é que, agora, não estamos exibindo apenas uma informação direto do banco de dados e sim, estamos exibindo “CPF_RG”, uma composição de valores implementada na definição da estrutura do objeto.
Elementos responsáveis pela mágica
Para que o milagre ocorra, temos a implementação de duas classes (propostas por Jeremy Skinner). As Listagens 4 e 5 as apresentam respectivamente.
[csharp]
public static class DataExtensions {
public static IEnumerable<T> Query<T>(this Database db, string commandText, params object[] args) {
var queryResults = db.Query(commandText, args);
var mapper = Mapper<T>.Create();
return (from DynamicRecord record in queryResults select mapper.Map(record)).ToList();
}
}
[/csharp]
Listagem 4: Extensão que implementa o método Query()
[csharp]
public class Mapper<T> {
private Func<T> factory;
private Dictionary<string, Action<T, object>> setters = new Dictionary<string, Action<T, object>>();
private static Lazy<Mapper<T>> _instance = new Lazy<Mapper<T>>(() => new Mapper<T>());
public static Mapper<T> Create() {
return _instance.Value;
}
private Mapper() {
factory = CreateActivatorDelegate();
foreach (var property in typeof(T).GetProperties()) {
if (property.CanWrite) {
setters[property.Name] = BuildSetterDelegate(property);
}
}
}
public T Map(DynamicRecord record) {
var instance = factory();
foreach(var column in record.Columns) {
Action<T, object> setter;
if(setters.TryGetValue(column, out setter)) {
setter(instance, record[column]);
}
}
return instance;
}
private static Func<T> CreateActivatorDelegate() {
return CreateActivatorDelegate(typeof(T).GetConstructor(Type.EmptyTypes));
}
private static Func<T> CreateActivatorDelegate(ConstructorInfo constructor) {
return Expression.Lambda<Func<T>>(Expression.New(constructor)).Compile();
}
private static Action<T, object> BuildSetterDelegate(PropertyInfo prop) {
var instance = Expression.Parameter(typeof(T), “x”);
var argument = Expression.Parameter(typeof(object), “v”);
var setterCall = Expression.Call(
instance,
prop.GetSetMethod(true),
Expression.Convert(argument, prop.PropertyType));
return (Action<T, object>)Expression.Lambda(setterCall, instance, argument).Compile();
}
}
[/csharp]
Listagem 5: Implementação do “mapeador” do objeto de conexão
A Listagem 4 apresenta a implementação do método “Query<T>()” que, simplesmente delega a função para o mapeador (Listagem 5), sendo que este faz a ligação dos elementos retornados por WebMatrix.Data com as propriedades do objeto recém criado (no caso do exemplo deste post, “ClienteType”).
Bom, por hoje é só pessoal. Espero que este post possa tê-lo ajudado de alguma forma a melhor suas estratégias de manipulação e acesso a dados com WebMatrix.
Não se esqueça de deixar seus comentários :-).
Facebook
Twitter
Instagram
LinkedIn
RSS