Grupo Linq para Entidades por (EXTERIOR APLICAR) "oracle 11.2.0.3.0 não suporta aplicação"
Eu tenho o exemplo de código abaixo, que consulta uma lista de produtos.
var productResults = Products.Where((p) => refFilterSequence.Contains(p.Ref))
.GroupBy(g => g.Code, (key, g) => g.OrderBy(whp => whp.Ref).First()).ToList();
Isso funciona exatamente como o esperado e retorna as 4 linhas desejadas ao usar uma coleção na memória, mas ao executar no banco de dados Oracle:
.GroupBy(g => g.Code, (key, g) => g.OrderBy(whp => whp.Ref).First())
Isso gera um erro dizendo que eu deveria usarFirstOrDefault
que não é suportado em um banco de dados Oracle. O errooracle 11.2.0.3.0 não suporta aplicar é jogado. O Googleing revela isso no CodePlex:https://entityframework.codeplex.com/workitem/910.
Isso ocorre ao usar os seguintes binários:
EntityFramework 6.0.0.0Oracle.ManagedDataAccess 4.121.2.0Oracle.ManagedDataAccess.EntityFramework 6.121.2.0.Net Framework 4.5.1O banco de dados é um banco de dados Oracle 11.2.0.3.0.
O sql gerado usa OUTER APPLY (veja a imagem abaixo), que não é suportada pela versão 11.2.0.3.0 do Oracle. Por que o EF / Oracle.ManagedDataAccess está tentando usá-lo? Existe uma maneira de dizer à EF para não usar a palavra-chave APPLY?
A página abaixo diz que o suporte ao APPLY foi adicionado no Oracle 12c Release 1, mas não consigo atualizar todos os meus bancos de dados apenas para fazer um GROUP BY funcionar.http://www.oracle.com/technetwork/database/windows/newfeatures-084113.html
Parece que este é um problema conhecido (Problemas conhecidos no SqlClient for Entity Framework):
A seguir, estão alguns cenários típicos que podem levar à presença de operadores CROSS APPLY e / ou OUTER APPLY na consulta de saída:
Consultas LINQ que usam métodos de agrupamento que aceitam um seletor de elemento.Antes de eu recorrer à criação de uma exibição (eu precisaria criar a exibição em vários bancos de dados), alguém pode ver outra solução?
Para qualquer pessoa interessada, o SQL que faria o que eu quero nessa versão do banco de dados seria algo como o seguinte:
select *
from ( select RANK() OVER (PARTITION BY sm.product ORDER BY refs.map) ranking, sm.*
from schema.table sm,
(
select 'R9' ref, 0 map from dual
union all
select 'R1' ref, 1 map from dual
union all
select 'R6' ref, 2 map from dual
) refs
where sm.ref= refs.ref
) stock
where ranking = 1
O código eventualmente estará em uma classe de serviço passada para o controlador OData na API da Web. O exemplo abaixo usa dados de demonstração, o banco de dados real possui 700.000 registros, portanto, gostaria de evitar a execução da consulta e deixar o OData manipular os limites da página e a filtragem adicional.
using System;
using System.Collections.Generic;
using System.Linq;
namespace DemoApp
{
class Program
{
public class Product
{
public string Ref { get; set; }
public string Code { get; set; }
public int Quantity { get; set; }
}
//demo data
static readonly List<Product> Products = new List<Product>
{
new Product { Ref = "B2", Code = "ITEM1", Quantity = 1},
new Product { Ref = "R1", Code = "ITEM1", Quantity = 2},
new Product { Ref = "R9", Code = "ITEM1", Quantity = 3},
new Product { Ref = "R9", Code = "ITEM2", Quantity = 4},
new Product { Ref = "R6", Code = "ITEM2", Quantity = 5},
new Product { Ref = "B2", Code = "ITEM3", Quantity = 6},
new Product { Ref = "R1", Code = "ITEM3", Quantity = 7},
new Product { Ref = "R9", Code = "ITEM3", Quantity = 8},
new Product { Ref = "B2", Code = "ITEM4", Quantity = 9},
new Product { Ref = "X3", Code = "ITEM4", Quantity = 10},
new Product { Ref = "B8", Code = "ITEM5", Quantity = 10},
new Product { Ref = "R6", Code = "ITEM5", Quantity = 12},
new Product { Ref = "M2", Code = "ITEM5", Quantity = 13},
new Product { Ref = "R1", Code = "ITEM5", Quantity = 14},
};
static void Main(string[] args)
{
// this array is of variable length, and will not always contain 3 items.
var refFilterSequence = new List<string> {"R9", "R1", "R6"};
var results = GetProductsForODataProcessing(refFilterSequence);
// some further filtering may occur after the queryable is returned.
// the actual implmentation is an OData Web API, so filters, expansions etc could be added.
//results = results.Where(p => p.Quantity > 2);
results.ToList().ForEach(p => Console.WriteLine("RANK:{0}\tREF:{1}\tCode:{2}\tQty:{3}", "?", p.Ref, p.Code, p.Quantity));
Console.ReadLine();
}
static IQueryable<Product> GetProductsForODataProcessing(List<string> filterSequence )
{
var productResults = Products.Where((p) => filterSequence.Contains(p.Ref))
.GroupBy(g => g.Code, (key, g) => g.OrderBy(whp => whp.Ref).First()).AsQueryable();
return productResults;
}
}
// Example Output
// .......................
// REF:R1 Code:ITEM1 Qty:2
// REF:R6 Code:ITEM2 Qty:3
// REF:R1 Code:ITEM3 Qty:7
// REF:R1 Code:ITEM5 Qty:14