Select simples mais perigoso
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


Daniel, acredito que você pode resolver isso utilizando utilizando extension methods e/ou expressions, de forma tipada e possivelmente mais performatica.
Ola Victor,
Sim é possível seria parecido com isso
//http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/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
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
buy@generic.LEVITRA” rel=”nofollow”>……
Need cheap generic LEVITRA?…