none
Consulta EF com Where montado dinamicamente RRS feed

  • Pergunta

  • Bom dia.

    Fiz uma consulta com ToList e funciona muito bem.  Conforme abaixo:

    public IEnumerable<ColaboradorListarItensView> RetornarColaboradores()
            {
                var data = (from o in _Contexto.DbColaborador
                            where (o.CodigoRegistro == GestorWebUtils.CodigoRegistro)
                            select new ColaboradorListarItensView
                            {
                                Codigo = o.Codigo,
                                Nome = o.Nome,
                                CPFCNPJ = o.CPFCNPJ,
                                EMail = o.EMail,
                                Ativo = o.Ativo,
                                Telefone = o.Telefone
                            }).ToList();
                
                return data.Count > 0 ? data : null;
            }

    Daí eu precisei agora criar uns filtros, opcionais, para dar a consulta.  Pesquisei e achei a Queryable com um where dinamico.

    Assim:

    public IQueryable<ColaboradorListarItensView> 
                RetornarColaboradoresParaCadastro(bool pFiltrarNome, string pNome, 
                                                  bool pFiltrarCPFCNPJ, string pCPFCNPJ,
                                                  bool pFiltrarAtivo, bool pAtivo)
            {
                var data = _Contexto.DbColaborador.AsQueryable();
    
                if (pFiltrarNome == true && pNome.Length > 0)
                    data = data.Where(o => o.Nome.Contains(pNome));
    
                if (pFiltrarCPFCNPJ == true && pCPFCNPJ.Length > 0)
                    data = data.Where(o => o.CPFCNPJ.Contains(pCPFCNPJ));
    
                if (pFiltrarAtivo == true)
                    data = data.Where(o => o.Ativo.Equals(pAtivo));
                        
                var result = (from o in data
                            select new ColaboradorListarItensView
                            {
                                Codigo = o.Codigo,
                                Nome = o.Nome,
                                CPFCNPJ = o.CPFCNPJ,
                                EMail = o.EMail,
                                Ativo = o.Ativo,
                                Telefone = o.Telefone
                            });
    
                return result;
            }

    Até este ponto na compilação não dá erros, o problema é que eu destruo (dispose) o contexto logo após a chamada neste método.  Daí na hora da execução da View, no for each, dá o erro de contexto não presente.

    The operation cannot be completed because the DbContext has been disposed.
    
    Line 56: </thead>
    Line 57: <tbody>
    Line 58: @foreach (var item in Model.Itens)
    Line 59: {
    Line 60:     string Titulo = "Colaborador: " + item.Nome;

    Como devo proceder?  Se eu fizer ToList dá problema de conversão.  Porém se não usar Queryable não consigo fazer um Where dinâmico.

    Obrigado!!!

    segunda-feira, 4 de março de 2013 17:10

Respostas

  • Você não deveria expor um IQueryable para a view pois assim a sua conexão com o banco está sendo feita a partir da view o que é totalmente errado.. 

    você pode fazer um .ToList na hora de retornar o valor:

    return result.ToList();


    http://www.linkedin.com/pub/murilo-kunze/44/191/455

    segunda-feira, 4 de março de 2013 17:13

Todas as Respostas

  • Você não deveria expor um IQueryable para a view pois assim a sua conexão com o banco está sendo feita a partir da view o que é totalmente errado.. 

    você pode fazer um .ToList na hora de retornar o valor:

    return result.ToList();


    http://www.linkedin.com/pub/murilo-kunze/44/191/455

    segunda-feira, 4 de março de 2013 17:13
  • Perfeito!  Obrigado

    Ainda aprendendo com EF e Linq.

    segunda-feira, 4 de março de 2013 17:27
  • Mas houve outro problema.

    Meu sistema é multi empresa e no usuário eu guardo, em uma variábel de ambiente com propriedades static, as empresas que ele tem permissão.

    Tanto tenho um array de int com por exemplo 1,2,3,4 ou uma string mesmo com "1,2,3,4".

    Preciso que na query acima ele verifique se o campo CodigoRegistro esteja dentro destes valores.  Não está indo com Contains não.

    Veja só:

    data = data.Where(GestorWebUtils.Registros.Concat(o => o.CodigoRegistro));

    Mas dá o erro:

    Error	3	'System.Linq.IQueryable<GestorWebNet.Models.Colaborador>' does not contain a definition for 'Where' and the best extension method overload 'System.Linq.Queryable.Where<TSource>(System.Linq.IQueryable<TSource>, System.Linq.Expressions.Expression<System.Func<TSource,bool>>)' has some invalid arguments	C:\Dropbox\Projetos\GestorWebNet\GestorWebNet\Models\Repository\ColaboradorRepository.cs	27	20	GestorWebNet
    

    Se eu fizer com apenas UM valor funciona, como abaixo:

    var data = (from o in _Contexto.DbColaborador
                            where (o.CodigoRegistro == GestorWebUtils.CodigoRegistro)
                            select new ColaboradorListarItensView
                            {
                                Codigo = o.Codigo,
                                Nome = o.Nome,
                                CPFCNPJ = o.CPFCNPJ,
                                EMail = o.EMail,
                                Ativo = o.Ativo,
                                Telefone = o.Telefone
                            }).ToList();

    Mas com esta verificação em vários valores não tá indo não.

    Alguma ajuda? Obrigado!

    segunda-feira, 4 de março de 2013 17:42
  • Está CONCAT acima mas usei o Contains mesmo.

    E daí tem 2 erros, esqueci e mostrar o segundo:

    Argument 2: cannot convert from 'bool'

    Cannot convert lambda expression to type 'int' because it is not a delegate type

    segunda-feira, 4 de março de 2013 17:43
  • Usa o .Any em vez do .Where.

    http://www.linkedin.com/pub/murilo-kunze/44/191/455

    segunda-feira, 4 de março de 2013 17:46
  • Tentei:

    data = data.Where(GestorWebUtils.Registros.Any(o => o.CodigoRegistro));

    Mas acontece:

    Error	5	'int' does not contain a definition for 'CodigoRegistro' and no extension method 'CodigoRegistro' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?)

    segunda-feira, 4 de março de 2013 17:49
  • Tenta assim:

    data = data.Any(d => suaListaComPermissoes.Contains(o.Valor)));


    http://www.linkedin.com/pub/murilo-kunze/44/191/455

    segunda-feira, 4 de março de 2013 17:51
  • Também não.

    data = data.Any(o => GestorWebUtils.Registros.Contains(o.CodigoRegistro));

    Error	4	'int[]' does not contain a definition for 'Contains' and the best extension method overload 'System.Linq.Queryable.Contains<TSource>(System.Linq.IQueryable<TSource>, TSource)' has some invalid arguments

    segunda-feira, 4 de março de 2013 17:59
  • CodigoRegistro é um int?

    http://www.linkedin.com/pub/murilo-kunze/44/191/455

    segunda-feira, 4 de março de 2013 18:03
  • Sim é um ?int
    segunda-feira, 4 de março de 2013 18:06
  • Assim deu certo:

    data = data.Where(o => GestorWebUtils.ListaRegistros.Contains(SqlFunctions.StringConvert((double)o.CodigoRegistro)));
    Só que a query gerada ficou muito esquisita!!!
    SELECT [Extent1].[col_codigo] AS [col_codigo], [Extent1].[col_nome] AS [col_nome], 
    [Extent1].[col_cpfcnpj] AS [col_cpfcnpj], [Extent1].[col_email] AS [col_email], [Extent1].[col_ativo] AS [col_ativo], [Extent1].[col_telefone] AS [col_telefone]
    
    FROM [dbo].[colaborador] AS [Extent1]WHERE ( CAST(CHARINDEX(STR( CAST( [Extent1].[id_registro] AS float)), @p__linq__0) AS int)) > 0"

    segunda-feira, 4 de março de 2013 18:14
  • To quase desistindo deste modelo pois cada query vai ter que ter isso.  minha idéia é ter um único banco de dados para todos os clientes mas dá um trabalho...
    segunda-feira, 4 de março de 2013 19:29
  • Achei uma forma:

    foreach (int I in GestorWebUtils.Registros)
                    data = data.Where(o => o.CodigoRegistro.Equals(I));

    O problema é que eu precisva que estes Where estivesse entre parenteses na hora de montar a Query
    segunda-feira, 4 de março de 2013 19:46
  • Outra coisa:

    Toda tabela minha tem o campo CodigoRegistro idêntico.  Eu precisva (em PHP fiz isso mole) criar uma funcao tipo MontarWherePadraoRegistro onde eu enviasse a consulta Queryable e ele fizesse isso automaticamente em qualquer select de qualquer tabela.  Eu chamo o método, mas ele, independente do objeto, iria fazer isso.  Tem como?
    segunda-feira, 4 de março de 2013 19:47
  • Consegui:

    public class ColaboradorRepository: RepositoryBase
        {
            public IEnumerable<ColaboradorListarItensView> 
                RetornarColaboradoresParaCadastro(bool pFiltrarNome, string pNome, 
                                                  bool pFiltrarCPFCNPJ, string pCPFCNPJ,
                                                  bool pFiltrarAtivo, bool pAtivo)
            {
                var data = _Contexto.DbColaborador.AsQueryable();
              
                if (pFiltrarNome == true && pNome.Length > 0)
                    data = data.Where(o => o.Nome.Contains(pNome));
    
                if (pFiltrarCPFCNPJ == true && pCPFCNPJ.Length > 0)
                    data = data.Where(o => o.CPFCNPJ.Contains(pCPFCNPJ));
    
                if (pFiltrarAtivo == true)
                    data = data.Where(o => o.Ativo.Equals(pAtivo));
                        
                var result = (from o in data                       
                              where (GestorWebUtils.Registros.Contains(o.CodigoRegistro))
                              select new ColaboradorListarItensView                          
                            {
                                Codigo = o.Codigo,
                                Nome = o.Nome,
                                CPFCNPJ = o.CPFCNPJ,
                                EMail = o.EMail,
                                Ativo = o.Ativo,
                                Telefone = o.Telefone
                            });
    
                return result.ToList();
            }

    Eu fiz o Contains com o Array de INTs (List) mas no select secundário, quando monto o resultado.  Agora o IN ficou perfeito.

    SELECT [Extent1].[col_codigo] AS [col_codigo], [Extent1].[col_nome] AS [col_nome], [Extent1].[col_cpfcnpj] AS [col_cpfcnpj], [Extent1].[col_email] AS [col_email], [Extent1].[col_ativo] AS [col_ativo], [Extent1].[col_telefone] AS [col_telefone]FROM [dbo].[colaborador] AS [Extent1]WHERE [Extent1].[id_registro] IN (1,2,3,4)

    Vlw!
    segunda-feira, 4 de março de 2013 19:59