如何在LINQ查询中调用本地函数?

时间:2012-10-28 01:01:00

标签: c# asp.net linq function

我试过了:

 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)与私人诊所的位置之间的距离。已从Google地图中检索地图上特定点的经度和纬度。在我的查询中,我指定此距离必须小于1 Km

4 个答案:

答案 0 :(得分:10)

问题是您的查询是由LINQ提供程序(EF / Linq2Sql?)转换为SQL的。它无法翻译任意.net方法。解决这个问题的唯一方法是在本地枚举整个集合,无需使用索引等数据库,还可能从数据库中获取网络IO的瓶颈。

如果这不是问题:

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

否则,请考虑以可转换为SQL的术语重新构建查询。看到您的GetDistance方法有助于我们为您提供帮助。

答案 1 :(得分: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 

答案 2 :(得分:1)

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

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

答案 3 :(得分: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,
                    };