Fórum Uso do LINQ para vários códigos #7233
01/07/2009
0
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
Curtir tópico
+ 0Posts
01/07/2009
Carlos Nogueira
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
01/07/2009
Luiz 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
02/07/2009
Carlos Nogueira
Gostei + 0
02/07/2009
Luiz Maia
Gostei + 0
02/07/2009
Carlos Nogueira
Gostei + 0
02/07/2009
Luiz Maia
Gostei + 0
02/07/2009
Luiz Maia
Gostei + 0
04/07/2009
Luiz Maia
Gostei + 0
06/07/2009
Carlos Nogueira
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
06/07/2009
Luiz Maia
// e assim por diante... } Abraços E aguardo retorno, ok? Att Luiz Maia
Gostei + 0
06/07/2009
Carlos Nogueira
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
06/07/2009
Luiz Maia
Gostei + 0
07/07/2009
Carlos Nogueira
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
07/07/2009
Luiz Maia
Gostei + 0
08/07/2009
Carlos Nogueira
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
Clique aqui para fazer login e interagir na Comunidade :)