Uso do LINQ para vários códigos
Boa tarde!
Eu tenho uma situação da qual preciso utilizar o LINQ para trazer registro conforme vários códigos selecionados pelo usuário. Para isso, foi feito o código abaixo:
public List<PessoaDeficiencia> SelecionarDeficienteFisico(List<PessoaStatus> pStatusPessoa)
{
List<PessoaDeficiencia> listPessoaDeficiencia = new List<PessoaDeficiencia>();
try
{
var consulta = from pessoadeficiencia in _context.tab_pessoa_deficiencia
where (pessoadeficiencia.tab_pessoa.possui_deficiencia == "S")
&& (pessoadeficiencia.tab_pessoa.tab_tipo_contratacao.c_def_fisico == "S")
&& (pessoadeficiencia.tab_pessoa.tab_pessoa_status.id_status_pessoa != 2)
&& ((0 == pStatusPessoa[0].IdStatusPessoa)
|| (from pessoastatus in pStatusPessoa select pessoastatus.IdStatusPessoa).Contains(pessoadeficiencia.tab_pessoa.tab_pessoa_status.id_status_pessoa))
select new
{
pessoadeficiencia.id_pessoa_deficiencia,
pessoadeficiencia.tab_pessoa.Id_pessoa,
pessoadeficiencia.tab_tipo_deficiente_fisico.id_tipo_deficiencia,
pessoadeficiencia.path
};
.
.
.
.
Foi utilizado um parâmetro, que é uma lista de uma classe. Nesta Lista, uma das propriedades é referente ao código. Por isso, com uma lista de vários códigos, gostaria de fazer uso do LINQ como se fosse na sintaxe do SELECT usando a instrução IN (SELECT * FROM TABELA WHERE Codigo IN (1,2,3,4,...)...
Mas, ao tentar realizar a consulta, gerou uma exceção de conflito entre uma lista de array do tipo int com o próprio tipo int (na linha "(from pessoastatus in pStatusPessoa select pessoastatus.IdStatusPessoa).Contains(pessoadeficiencia.tab_pessoa.tab_pessoa_status.id_status_pessoa))".
Por isso gostaria de saber se alguém poderia me ajudar a como realizar esta tarefa fazendo seleção pelo LINQ na condição de usar vários código como se faz no SELECT com a palavra-chave IN??
Neste exemplo, está sendo usado LINQ to Entities
Obrigado, fico no aguardo!!
Eu tenho uma situação da qual preciso utilizar o LINQ para trazer registro conforme vários códigos selecionados pelo usuário. Para isso, foi feito o código abaixo:
public List<PessoaDeficiencia> SelecionarDeficienteFisico(List<PessoaStatus> pStatusPessoa)
{
List<PessoaDeficiencia> listPessoaDeficiencia = new List<PessoaDeficiencia>();
try
{
var consulta = from pessoadeficiencia in _context.tab_pessoa_deficiencia
where (pessoadeficiencia.tab_pessoa.possui_deficiencia == "S")
&& (pessoadeficiencia.tab_pessoa.tab_tipo_contratacao.c_def_fisico == "S")
&& (pessoadeficiencia.tab_pessoa.tab_pessoa_status.id_status_pessoa != 2)
&& ((0 == pStatusPessoa[0].IdStatusPessoa)
|| (from pessoastatus in pStatusPessoa select pessoastatus.IdStatusPessoa).Contains(pessoadeficiencia.tab_pessoa.tab_pessoa_status.id_status_pessoa))
select new
{
pessoadeficiencia.id_pessoa_deficiencia,
pessoadeficiencia.tab_pessoa.Id_pessoa,
pessoadeficiencia.tab_tipo_deficiente_fisico.id_tipo_deficiencia,
pessoadeficiencia.path
};
.
.
.
.
Foi utilizado um parâmetro, que é uma lista de uma classe. Nesta Lista, uma das propriedades é referente ao código. Por isso, com uma lista de vários códigos, gostaria de fazer uso do LINQ como se fosse na sintaxe do SELECT usando a instrução IN (SELECT * FROM TABELA WHERE Codigo IN (1,2,3,4,...)...
Mas, ao tentar realizar a consulta, gerou uma exceção de conflito entre uma lista de array do tipo int com o próprio tipo int (na linha "(from pessoastatus in pStatusPessoa select pessoastatus.IdStatusPessoa).Contains(pessoadeficiencia.tab_pessoa.tab_pessoa_status.id_status_pessoa))".
Por isso gostaria de saber se alguém poderia me ajudar a como realizar esta tarefa fazendo seleção pelo LINQ na condição de usar vários código como se faz no SELECT com a palavra-chave IN??
Neste exemplo, está sendo usado LINQ to Entities
Obrigado, fico no aguardo!!
Carlos Nogueira
Curtidas 0
Respostas
Carlos Nogueira
01/07/2009
Só para acrescentar com o que foi informado na mensagem anterior, já foi feito outros testes para informar estes códigos com o método Contains(), fazendo uso de ArrayList ou de List<int> para armazenar estes códigos e usar da sintaxe do LINQ, mas mesmo assim, apresentou uma exceção. A descrição da exceção segue abaixo:
LINQ to Entities não reconhece o método 'Boolean Contains(Int32)', que não pode ser convertido em uma expressão de armazenamento.
LINQ to Entities não reconhece o método 'Boolean Contains(Int32)', que não pode ser convertido em uma expressão de armazenamento.
GOSTEI 0
Luiz Maia
01/07/2009
Ola Carlos,
Para fazer o que necessita, vc precisa usar apenas um Array:
int[] productList = new int[] { 1, 2, 3, 4 };
var myProducts = from p in db.Products
where productList.Contains(p.ProductID)
select p;Espero ter ajudado.
Por favor me de um retorno se conseguiu, ok?
Ja fiz desta forma e funciona perfeitamente.
Agora, outra forma de fazer é criar um coleção e depois comparar. Veja o exemplo abaixo: AdventureWorks.DB db=new DB(); var itemQuery = from cartItems in db.SalesOrderDetails where cartItems.SalesOrderID == 75144 select cartItems.ProductID; E depois vc compara: var myProducts = from p in db.Products where itemQuery.Contains(p.ProductID) select p; AbraçosAttLuiz Maia
Por favor me de um retorno se conseguiu, ok?
Ja fiz desta forma e funciona perfeitamente.
Agora, outra forma de fazer é criar um coleção e depois comparar. Veja o exemplo abaixo: AdventureWorks.DB db=new DB(); var itemQuery = from cartItems in db.SalesOrderDetails where cartItems.SalesOrderID == 75144 select cartItems.ProductID; E depois vc compara: var myProducts = from p in db.Products where itemQuery.Contains(p.ProductID) select p; AbraçosAttLuiz Maia
GOSTEI 0
Carlos Nogueira
01/07/2009
Entendo. Gostaria de implementar uma solução em que não fosse necessário fazer utilização de duas querys para resolver o problema. Em relação ao primeiro exemplo, já fiz este tipo de simulação, porém os códigos contidos no array podem variar de uma hora para outra, conforme seleção do usuário (por este motivo coloquei um parâmetro no método para receber um ou mais códigos selecionados pelo usuário). Por isso que no post anterior informei as simulações feitas dom ArrayList e List<int> para fazer com que esta lista de códigos fosse dinâmica para informar na sintaxe do linq, porém ao fazer isso gera a exceção informada pelo método Contains() que não possui 'expressão booleana".
Existe a possibilidade de implementar uma solução nestes moldes, isto é, que seja solucionado numa única query com esta list de códigos dinâmicas? Por exemplo, existe alguma foma de fazer com que o array int[] citado por você no exemplo seja dinâmico, isto é, a cada chamada do método montar este array de int com a sintaxe int[]? Tentei fazer isso com as classes ArrayList e List<int> mas não conseguiu!
Eu li em alguns sites (blogs) que a Microsoft pretende disponibilizar mais expressões para o método Contains() numa próxima versão do LINQ, isso é mesmo verdade?
Bem, fico no aguardo, e obrigado pela atenção!
GOSTEI 0
Luiz Maia
01/07/2009
Ola Carlos,
Nada impede que seus dados sejam dinâmicos. Criei um array de inteiros estatico abaixo somente para testes.
Faça um metodo que retorna estes arrays de inteiro para vc então.
Pegue seus dados dinamicos, por exemplo "1,3,5,6,7,8,9,12,13..". Vc tem estes dados que recuperou em outro metodo, correto?
Agora monte um metodo que transforma isto num array, usando um "split" e depois um "for". Sabe como fazer?
Aguardo seu retorno.
Abraços
Att
Luiz Maia
GOSTEI 0
Carlos Nogueira
01/07/2009
Para ser sincero não. Você poderia me passar um exemplo de como eu poderia proceder para implementar esta forma dinâmica como você mencionou na mensagem anterior?
GOSTEI 0
Luiz Maia
01/07/2009
Carlos,
Uma solução bem mais estrutura e com bases em boas praticas de codificação seria usar um Stored Procedure para isto, veja um exemplo abaixo:
public static List<Entity.Estado> Pesquisar(string siglaEstado, string nomeEstado, string pais)
{
Entity.DataClassesDataContext dataClass = new Entity.DataClassesDataContext();
var est = dataClass.SP_PESQUISAR_ESTADO(siglaEstado, nomeEstado, pais);
List<Entity.Estado> estado = est.ToList();
return estado;
}
Assim toda logica de subquerys e subselects estariam dentro da Procedure, fica muito melhor e mais legivel.
Mas mesmo assim, ja estou desenvolvendo a outra alternativa aqui e te mando agorinha, caso não queira usar SPs.
Abraços
Att
Luiz Maia
GOSTEI 0
Luiz Maia
01/07/2009
Ola Carlos,
Conforme te prometi, segue o projeto funcionando. Estou usando o banco de dados NorthWind, que vc pode baixar gratuitamente no site da MircroSoft.
Algumas considerações:
1 - Este é a estrutura do meu arquivo .dbml, que usei no teste:
2 - Criei um DataList que lista as Categorias, alterei a propriedade de Seleção para MultiModo, assim consigo selecionar mais de um item ao mesmo tempo.
3 - Depois de marcar todas as categorias que quero que sejam listados os produtos, faço o binding no GrigView:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
private int[] RecuperarCategoriasSelecao()
{
int[] codigos = new int[ListBox1.Items.Count];
int cont = 0;
foreach (ListItem item in ListBox1.Items)
{
if (item.Selected)
{
codigos[cont] = Convert.ToInt32(item.Value);
cont++;
}
}
return codigos;
}
public List<Product> RetornaDados()
{
DataClassesDataContext dataClass = new DataClassesDataContext();
int[] codCategoriasSelecao = RecuperarCategoriasSelecao();
var prod = from p in dataClass.Products
where codCategoriasSelecao.Contains((int)p.CategoryID)
select p;
List<Product> produtos = prod.ToList();
return produtos;
}
protected void Button1_Click(object sender, EventArgs e)
{
GridView1.DataSource = RetornaDados();
GridView1.DataBind();
}
}
O codigo completo vc pode baixar aqui:
https://www.devmedia.com.br/imagens/discovirtual/200237/Consultoria/LinqComSubSelect.zip
Veja o resultado funcionando abaixo, repare que filtrei pelas categorias Bevereges e Confections, com Ids 1 e 3 respectivamente, pode conferir na grid na coluna CategoryId:
Espero ter ajudado.
Abraços
Att
Luiz Maia
GOSTEI 0
Luiz Maia
01/07/2009
Ola Carlos,
Como esta indo?
Esta conseguindo implementar?
Aguardo um retorno.
Abraços
Att
Luiz Maia
GOSTEI 0
Carlos Nogueira
01/07/2009
Oi!
Eu implementei, o código ficou como você pode visualizar abaixo:
private int[] ObterCodigosSelecao(List<PessoaStatus> pPessoaStatus)
{
int[] _codigos = new int[pPessoaStatus.Count];
int _cont = 0;
foreach (PessoaStatus item in pPessoaStatus)
{
_codigos[_cont] = item.IdStatusPessoa;
_cont++;
}
return _codigos;
}
public List<PessoaDeficiencia> SelecionarDeficienteFisico(List<PessoaStatus> pPessoaStatus)
{
List<PessoaDeficiencia> listPessoaDeficiencia = new List<PessoaDeficiencia>();
try
{
int[] _codStatusSelecao = ObterCodigosSelecao(pPessoaStatus);
var consulta = from pessoadeficiencia in _context.tab_pessoa_deficiencia
where (pessoadeficiencia.tab_pessoa.possui_deficiencia == "S")
&& (pessoadeficiencia.tab_pessoa.tab_tipo_contratacao.c_def_fisico == "S")
&& (pessoadeficiencia.tab_pessoa.tab_pessoa_status.id_status_pessoa != 2)
&& ((0 == pPessoaStatus[0].IdStatusPessoa)
|| (_codStatusSelecao.Contains(pessoadeficiencia.tab_pessoa.tab_pessoa_status.id_status_pessoa)))
select new
{
pessoadeficiencia.id_pessoa_deficiencia,
pessoadeficiencia.tab_pessoa.Id_pessoa,
pessoadeficiencia.tab_tipo_deficiente_fisico.id_tipo_deficiencia,
pessoadeficiencia.path
};
var listConsulta = consulta.ToList();
foreach (var list in listConsulta)
{
PessoaDeficiencia pessoadeficiencia = new PessoaDeficiencia();
pessoadeficiencia.IdPessoaDeficiencia = list.id_pessoa_deficiencia;
pessoadeficiencia.Pessoa = new Pessoa(list.Id_pessoa);
pessoadeficiencia.TipoDeficienteFisico = new TipoDeficienteFisico(list.id_tipo_deficiencia);
pessoadeficiencia.Path = list.path;
listPessoaDeficiencia.Add(pessoadeficiencia);
}
return listPessoaDeficiencia;
}
catch (Exception ex)
{
throw ex;
}
}
No método SelecionarDeficienteFisico, ao passar pela linha "varlisConsulta = consulta.ToList()" ele gerou uma exceção com a seguinte mensagem:
"LINQ to Entities não reconhece o método 'SMA.GENTEv1.PessoaStatus get_Item(Int32)', que não pode ser convertido em uma expressão de armazenamento."
O que você acha que pode ser, ou o que devo fazer para contornar isso?
Eu implementei, o código ficou como você pode visualizar abaixo:
private int[] ObterCodigosSelecao(List<PessoaStatus> pPessoaStatus)
{
int[] _codigos = new int[pPessoaStatus.Count];
int _cont = 0;
foreach (PessoaStatus item in pPessoaStatus)
{
_codigos[_cont] = item.IdStatusPessoa;
_cont++;
}
return _codigos;
}
public List<PessoaDeficiencia> SelecionarDeficienteFisico(List<PessoaStatus> pPessoaStatus)
{
List<PessoaDeficiencia> listPessoaDeficiencia = new List<PessoaDeficiencia>();
try
{
int[] _codStatusSelecao = ObterCodigosSelecao(pPessoaStatus);
var consulta = from pessoadeficiencia in _context.tab_pessoa_deficiencia
where (pessoadeficiencia.tab_pessoa.possui_deficiencia == "S")
&& (pessoadeficiencia.tab_pessoa.tab_tipo_contratacao.c_def_fisico == "S")
&& (pessoadeficiencia.tab_pessoa.tab_pessoa_status.id_status_pessoa != 2)
&& ((0 == pPessoaStatus[0].IdStatusPessoa)
|| (_codStatusSelecao.Contains(pessoadeficiencia.tab_pessoa.tab_pessoa_status.id_status_pessoa)))
select new
{
pessoadeficiencia.id_pessoa_deficiencia,
pessoadeficiencia.tab_pessoa.Id_pessoa,
pessoadeficiencia.tab_tipo_deficiente_fisico.id_tipo_deficiencia,
pessoadeficiencia.path
};
var listConsulta = consulta.ToList();
foreach (var list in listConsulta)
{
PessoaDeficiencia pessoadeficiencia = new PessoaDeficiencia();
pessoadeficiencia.IdPessoaDeficiencia = list.id_pessoa_deficiencia;
pessoadeficiencia.Pessoa = new Pessoa(list.Id_pessoa);
pessoadeficiencia.TipoDeficienteFisico = new TipoDeficienteFisico(list.id_tipo_deficiencia);
pessoadeficiencia.Path = list.path;
listPessoaDeficiencia.Add(pessoadeficiencia);
}
return listPessoaDeficiencia;
}
catch (Exception ex)
{
throw ex;
}
}
No método SelecionarDeficienteFisico, ao passar pela linha "varlisConsulta = consulta.ToList()" ele gerou uma exceção com a seguinte mensagem:
"LINQ to Entities não reconhece o método 'SMA.GENTEv1.PessoaStatus get_Item(Int32)', que não pode ser convertido em uma expressão de armazenamento."
O que você acha que pode ser, ou o que devo fazer para contornar isso?
GOSTEI 0
Luiz Maia
01/07/2009
Ola Carlos,
Tente algo do tipo:
if (consulta.Count() == 1)
{
pessoadeficiencia.IdPessoaDeficiencia = consulta.Single().id_pessoa_deficiencia;
// e assim por diante... } Abraços E aguardo retorno, ok? Att Luiz Maia
// e assim por diante... } Abraços E aguardo retorno, ok? Att Luiz Maia
GOSTEI 0
Carlos Nogueira
01/07/2009
Oi Luiz!
Efetuei o teste, mas gerou a mesma exceção citada na mensagem anterior. E com o foreach neste método já consigo tratar o resultado da consulta se tiver apenas um registro, vários registros ou nenhum registro, ao invés de efetuar tratamento com o if.
Mas bem, estou enviando a mensagem que gerou a mesma exceção ainda. Quando foi passar na linha que você sugeriu, "if (consulta.Count() == 1)", ocorreu a exceção.
Efetuei o teste, mas gerou a mesma exceção citada na mensagem anterior. E com o foreach neste método já consigo tratar o resultado da consulta se tiver apenas um registro, vários registros ou nenhum registro, ao invés de efetuar tratamento com o if.
Mas bem, estou enviando a mensagem que gerou a mesma exceção ainda. Quando foi passar na linha que você sugeriu, "if (consulta.Count() == 1)", ocorreu a exceção.
GOSTEI 0
Luiz Maia
01/07/2009
Ola,
tente fazer o seguinte, retirar a linha que esta dando excessao, e usar direto no foreach:
foreach (var list in Consulta.ToList())
Tente algo assim, e caso de alguma excessao me mande o print da tela por favor, antes tente debugar a aplicação e ver se o list foi populado.
Aguardo seu retorno,
Abraços
Att
Luiz Maia
GOSTEI 0
Carlos Nogueira
01/07/2009
Oi Luiz!
Conforme você havia pedido, eu fiz a alteração no foreach, e ao debugar passando pelo consulta.ToList() ele gera a exceção. Abaixo, vou colocar as imagens que obtive no momento de debugar.
Esta imagem se refere a uma chamada de teste para o método, onde alimente um List com algumas informações para passar para o parâmetro:
Agora esta imagem se refere no momento que obtive os códigos para utilizar no LINQ, aquele exemplo que você me passou para obter de forma dinâmica:
Esta imagem se refere a exceção que foi gerada conforma havia comentado acima, quando passa pela instrução consulta.ToList():
E decide colocar também esta imagem do quick watch em relação aa variável consulta após ter passado pela sintaxe do LINQ:
Bem, essas são as imagens que capturei para lhe enviar. Fico no aguardo de um retorno. Obrigado!
Conforme você havia pedido, eu fiz a alteração no foreach, e ao debugar passando pelo consulta.ToList() ele gera a exceção. Abaixo, vou colocar as imagens que obtive no momento de debugar.
Esta imagem se refere a uma chamada de teste para o método, onde alimente um List com algumas informações para passar para o parâmetro:
Agora esta imagem se refere no momento que obtive os códigos para utilizar no LINQ, aquele exemplo que você me passou para obter de forma dinâmica:
Esta imagem se refere a exceção que foi gerada conforma havia comentado acima, quando passa pela instrução consulta.ToList():
E decide colocar também esta imagem do quick watch em relação aa variável consulta após ter passado pela sintaxe do LINQ:
Bem, essas são as imagens que capturei para lhe enviar. Fico no aguardo de um retorno. Obrigado!
GOSTEI 0
Luiz Maia
01/07/2009
Ola Carlos,
Realmente o que esta querendo fazer nao funcionado para o Linq to Entities, mas funciona para Linq to SQL, acordo com o proprio pessoal da Microsoft:
Diogo Vega faz parte do ADO.Net Team, ele mandou a seguinte explanação sobre a questão do L2E:
"By design, LINQ to Entities requires the whole LINQ query expression to be translated to a server query. Only a few uncorrelated subexpressions (expressions in the query that do not depend on the results from the server) are evaluated on the client before the query is translated. Arbitrary method invocations that do not have a known translation, like GetProducts() in this case, are not supported."
Para maiores informações vc pode consultar o site:
http://mosesofegypt.net/post/2008/08/24/LINQ-to-Entities-what-is-not-supported.aspx
Cara, L2E é ruim demais... É muito engessado, e dependendo do tamanho do projeto, é altamente recomendado não usar L2E, use pelo menos L2Sql.
"LINQ to Entities only support Parameterless constructors and Initializers".
O que sugiro fazer, é voltar ao inicio de nossa conversa, faça uma sub-query mesmo.
Qualquer coisa, me avise ok?
Mas de qualquer forma a questão o IN Query Sql foi resolvido, ne?
Testei aqui e funcionou perfeitamente, como vc mesmo pode ver no projeto que me enviei.
Abraços e até mais.
Aguardo seu retorno.
Att
Luiz Maia
GOSTEI 0
Carlos Nogueira
01/07/2009
Oi Luiz!
Então, antes de eu entrar em contato com vocês a respeito deste problema, já havia feito uma pesquisa para ver o que poderia encontrar de solução, e até havia encontrado uma que alguém desenvolveu um método que cria uma expressão para ser utilizada no método Where dos objetos do context. Só que antes de fazer alguns testes com isso que encontrei, decidi enviar o problema para vocês pois com o expertise que vocês possuem, talvez soubessem de algo mais simples ou mais eficaz.
Eu consegui solucionar o problema fazendo uso do linq to entities conforme o código que segue abaixo:
private static Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
{
if (null == valueSelector)
{
throw new
ArgumentNullException("valueSelector");
}
if (null == values) { throw new ArgumentNullException("values"); }
ParameterExpression p = valueSelector.Parameters.Single();
if (!values.Any())
{
return e => false;
}
var equals = values.Select(value =>
(Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value,
typeof(TValue))));
var body = equals.Aggregate<Expression>((accumulate, equal) =>
Expression.Or(accumulate, equal));
return Expression.Lambda<Func<TElement, bool>>(body, p);
}
public List<Pessoa> SelecionarDeficienteFisico(List<PessoaStatus> pPessoaStatus)
{
List<Pessoa> listPessoaDeficiencia = new List<Pessoa>();
try
{
int[] _codStatusSelecao = ObterCodigosSelecao(pPessoaStatus);
var consulta = from pessoa in (_codStatusSelecao[0] != 0) ? _context.tab_pessoa.Where((BuildContainsExpression<tab_pessoa, int>(item => item.tab_pessoa_status.id_status_pessoa, _codStatusSelecao))) : _context.tab_pessoa
where (pessoa.possui_deficiencia == "S")
&& (pessoa.tab_tipo_contratacao.c_def_fisico == "X")
&& (pessoa.tab_pessoa_status.id_status_pessoa != 2)
select new
{
pessoa.Id_pessoa,
pessoa.nome_completo,
pessoa.matricula,
pessoa.dt_admissao,
pessoa.tab_tipo_deficiente_fisico.id_tipo_deficiencia,
pessoa.tab_pessoa_status.id_status_pessoa,
pessoa.tab_tipo_contratacao.Id_tipo_contratacao,
pessoa.tab_organizacao.Id_organizacao
};
var listConsulta = consulta.ToList();
foreach (var list in listConsulta)
{
Pessoa pessoa = new Pessoa();
pessoa.IdPessoa = list.Id_pessoa;
pessoa.NomeCompleto = list.nome_completo;
pessoa.Matricula = list.matricula;
pessoa.DtAdmissao = list.dt_admissao;
pessoa.TipoDeficienteFisico = new TipoDeficienteFisico(list.id_tipo_deficiencia);
pessoa.TipoContratacao=new TipoContratacao(list.Id_tipo_contratacao);
pessoa.Organizacao=new Organizacao(list.Id_organizacao);
listPessoaDeficiencia.Add(pessoa);
}
return listPessoaDeficiencia;
}
catch (Exception ex)
{
throw ex;
}
}
Então, no que havia encontrado de pesquisa, o cara criou este método chamado BuildContainsExpression, que ao passar no método Where do objeto, permite eu simular a palavra-chave IN do SELECT no linq to entities. Então, acrescentei mais uma condição na sintaxe do linq para que quando o primeiro índice do array for igual a 0, desconsiderar o método Where. Isso é para aquilo que havia mencionado anteriormente (eu acho) de quando o usuário selecionar a opção todos, tentar resolver tudo em uma sintaxe só, ao invés de ficar fazendo if ou switch no código.
Se você quiser acessar o linq desta pesquisa para comentar o que pensa a respeito, é http://www.velocityreviews.com/forums/t645784-linq-where-clause.html
Agora, você comentou que conforme o porte do projeto, é altamente recomendável não utilizar o linq to entities. O que deu a entender, poderia usar o linq to sql e olha lá. Bem, você poderia me explicar melhor o do porque disso?
O local onde trabalho atualmente eles persistem os dados desta forma (entrei aqui a pouco tempo, 2 semana e pouco, e antes trabalhava persistindo os dados com o ADO.NET usando transact-sql nas classes, conforme política da outra empresa), mas conforme o que você me passar de informações ou referências sobre este assunto (fora o que você já mencionou a citação da equipe da Microsoft), posso tentar apresentar para a equipe. O projeto vai aumentar bastante de tamanho por aqui.
Fora este problema com o Contains (para simular o IN do SELECT) nas demais formas que precisei utilizar o linq to entities foi tranquilo e estou até achando interessante o aprendizado e a experiência que estou adquirindo com ele. O que você aconselharia nestes casos para persistir os dados, ou o que o mercado tem usado para persistir os dados? Linq to SQL, NHibernate, ADO.NET nas classes, store procedures no banco, .....
Fico no aguardo!
Atenciosamente,
Carlos
Então, antes de eu entrar em contato com vocês a respeito deste problema, já havia feito uma pesquisa para ver o que poderia encontrar de solução, e até havia encontrado uma que alguém desenvolveu um método que cria uma expressão para ser utilizada no método Where dos objetos do context. Só que antes de fazer alguns testes com isso que encontrei, decidi enviar o problema para vocês pois com o expertise que vocês possuem, talvez soubessem de algo mais simples ou mais eficaz.
Eu consegui solucionar o problema fazendo uso do linq to entities conforme o código que segue abaixo:
private static Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
{
if (null == valueSelector)
{
throw new
ArgumentNullException("valueSelector");
}
if (null == values) { throw new ArgumentNullException("values"); }
ParameterExpression p = valueSelector.Parameters.Single();
if (!values.Any())
{
return e => false;
}
var equals = values.Select(value =>
(Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value,
typeof(TValue))));
var body = equals.Aggregate<Expression>((accumulate, equal) =>
Expression.Or(accumulate, equal));
return Expression.Lambda<Func<TElement, bool>>(body, p);
}
public List<Pessoa> SelecionarDeficienteFisico(List<PessoaStatus> pPessoaStatus)
{
List<Pessoa> listPessoaDeficiencia = new List<Pessoa>();
try
{
int[] _codStatusSelecao = ObterCodigosSelecao(pPessoaStatus);
var consulta = from pessoa in (_codStatusSelecao[0] != 0) ? _context.tab_pessoa.Where((BuildContainsExpression<tab_pessoa, int>(item => item.tab_pessoa_status.id_status_pessoa, _codStatusSelecao))) : _context.tab_pessoa
where (pessoa.possui_deficiencia == "S")
&& (pessoa.tab_tipo_contratacao.c_def_fisico == "X")
&& (pessoa.tab_pessoa_status.id_status_pessoa != 2)
select new
{
pessoa.Id_pessoa,
pessoa.nome_completo,
pessoa.matricula,
pessoa.dt_admissao,
pessoa.tab_tipo_deficiente_fisico.id_tipo_deficiencia,
pessoa.tab_pessoa_status.id_status_pessoa,
pessoa.tab_tipo_contratacao.Id_tipo_contratacao,
pessoa.tab_organizacao.Id_organizacao
};
var listConsulta = consulta.ToList();
foreach (var list in listConsulta)
{
Pessoa pessoa = new Pessoa();
pessoa.IdPessoa = list.Id_pessoa;
pessoa.NomeCompleto = list.nome_completo;
pessoa.Matricula = list.matricula;
pessoa.DtAdmissao = list.dt_admissao;
pessoa.TipoDeficienteFisico = new TipoDeficienteFisico(list.id_tipo_deficiencia);
pessoa.TipoContratacao=new TipoContratacao(list.Id_tipo_contratacao);
pessoa.Organizacao=new Organizacao(list.Id_organizacao);
listPessoaDeficiencia.Add(pessoa);
}
return listPessoaDeficiencia;
}
catch (Exception ex)
{
throw ex;
}
}
Então, no que havia encontrado de pesquisa, o cara criou este método chamado BuildContainsExpression, que ao passar no método Where do objeto, permite eu simular a palavra-chave IN do SELECT no linq to entities. Então, acrescentei mais uma condição na sintaxe do linq para que quando o primeiro índice do array for igual a 0, desconsiderar o método Where. Isso é para aquilo que havia mencionado anteriormente (eu acho) de quando o usuário selecionar a opção todos, tentar resolver tudo em uma sintaxe só, ao invés de ficar fazendo if ou switch no código.
Se você quiser acessar o linq desta pesquisa para comentar o que pensa a respeito, é http://www.velocityreviews.com/forums/t645784-linq-where-clause.html
Agora, você comentou que conforme o porte do projeto, é altamente recomendável não utilizar o linq to entities. O que deu a entender, poderia usar o linq to sql e olha lá. Bem, você poderia me explicar melhor o do porque disso?
O local onde trabalho atualmente eles persistem os dados desta forma (entrei aqui a pouco tempo, 2 semana e pouco, e antes trabalhava persistindo os dados com o ADO.NET usando transact-sql nas classes, conforme política da outra empresa), mas conforme o que você me passar de informações ou referências sobre este assunto (fora o que você já mencionou a citação da equipe da Microsoft), posso tentar apresentar para a equipe. O projeto vai aumentar bastante de tamanho por aqui.
Fora este problema com o Contains (para simular o IN do SELECT) nas demais formas que precisei utilizar o linq to entities foi tranquilo e estou até achando interessante o aprendizado e a experiência que estou adquirindo com ele. O que você aconselharia nestes casos para persistir os dados, ou o que o mercado tem usado para persistir os dados? Linq to SQL, NHibernate, ADO.NET nas classes, store procedures no banco, .....
Fico no aguardo!
Atenciosamente,
Carlos
GOSTEI 0
Carlos Nogueira
01/07/2009
Luiz, acessei o link que você me passou e li sobre a experiência do cara fazendo comparação do linq to entities com o linq to sql, realmente muito interessante.
Na forma como ele estruturou o projeto dele, se futuramente ele desejar trocar a tecnologia (de linq to sql para linq to entities) ele só vai precisar se preocupar em atualizar sua camada de repositório, não é?
Pergunto porque aqui no projeto tem uma camada de negócio, onde irá se encontrar a regra de negócio para as respectivas classes, mas também nesta mesma camada está fazendo uso do linq, e eu acho que se estivesse em uma outra camada, ficaria melhor estuturado para manutenção. Estou certo neste pensamento? Pretendo também passar isso para a equipe de desenvolvimento aqui a respeito, só precisaria argumentar de forma correta para eles aceitarem.
Ah, mas também ainda continuo querendo saber sua opinião referente a pergunta que lhe fiz no post anterior.
Abraços, até mais!!
Na forma como ele estruturou o projeto dele, se futuramente ele desejar trocar a tecnologia (de linq to sql para linq to entities) ele só vai precisar se preocupar em atualizar sua camada de repositório, não é?
Pergunto porque aqui no projeto tem uma camada de negócio, onde irá se encontrar a regra de negócio para as respectivas classes, mas também nesta mesma camada está fazendo uso do linq, e eu acho que se estivesse em uma outra camada, ficaria melhor estuturado para manutenção. Estou certo neste pensamento? Pretendo também passar isso para a equipe de desenvolvimento aqui a respeito, só precisaria argumentar de forma correta para eles aceitarem.
Ah, mas também ainda continuo querendo saber sua opinião referente a pergunta que lhe fiz no post anterior.
Abraços, até mais!!
GOSTEI 0
Luiz Maia
01/07/2009
Ola Carlos,
Esta questão de mercado é muito complexa. Tem empresas que usam todos os tipos de acesso a dados, inclusive L2E, L2Sql, ADO.net NHibernte e outros... Isto depende de cada metodologia adotada pela empresa. Empresas de meio a grande porte geralmente tem seu proprio Framework. Atualmente presto consultoria do Banco Mercantil e la eles construiram um Framework baseado nos Enterprise Library da MS. Eu, particularmente eu meus projetos, tenho um mini-framework que construi para integraçao com banco de dados, é uma DLL que chamo de superclasse, tem transacoes, controle de conexoes e etc, é bem simples mas pra mim funciona perfeitamente, e é totalmente flexivel, ao contrario de linq, edm e outros, que sao engessados.
Minha sugestao para vc é a seguinte, trabalhar com 3 classes, a interface, negocios e persistencia, na persistencia use SPs, tem um ganho de performance enorme, ja que sao pre compiladas.
"What is the difference between LINQ to SQL and LINQ to Entities? " (Mike Pizzo (Architect, Data Programmability) 04/2007:)
LINQ to SQL supports rapid development of applications that query Microsoft SQL Server databases using objects that map directly to SQL Server schemas. LINQ to Entities supports more flexible mapping of objects to Microsoft SQL Server and other relational databases through extended ADO.NET Data Providers."
Veja a seguir a comparação de algumas características entre os dois LINQs :
Característica
LINQ to SQL
LINQ to Entities
Language Extensions Support
Sim
Sim
Language Integrated Database Queries
Sim
Sim
Many-to-Many (3way Join/Payload relationship)
Não
Não
Many-to-Many
Não
Sim
Stored Procedures
Sim
Não
Entity Inheritance
Não
Sim
Single Entity from Multiple Tables
Não
Sim
Identity Management / CRUD features
Sim
Sim
Você pode usar o ADO .NET para o acesso a dados, e muitas vezes esse é o caminho mais recomendado, ou, se a situação mostrar que a utilização de um mapeamento objeto relacional for mais adequado (verifique o desempenho) , pode usar o LINQ sem problemas.
Espero ter ajudado.
Abraços
Att
Luiz Maia
GOSTEI 0
Carlos Nogueira
01/07/2009
Aqui no projeto tem um Class Library que só contém o data model, e na camada de negócios além das regras também está incluso as instruções de linq? Você acha que isso está legal, ou essas instruções deveriam ir para junto do data model, ou deveria ter mais uma camada somente para isso, isto, só com essas instruções?
GOSTEI 0
Luiz Maia
01/07/2009
Tá legal sim, vou te mandar abaixo a estrutura do ultimo projeto que fiz usando link:
Veja acima minha a estrutura que usei,
1 - A pasta Entities é uma ClassLibrary somente com o dbml. (parecido com o que vc fez)
2 - Framework é a camada de Negocios (Bussines Logical Layer)
3 - Gestao Web é a interface, a aplicação Web em si.
4 - Persistence é a camada de persistencia ou acesso a dados(data access layer)
Vou madar abaixo os as chamadas dos metodos de popular um controle com os dados de "Estado" (MG, RJ....) por exemplo, ok?
1 - Interface:
gridResultado.DataSource = BusinessLogicLayerComponent.Estado.pesquisar(txtSigla.Text, txtNome.Text, "");
gridResultado.DataBind();
2 - Negocios:
public static List<Entity.Estado> pesquisar(string siglaEstado, string nomeEstado, string pais)
{
return DataAcessLayerComponent.Estado.Pesquisar(siglaEstado, nomeEstado, pais);
}
3 - Dados:
public static List<Entity.Estado> Pesquisar(string siglaEstado, string nomeEstado, string pais)
{
Entity.DataClassesDataContext dataClass = new Entity.DataClassesDataContext();
var est = dataClass.SP_PESQUISAR_ESTADO(siglaEstado, nomeEstado, pais);
List<Entity.Estado> estado = est.ToList();
return estado;
}
obs: Repare que faço uso de Stored Procedures.
Veja abaixo o dbml, ja populado com as SPs:
Acho que este foi ultimo projeto que fiz usando Linq.
Hj em dia prefiro usar meu proprio Framework ( desenvolvi baseado no Enterprose Library), tenho um ganho de performance sem comparação, desenvolvo muito mais rapido e muito mais facil pra dar manutenção.
Mas fica acima, minha sugestão de estrutura para um projeto 3 camadas com Linq, blz?
Esta bem parecido mesmo com o que ja esta fazendo.
Abraços
Att
Luiz Maia
GOSTEI 0
Carlos Nogueira
01/07/2009
Beleza, sendo assim, eu vou finalizar o atendimento, obrigado pelas dicas e troca de informação sobre o problema no linq.
Abraços
Atenciosamente,
Carlos
Abraços
Atenciosamente,
Carlos
GOSTEI 0
Luiz Maia
01/07/2009
Blz Carlos,
Precisando é so dizer, continuamos a sua disposição para qualquer tipo de dúvidas, ok?
Abraços
Att
Luiz Maia
GOSTEI 0