2012-09-18 15 views
6

Powiel możliwe:
Can SQL level functions be made available to LINQ to Entity queries?Jak korzystać z funkcji wartości skalarnej z linq do encji?

Mam funkcję skalarną, która pobiera odległość między dwoma punktami i chcę go używać do kwerendy najbliższy rekord do punktu. Funkcja skalarna współpracuje z LINQ do SQL, ale nie powiedzie się z EF

funkcja skalarna

USE [GeoData] 
GO 

/****** Object: UserDefinedFunction [dbo].[DistanceBetween] Script Date: 09/18/2012 19:40:44 ******/ 
SET ANSI_NULLS ON 
GO 

SET QUOTED_IDENTIFIER ON 
GO 



CREATE FUNCTION [dbo].[DistanceBetween](@Lat1 as real, 
@Long1 as real, @Lat2 as real, @Long2 as real) 
RETURNS real 
AS 
BEGIN 

DECLARE @dLat1InRad as float(53); 
SET @dLat1InRad = @Lat1; 
DECLARE @dLong1InRad as float(53); 
SET @dLong1InRad = @Long1; 
DECLARE @dLat2InRad as float(53); 
SET @dLat2InRad = @Lat2; 
DECLARE @dLong2InRad as float(53); 
SET @dLong2InRad = @Long2 ; 

DECLARE @dLongitude as float(53); 
SET @dLongitude = @dLong2InRad - @dLong1InRad; 
DECLARE @dLatitude as float(53); 
SET @dLatitude = @dLat2InRad - @dLat1InRad; 
/* Intermediate result a. */ 
DECLARE @a as float(53); 
SET @a = SQUARE (SIN (@dLatitude/2.0)) + COS (@dLat1InRad) 
* COS (@dLat2InRad) 
* SQUARE(SIN (@dLongitude/2.0)); 
/* Intermediate result c (great circle distance in Radians). */ 
DECLARE @c as real; 
SET @c = 2.0 * ATN2 (SQRT (@a), SQRT (1.0 - @a)); 
DECLARE @kEarthRadius as real; 
/* SET kEarthRadius = 3956.0 miles */ 
SET @kEarthRadius = 6376.5;  /* kms */ 

DECLARE @dDistance as real; 
SET @dDistance = @kEarthRadius * @c; 
return (@dDistance); 
END 

GO 

Dodałem modelu podmiotu ado.net, zaktualizowany model z bazą danych i wybrał distancebetween

<Function Name="DistanceBetween" ReturnType="real" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="true" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo"> 
     <Parameter Name="Lat1" Type="real" Mode="In" /> 
     <Parameter Name="Long1" Type="real" Mode="In" /> 
     <Parameter Name="Lat2" Type="real" Mode="In" /> 
     <Parameter Name="Long2" Type="real" Mode="In" /> 
    </Function> 

zrobiłem częściowej klasy i napisał tę metodę

public partial class GeoDataEntities 
{ 
    [EdmFunction("GeoDataModel.Store", "DistanceBetween")] 
    public double DistanceBetween(double lat1, double lon1, double lat2, double lon2) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Próbowałem wiele razy, aby zapytać funkcję z tego kodu, ale robi błąd

var NParcel = db.geoAddresses.Where(g=> db.DistanceBetween(21.5,39.5, g.lat,g.lon) < 20); 

gdy próbuję count lub foreach na NParcel otrzymuję ten błąd

wybraną metodą „Podwójne DistanceBetween (Podwójne, podwójne, podwójne, Double) "na typie" EFSample.GeoDataEntities "nie można przetłumaczyć na wyrażenie składowe LINQ do Entities.

i StackTrace

w System.Data.Objects.ELinq.ExpressionConverter.ThrowUnresolvableFunction (wyrażenie ekspresji) w System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.FunctionCallTranslator.TranslateFunctionCall (ExpressionConverter rodzic, wywołanie MethodCallExpression, EdmFunctionAttribute functionAttribute) w System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate (ExpressionConverter dominująca MethodCallExpression LINQ) w System.Data.Objects.ELinq.ExpressionConverter.BinaryTranslator.TypedTranslate (ExpressionConverter macierzystego BinaryExpression LINQ) w System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression (wyrażenie LINQ) w systemie .Data.Objects.ELinq.ExpressionConverter.TranslateLambda (LambdaExpression lambda wejście DbExpression) w System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate (ExpressionConverter macierzystego, połączenie MethodCallExpression, DbExpression & źródła, DbExpressionBinding & sourceBinding, DbExpression & lambda) o System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate (ExpressionConverter rodzic, wywołanie MethodCallExpression) pod numerem System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate (ExpressionConverter dominująca MethodCallExpression LINQ) w System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression (wyrażenie LINQ) w System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.AggregateTranslator.Translate (ExpressionConverter rodzicem MethodCallExpression nazwać) w System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate (ExpressionConverter macierzystego MethodCallExpression LINQ) w System.Data.Objects.ELinq.ExpressionConverter.Convert() w System.Data.Objects.ELinq. ELinqQueryState.GetExecutionPlan (Nullable 1 forMergeOption) at System.Data.Objects.ObjectQuery 1.GetResults (Nullable 1 forMergeOption) at System.Data.Objects.ObjectQuery 1.System.Collections.Generic.IEnumerable.GetEnume włożenie do() na System.Linq.Enumerable.Single [TSource] (IEnumerable 1 source) at System.Linq.Queryable.Count[TSource](IQueryable 1 źródła)

+0

EF5 obsługuje typ danych przestrzennych (http://blogs.msdn.com/b/efdesign/archive/2011/05/04/spatial-types-in-the-entity-framework.aspx) po wyjęciu z pudełka. – Pawel

+0

Problem nie w danych przestrzennych problem, że mam wiele funkcji skalarnych chcę używać –

+0

Gert: i śledzić ten link http://stackoverflow.com/questions/10625955/can-sql-level-functions-be-made- kwerendy dostępne do linq-to-entity i zmodyfikowałem edmx, a problem nie został rozwiązany. –

Odpowiedz

24

Oto jak to zrobić:

Krok 1: W edmx

 <Function Name="DistanceBetween" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo"> 
     <CommandText> 
     select dbo.DistanceBetween(@lat1,@long1,@lat2,@long2) 
     </CommandText> 
     <Parameter Name="Lat1" Type="real" Mode="In" /> 
     <Parameter Name="Long1" Type="real" Mode="In" /> 
     <Parameter Name="Lat2" Type="real" Mode="In" /> 
     <Parameter Name="Long2" Type="real" Mode="In" /> 
    </Function> 

etap 2: importować funkcja

  1. kliknij dwukrotnie edmx
  2. w modelu widoku przeglądarki, rozwiń GeoDataModel.Store (mogłoby być nazwane inny)
  3. poszerzyć stored procedures /function
  4. kliknij dwukrotnie DistanceBetween
  5. Scalars = Single
  6. kliknij OK

Krok 3: In C#:

GeoDataEntities db = new GeoDataEntities(); 
    var first = db.DistanceBetween(234, 2342, 424, 243).First().Value; 

uwaga że IsComposable="false" i nie ReturnType i nie zapomnij dodać:

 <CommandText> 
     select dbo.DistanceBetween(@lat1,@long1,@lat2,@long2) 
     </CommandText> 

nadzieję, że pomoże ....

+0

To wydawało się działać. Ale kiedy odświeżyłem model (dla innych obiektów), zmieniło się z powrotem na Composable, co dawało błędy. Nie kompiluje błędów, więc może nadal działać, ale nie wydaje się dobrym długoterminowym podejściem. Każda funkcja dająca błędy w modelu. Zgaduję, że tworzenie SPROC do wywoływania funkcji jest wciąż moim podejściem. –