Olá pessoal, vamos dar continuidade em nossa série sobre ADO.Net Data Services (DS), tecnologia introduzida no .Net Framework 3.5 SP1.
Este é o terceiro artigo da série, caso você não tenha lido os primeiros artigos, segue os links:
Artigo I
Artigo II
Aqui vamos tratar da manipulação de dados de uma entidade que possui uma propriedade relacionada à outra entidade, seguindo nosso modelo de dados iremos considerar a entidade "Categorias" que contêm um relacionamento com "TipoCategoria".
Seguindo a mesma idéia (hi agora não tem acento
, escreve-se "ideia"), do artigo anterior vamos criar um "Form" para trabalhar o cadastro de Categorias conforme figura abaixo.

Figura 1: Formulário de manutenção de "Categorias"
Como já vimos todas as configurações do ADO.Net Data Services, vamos diretamente ao código.
Vamos implementar as funcionalidades de nosso formulário, começaremos vendo como podemos consultar um objeto específico da classe "Categoria". Para isso vamos inserir o seguinte código no evento click do botão "Consultar" (não vamos nos preocupar com validações em nossos exemplos).
private void btnConsultar_Click(object sender, EventArgs e)
{
//endereço do serviço
Uri enderecoServico = new Uri("http://localhost:3752/DSProdutos.svc");
//Contexto do serviço que utilizamos para manipular os dados
srDSProdutos.DBProdutosEntities contexto;
contexto = new ClienteWindows.srDSProdutos.DBProdutosEntities(enderecoServico);
//fazendo a consulta ao serviço através de LINQ
var cat = (from c in contexto.Categorias
where c.ID == Convert.ToInt32(txtID.Text)
select c).FirstOrDefault();
if (cat != null)
{
txtDescricao.Text = cat.Descricao;
txtIDTipoCat.Text = cat.TipoCategoria.ID.ToString(); //AQUI VAI DAR ERRO!!!
txtDescTipoCat.Text = cat.TipoCategoria.Descricao; //AQUI TAMBÉM (de propósito)
}
else
txtDescricao.Text = "No encontrado.";
}
Note que inserimos um erro que ocorrerá somente em tempo de execução, isso foi proposital para incluirmos um novo elemento em nossos estudos. O DS trabalha com o conceito "lazy loading". Lazy Loading é o mecanismo utilizado pelos frameworks de persistência para carregar informações sobre demanda. Esse mecanismo torna as entidades mais leves, pois suas associações são carregadas apenas no momento em que o método que disponibiliza o dado associativo é chamado.
No DS isso pode ser facilmente notado quando fazemos a chamada através da URI em nosso navegador, por exemplo, vamos consultar a Categoria "2" através do navegador utilizando o endereço:
http://localhost:3752/DSProdutos.svc/Categorias(2)
Obtemos o seguinte resultado:

Figura 2: Consulta a Categoria ID=2
Podemos perceber no XML retornado que existe um elemento "link rel" indicando que existe um relacionamento com "TipoCategoria" no entanto não temos acesso ao objeto relacionado, pois não vem carregado. Para obter o que queremos precisamos utilizar o
parâmetro "expand" alterando nossa URI conforme abaixo:
http://localhost:3752/DSProdutos.svc/Categorias(2)?$expand=TipoCategoria
Com isso obtemos o resultado abaixo:

Figura 3: Consulta a Categoria ID=2 utilizando "expand"
Para obter o mesmo resultado em nosso código para o botão consulta, precisamos fazer uma pequena modificação em nossa expressão LINQ, veja abaixo o que deve ser feito.
private void btnConsultar_Click(object sender, EventArgs e)
{
//endereço do serviço
Uri enderecoServico = new Uri("http://localhost:3752/DSProdutos.svc");
//Contexto do serviço que utilizamos para manipular os dados
srDSProdutos.DBProdutosEntities contexto;
contexto = new ClienteWindows.srDSProdutos.DBProdutosEntities(enderecoServico);
//fazendo a consulta ao serviço através de LINQ
//AQUI UTILIZAMOS Expand
var cat = (from c in contexto.Categorias.Expand("TipoCategoria")
where c.ID == Convert.ToInt32(txtID.Text)
select c).FirstOrDefault();
if (cat != null)
{
txtDescricao.Text = cat.Descricao;
//agora sim, teremos acesso ao objeto Tipo Categoria.
txtIDTipoCat.Text = cat.TipoCategoria.ID.ToString();
txtDescTipoCat.Text = cat.TipoCategoria.Descricao;
}
else
txtDescricao.Text = "No encontrado.";
}
Veja o resultado que obtemos ao consultar a Categoria "2" em nosso projeto WindowsForms.

Figura 4: Consulta a Categoria ID=2 utilizando o método "Expand"
Agora vamos ao botão 'Salvar' que será utilizado para incluir e alterar. Veremos que durante os procedimentos teremos um novo método apresentado chamado "SetLink" que é responsável por linkar os dois objetos, persistindo no banco de dados a chave estrangeira TipoCategoriaId na tabela Categorias.
private void btnSalvar_Click(object sender, EventArgs e)
{
//setando a URI do serviço
Uri enderecoServico = new Uri("http://localhost:3752/DSProdutos.svc");
//contexto do servio
srDSProdutos.DBProdutosEntities contexto;
contexto = new ClienteWindows.srDSProdutos.DBProdutosEntities (enderecoServico);
contexto.MergeOption = System.Data.Services.Client.MergeOption.AppendOnly;
//criando o objeto que ser persistido
srDSProdutos.Categorias cat = new ClienteWindows.srDSProdutos.Categorias();
cat.Descricao = txtDescricao.Text;
//devemos buscar o objeto de TipoCategoria
//para associar a Categoria
var tipo (from t in contexto.TipoCategoria
where t.ID == Convert.ToInt32(txtIDTipoCat.Text)
select t).FirstOrDefault();
if (txtID.Text == "Novo") //no caso de um novo cadastro
{
//adicionando o objeto no contexto
contexto.AddToCategorias(cat);
//aqui fazemos o link entre Categoria e Tipo Categoria
contexto.SetLink(cat, "TipoCategoria", tipo);
//salvando as alterações do contexto
contexto.SaveChanges();
//no caso de novo cadastro o DS nos retorno o objeto
//com isso podemos identificar o ID gerado pois utilizamos
//um campo Identity (gerado sequencialmente)
txtID.Text = cat.ID.ToString();
}
else //alteração
{
cat.ID = Convert.ToInt32(txtID.Text);
//para o caso de alteração, devemos atachar o objeto no contexto
contexto.AttachTo("Categorias", cat);
contexto.UpdateObject(cat);
//no caso de update o setlink vem após o UpdateObject
contexto.SetLink(cat, "TipoCategoria", tipo);
contexto.SaveChanges();
}
}
Agora vamos ver o código para o botão 'Excluir'.
private void btnExcluir_Click(object sender, EventArgs e)
{
//setando a URI do serviço
Uri enderecoServico = new Uri("http://localhost:3752/DSProdutos.svc");
//contexto do serviço
srDSProdutos.DBProdutosEntities contexto;
contexto = new ClienteWindows.srDSProdutos.DBProdutosEntities (enderecoServico);
contexto.MergeOption = System.Data.Services.Client.MergeOption.AppendOnly;
//criando o objeto que ser excluído
srDSProdutos.Categorias cat = new ClienteWindows.srDSProdutos.Categorias ();
cat.Descricao = txtDescricao.Text;
cat.ID = Convert.ToInt32(txtID.Text);
//para o caso de exclusão, devemos atachar o objeto no contexto
contexto.AttachTo("Categorias", cat);
contexto.DeleteObject(cat);
contexto.SaveChanges();
}
Conclusão: Neste artigo aprendemos como trabalhar com entidades relacionadas com outras entidades, criando os vínculos entre elas e também aprendemos sobre o conceito "lazy loading" utilizado no DS. Em nosso próximo artigo criaremos as funções para o cadastro de produtos e como podemos navegar entre as entidades relacionadas. Até mais.