2013-01-18 9 views
7

Ho un database e in esso ho un hotel di classe con coordinate GPS. Voglio ottenere i posti più vicini alle coordinate che scelgo.C# - LINQ - distanza più breve con latitudine e longitudine GPS

penso che dovrebbe essere simile a questo (ho trovato molti codici di esempio qui e come questo):

var coord = new GeoCoordinate(latitude, longitude); 
var nearest = (from h in db.hotels 
       let geo = new GeoCoordinate(h.gps.lat, h.gps.lng) 
       orderby geo.GetDistanceTo(coord) 
       select h).Take(10); 

Il problema è che ho questo errore quando ho provato a cercare qualcosa:

Solo costruttori senza parametri e inizializzatori sono supportati in LINQ to Entities

ho cercato di google un d ho trovato che dividere linq in due mi può aiutare ma non sono sicuro di come. Grazie per l'aiuto.

+0

che sta attualmente cercando di fare l'aritmetica * nel database *. Sospetto che non funzionerà senza molto lavoro. Probabilmente vorrai un proc memorizzato o una UDF. –

risposta

6

È possibile utilizzare l'inizializzatore oggetto invece di costruttore con parametri:

var nearest = (from h in db.hotels 
      let geo = new GeoCoordinate{ Latitude = h.gps.lat, Longitude = h.gps.lng} 
      orderby geo.GetDistanceTo(coord) 
      select h).Take(10); 

Ma si dovrà probabilmente i problemi causati dal metodo GetDistanceTo, potrebbe fornire l'attuazione di tale metodo?

+0

Sì, hai ragione. Hai risolto il mio problema con il costruttore senza parametri ma ho problemi con quel metodo. Non lo sto impelagando. È un metodo fornito dalla classe GeoCoordinate. Come ho scoperto altrove, in cui ho solo poche operazioni aritmetiche permettono in Linq quindi devo usarle e ho trovato un algoritmo per questo, ho ragione? –

+0

@Bibo Oh, non sapevo che GeoCoordinate fosse una classe framework. Linq to Entities costruisce internamente uno script SQL che verrà successivamente eseguito sul database una volta che si chiama ad esempio una ToList() su un IQueryable , quindi si ha ragione si è limitati alle poche operazioni consentite dal linguaggio di manipolazione dei dati SQL. – TKharaishvili

+0

@Bibo potresti trovare utili le seguenti due classi: http://msdn.microsoft.com/en-us/library/system.data.objects.entityfunctions.aspx http://msdn.microsoft.com/ it-us/library/system.data.objects.sqlclient.sqlfunctions.aspx – TKharaishvili

4

Inserisco qui la mia soluzione che sto usando per ora. Ma scelgo GwynnBliedd perché risolve il mio problema in questione.

Ho aggiunto il tipo DbGeography alla mia classe e lo sto utilizzando invece di salvare latitudine e longitudine nel database.

locationField = DbGeography.FromText(String.Format("POINT({0} {1})", orig.gps.lat.ToString().Replace(",", "."), orig.gps.lng.ToString().Replace(",", "."))); 

allora è molto facile da usare LINQ:

var coord = DbGeography.FromText(String.Format("POINT({0} {1})", latitude.ToString().Replace(",", "."), longitude.ToString().Replace(",", "."))); 
      var nearest = (from h in db.hotels 
          where h.location != null 
          orderby h.location.Distance(coord) 
          select h).Take(limit); 
      return nearest; 

Per la soluzione ora questo sta funzionando ed è buona. Per qualche tempo lo userò ma, come hanno detto alcuni utenti, potrei provare UDF con l'implementazione della formula di Haversine (come in this answer).

+0

+1 - Grazie per il puntatore alla classe DbGeography! Tuttavia: penso che l'ordine degli argomenti di longitudine e latitudine nel metodo FromText sia invertito; vedere: http://stackoverflow.com/questions/16546598/dbgeography-pointfromtext-throws-latitude-values-must-be-between-90-and-90-d –

3

ho avuto una ragionevole su di successo con questa implementazione del Haversine Distance formula

var startPoint = new { Latitude = 1.123, Longitude = 12.3 }; 

var closest = entities.Something.OrderBy(x => 12742 * SqlFunctions.Asin(SqlFunctions.SquareRoot(SqlFunctions.Sin(((SqlFunctions.Pi()/180) * (x.Latitude - startPoint.Latitude))/2) * SqlFunctions.Sin(((SqlFunctions.Pi()/180) * (x.Latitude - startPoint.Latitude))/2) + 
            SqlFunctions.Cos((SqlFunctions.Pi()/180) * startPoint.Latitude) * SqlFunctions.Cos((SqlFunctions.Pi()/180) * (x.Latitude)) * 
            SqlFunctions.Sin(((SqlFunctions.Pi()/180) * (x.Longitude - startPoint.Longitude))/2) * SqlFunctions.Sin(((SqlFunctions.Pi()/180) * (x.Longitude - startPoint.Longitude))/2)))).Take(5);