2012-10-28 17 views
4

我想:如何在LINQ查詢中調用本地函數?

var doctorPractise = from d in db.DoctorsPrivateClinics 
            where GetDistance(db, d)<1.0 
            select d; 

但是,這並不工作,getDistance的是本地函數,我沒有得到一個exeption,但它似乎沒有工作,任何想法? 謝謝

的getDistance的是:

private double GetDistance(DBClassesDataContext db, DoctorsPrivateClinic docPra) 
    { 
     double distance=-1; 
     double longitude; 
     double latitude; 

     var city = from c in db.Cities 
        where c.Code == Convert.ToInt32(Request.QueryString["Area"]) 
        select c; 


     foreach (var cit in city)//it will be just one row that will be found but I don't know how to express it without foreach 
     { 
      Calculations.GetLongitudeLatitudeGoogle getLongLat = new Calculations.GetLongitudeLatitudeGoogle(); 
      getLongLat.GetLongitudeLatitude("", cit.Name, "", out longitude, out latitude); 
      distance = CalcualateDistance(Convert.ToDouble(docPra.PrivateClinic.Latitude), Convert.ToDouble(docPra.PrivateClinic.Longtitude), latitude, longitude); 
     } 
     return distance; 
    } 

我想實現的是計算地圖(cit.Name)和一傢俬人診所的位置上的特定點之間的距離。地圖上特定點的經度和緯度從Googlemaps中檢索。在我的查詢中,我指定這個距離必須小於1公里

+1

「似乎沒有工作」的意思是什麼? – dtb

+0

如果我沒有行,我的結果將是相同的:其中GetDistance(db,d)<1.0 – orestispan

+0

數據庫提供程序如何將您的C#函數轉換爲SQL?我不知道是否有更新,更強大的解決方案,但對於初學者,請查看模型定義的功能。 http://blogs.msdn.com/b/efdesign/archive/2009/01/07/model-defined-functions.aspx –

回答

8

問題在於您的查詢正在被LINQ提供程序(EF/Linq2Sql?)轉換爲SQL。它不能翻譯任意的.net方法。唯一的方法就是在本地枚舉整個集合,省去索引等數據庫的好處,並且可能會阻塞來自數據庫的網絡IO。

如果這不是一個問題:

var doctorPractise = from d in db.DoctorsPrivateClinics.AsEnumerable() 
           where GetDistance(db, d)<1.0 
           select d; 

否則,考慮可轉換爲SQL方面重申查詢。看到你的GetDistance方法將幫助我們在這裏幫助你。

+0

我按照上面的建議更改了我的代碼,但仍然沒有發生任何事情。我似乎沒有執行函數GetDistance,因爲我在聲明函數的位置設置了一個斷點,並且程序的執行沒有中斷。我已經發布了Getdistance函數,以防這可以提供幫助。 – orestispan

+0

@orestispan:嗯。也許'db.DoctorsPrivateClinics.Count()== 0'? – spender

+0

我的代碼的另一部分出現了錯誤,因此,當我修復了您的提案時,該錯誤仍然存​​在!謝謝 – orestispan

0

如果使用EF,有寫SQL和地圖LINQ到它的方式,但在簡單的情況下,你可以檢索所有數據,然後執行您的自定義功能:

var doctorPractise = from d in db.DoctorsPrivateClinics.ToList() 
            where GetDistance(db, d)<1.0 
            select d; 
+0

我按照上面的建議更改了我的代碼,但仍然沒有發生任何事情。我似乎沒有執行函數GetDistance,因爲我在聲明函數的位置設置了一個斷點,並且程序的執行沒有中斷。我已經發布了Getdistance函數,以防這可以提供幫助。 – orestispan

+0

我的代碼的另一部分出現了錯誤,因此當我修復了您的提案後,該錯誤消失了!謝謝 – orestispan

1

至於有人提議上面添加db.DoctorsPrivateClinics.AsEnumerable()是什麼解決了我的問題。在這裏提高一點點我的功能後的解決方案:

   double longitude=0; 
       double latitude=0; 

       var city = from c in db.Cities 
          where c.Code == Convert.ToInt32(Request.QueryString["Area"]) 
          select c; 
       city = city.Take(1);//take the first value, that sould be the only value in this case 
       if (city.Count() == 0) 
       { 
        //hanlde error 
       } 
       else 
       { 
        City cit = city.First(); 
        Calculations.GetLongitudeLatitudeGoogle getLongLat = new Calculations.GetLongitudeLatitudeGoogle(); 
        getLongLat.GetLongitudeLatitude("", cit.Name, "", out longitude, out latitude); 
       } 


       var doctorPractise = from d in db.DoctorsPrivateClinics.AsEnumerable()//or .ToList() 
            where CalcualateDistance(Convert.ToDouble(d.PrivateClinic.Latitude), Convert.ToDouble(d.PrivateClinic.Longtitude), latitude, longitude)<5.0f 

            select d; 

其中函數CalcualateDistance是:

{ 
     /* 
      The Haversine formula according to Dr. Math. 
      http://mathforum.org/library/drmath/view/51879.html 

      dlon = lon2 - lon1 
      dlat = lat2 - lat1 
      a = (sin(dlat/2))^2 + cos(lat1) * cos(lat2) * (sin(dlon/2))^2 
      c = 2 * atan2(sqrt(a), sqrt(1-a)) 
      d = R * c 

      Where 
       * dlon is the change in longitude 
       * dlat is the change in latitude 
       * c is the great circle distance in Radians. 
       * R is the radius of a spherical Earth. 
       * The locations of the two points in 
        spherical coordinates (longitude and 
        latitude) are lon1,lat1 and lon2, lat2. 
     */ 
     double dDistance = Double.MinValue; 
     double dLat1InRad = Lat1 * (Math.PI/180.0); 
     double dLong1InRad = Long1 * (Math.PI/180.0); 
     double dLat2InRad = Lat2 * (Math.PI/180.0); 
     double dLong2InRad = Long2 * (Math.PI/180.0); 

     double dLongitude = dLong2InRad - dLong1InRad; 
     double dLatitude = dLat2InRad - dLat1InRad; 

     // Intermediate result a. 
     double a = Math.Pow(Math.Sin(dLatitude/2.0), 2.0) + 
        Math.Cos(dLat1InRad) * Math.Cos(dLat2InRad) * 
        Math.Pow(Math.Sin(dLongitude/2.0), 2.0); 

     // Intermediate result c (great circle distance in Radians). 
     double c = 2.0 * Math.Asin(Math.Sqrt(a)); 

     // Distance. 
     // const Double kEarthRadiusMiles = 3956.0; 
     const Double kEarthRadiusKms = 6376.5; 
     dDistance = kEarthRadiusKms * c; 

     return dDistance; 
    } 

即使這個工作形成我來說不使用LINQ的有效途徑。所以,我搜索了一個更好的解決辦法是重新寫我的功能在T-SQL這樣的改進方案是:

double longitude=0; 
       double latitude=0; 

       var city = from c in db.Cities 
          where c.Code == Convert.ToInt32(Request.QueryString["Area"]) 
          select c; 
       city = city.Take(1);//take the first value, that should be the only value in this case 
       if (city.Count() == 0) 
       { 
        //hanlde error 
       } 
       else 
       { 
        City cit = city.First(); 
        Calculations.GetLongitudeLatitudeGoogle getLongLat = new Calculations.GetLongitudeLatitudeGoogle(); 
        getLongLat.GetLongitudeLatitude("", cit.Name, "", out longitude, out latitude); 
       } 


       var doctorPractise = from d in db.DoctorsPrivateClinics 
            where db.CalculateDistance(Convert.ToDouble(d.PrivateClinic.Latitude), Convert.ToDouble(d.PrivateClinic.Longtitude), latitude, longitude) < 5.0f        
            select d; 

凡寫在T-SQL的功能是:

ALTER FUNCTION PublicSiteDBUser.CalculateDistance 
(

@latitudeArea float(53), 
@longitudeArea float(53), 
@latitudePractise float(53), 
@longitudePractise float(53) 
) 

RETURNS float(53) 
AS 
BEGIN 

DECLARE @dDistance as float(53)=0 
DECLARE @dLat1InRad as float(53)=0 
DECLARE @dLong1InRad as float(53)=0 
DECLARE @dLat2InRad as float(53)=0 
DECLARE @dLong2InRad as float(53)=0 

DECLARE @dLongitude as float(53)=0 
DECLARE @dLatitude as float(53)=0 

DECLARE @a as float(53)=0 
DECLARE @c as float(53)=0 

DECLARE @kEarthRadiusKms as float(53)=6376.5 

SET @dLat1InRad = @latitudeArea * PI()/180.0 
SET @dLong1InRad= @longitudeArea * PI()/180.0 
SET @dLat2InRad= @latitudePractise * PI()/180.0 
SET @dLong2InRad= @longitudePractise * PI()/180.0 

SET @dLongitude = @dLong2InRad - @dLong1InRad 
SET @dLatitude = @dLat2InRad - @dLat1InRad 

SET @a = POWER(SIN(@dLatitude/2.0), 2.0)+COS(@dLat1InRad)*COS(@dLat2InRad) * POWER (SIN(@dLongitude/2.0),2.0) 
SET @c = 2.0*ASIN(SQRT(@a))  
SET @dDistance = @kEarthRadiusKms * @c 

RETURN @dDistance 
END 
0

使用SQL功能

var query = from it in db.items 
        let facilityLatitude = it.Latitude ?? 0 
        let facilityLongitude = it.Longitude ?? 0 
        let theta = ((lon - facilityLongitude) * Math.PI/180.0) 
        let requestLat = (lat * Math.PI/180.0) 
        let facilityLat = (facilityLatitude * Math.PI/180.0) 
        let dist = (SqlFunctions.Sin(requestLat) * SqlFunctions.Sin(facilityLat)) + (SqlFunctions.Cos(requestLat) * SqlFunctions.Cos(facilityLat) * SqlFunctions.Cos(theta)) 
        let cosDist = SqlFunctions.Acos(dist) 
        let degDist = (cosDist/Math.PI * 180.0) 
        let absoluteDist = degDist * 60 * 1.1515 
        let distInKM = absoluteDist * 1.609344 
        where distInKM < distance 
        select new() 
        { 
         Address = it.Address, 
        };