7

我试过了:

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

但这不起作用,GetDistance 是一个本地函数,我没有得到例外,但它似乎不起作用,有什么想法吗?谢谢

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 公里

4

4 回答 4

11

问题是您的查询正在被 LINQ 提供程序 (EF/Linq2Sql?) 转换为 SQL。它不能翻译任意 .net 方法。解决这个问题的唯一方法是在本地枚举整个集合,省去索引等数据库的优点,并且可能会成为来自数据库的网络 IO 瓶颈。

如果这不是问题:

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

否则,请考虑用可以翻译成 SQL 的术语重新表述您的查询。看到您的GetDistance方法将有助于我们在这里为您提供帮助。

于 2012-10-28T01:11:29.220 回答
2

正如上面建议的那样,添加 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 
于 2012-10-29T17:59:58.023 回答
1

如果你使用 ef,有办法编写 sql 并将 linq 映射到它,但在最简单的情况下,你可以检索所有数据,然后执行你的自定义函数:

var doctorPractise = from d in db.DoctorsPrivateClinics.ToList()
                                     where  GetDistance(db, d)<1.0
                                     select d;
于 2012-10-28T01:13:21.867 回答
1

使用 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,
                    };
于 2016-11-25T10:47:04.210 回答