t
  • Entreguei o primeiro projeto em #WRT gastei mais tempo configurando o ambiente do que desenvolvendo! A #nokia poderia melhorar isso..

Posts recentes

Comentários recentes

Select simples mais perigoso

Postado por Daniel Fonseca Castro - Friday, June 11, 2010 10:38 PM

Sabe aquele obstáculo que parece ser a coisa mais simples do mundo?

Pois é! Em um novo projeto com EF 1.0 surgiu uma necessidade simples, fazer um select utilizando Linq to Entites que retornasse todos os ids de um array de inteiro , como no exemplo abaixo:

static void Main(string[] args)
{
    NorthwindEntities context = new NorthwindEntities();
    int[] ids = new int[] { 1, 2, 3 };
    var resultado = from product in context.Products
                    where ids.Contains(product.ProductID)
                    select product;

    foreach (var item in resultado)
        Console.WriteLine("Nome: {0}", item.ProductName);

    Console.Read();
}

Super simples, esse código inclusive funciona perfeitamente no L2Q, mais no EF 1.0 uma exception do tipo NotSupportedException é lançada com a seguinte mensagem de erro:

"LINQ to Entities não reconhece o método 'Boolean Contains[Int32](System.Collections.Generic.IEnumerable`1[System.Int32], Int32)', que não pode ser convertido em uma expressão de armazenamento."

Fui pesquisar como resolver esse problema e a maioria das soluções que encontrei "cheirava mal", e as que não tinha cheiro nenhum eram complicadas demais para um problema tão pontual, quase todas as soluções seguia a lógica abaixo , com pouquíssimas variações.

static void Main(string[] args)
{
    NorthwindEntities context = new NorthwindEntities();
    int[] ids = new int[] { 1, 2, 3 };
    var resultado = from product in context.Products.AsEnumerable()
                    where ids.Contains(product.ProductID)
                    select product;

    foreach (var item in resultado)
        Console.WriteLine("Nome: {0}", item.ProductName);

    Console.Read();
}

Note a presença do método AsEnumerable() isso resolveu o problema, mais conversando com outra pessoa questionei que esse código provavelmente trazia todos os dados da tabela para depois aplicar o filtro.

Utilize uma ferramenta open source http://code.google.com/p/sqlexpressprofiler/ para verifica se a afirmação estava correta, veja abaixo a consulta SQL gerada.

SELECT 1 AS [C1], 
[Extent1].[ProductID] AS [ProductID], 
[Extent1].[ProductName] AS [ProductName], 
[Extent1].[QuantityPerUnit] AS [QuantityPerUnit], 
[Extent1].[UnitPrice] AS [UnitPrice], 
[Extent1].[UnitsInStock] AS [UnitsInStock], 
[Extent1].[UnitsOnOrder] AS [UnitsOnOrder], 
[Extent1].[ReorderLevel] AS [ReorderLevel], 
[Extent1].[Discontinued] AS [Discontinued], 
[Extent1].[CategoryID] AS [CategoryID], 
[Extent1].[SupplierID] AS [SupplierID]
FROM [dbo].[Products] AS [Extent1]

Para tudo! Select sem where e pedi para DBA pegar no seu pé e "tacar o pau" em todos ORMs do mundo!

Eu resolvi esse problema utilizando Entity SQL, veja o código abaixo:

static void Main(string[] args)
{
    NorthwindEntities context = new NorthwindEntities();
    var resultado = context.Products.Where("it.ProductID IN {1,2,3}");
   
    foreach (var item in resultado)
        Console.WriteLine("Nome: {0}", item.ProductName);

    Console.Read();
}

Talvez a minha solução não seja a mais adequada mais com certeza o select será gerado de forma correta, para finalizar eu tenho uma boa noticia nada disso é problema para o EF 4, então corra para VS 2010!

Daniel Fonseca Castro

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: Entity Framework

Related posts

  • Victor Arias
    Victor Arias
    12 Jun 2010 1:46 AM
    Daniel, acredito que você pode resolver isso utilizando utilizando extension methods e/ou expressions, de forma tipada e possivelmente mais performatica.
  • Daniel Fonseca Castro
    Daniel Fonseca Castro
    12 Jun 2010 11:50 PM
    Ola Victor,

    Sim é possível seria parecido com isso

    //social.msdn.microsoft.com/.../095745fe-dcf0-4142-b684-b7e4a1ab59f0
    public static Expression<Func<TElement, bool>> WhereWithIn<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);
    }

    Sobre a performance eu não sei se isso é mais rapido , mais o select executado no banco é o mesmo!

    Abraços,
    Daniel Fonseca Castro
  • Henrique
    Henrique
    25 Jul 2010 8:33 AM
    Gostei do seu post, realmente temos que tomar certos cuidados ao executar selects. Utilizat traces é sempre válido!

    Henrique Teles
    http://www.bhone.com.br
    Desenvolvimento de Sistemas

Add comment


 

  Country flag