简体   繁体   English

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

[英]How can I call a local function inside a LINQ query?

I tried: 我试过了:

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

But this doesn't work, GetDistance is a local function, I didn't get an exeption but it didn't seem to work, any ideas? 但这不起作用,GetDistance是一个本地函数,我没有得到一个例外但它似乎没有工作,任何想法? Thank you 谢谢

The GetDistance is: 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;
    }

What I want to achieve is to calculate the distance between a specific point on the map (cit.Name) and the location of a private clinic. 我想要实现的是计算地图上某个特定点(cit.Name)与私人诊所的位置之间的距离。 The longitude and latitude of the specific point on the map is been retrieved from Googlemaps. 已从Google地图中检索地图上特定点的经度和纬度。 In my query I specify that this distance must be less than 1 Km 在我的查询中,我指定此距离必须小于1公里

The trouble is that your query is being translated into SQL by the LINQ provider (EF/Linq2Sql?). 问题是您的查询是由LINQ提供程序(EF / Linq2Sql?)转换为SQL的。 It's not capable of translation of arbitrary .net methods. 它无法翻译任意.net方法。 The only way round this is to enumerate the entire collection locally, dispensing with the database goodness of indexes etc, and probably bottlenecking on network IO from the DB. 解决这个问题的唯一方法是在本地枚举整个集合,无需使用索引等数据库,还可能从数据库中获取网络IO的瓶颈。

If this isn't a problem: 如果这不是问题:

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

otherwise, consider restating your query in terms that can be translated to SQL. 否则,请考虑以可转换为SQL的术语重新表达您的查询。 Seeing your GetDistance method would help us to help you here. 看到你的GetDistance方法可以帮助我们在这里帮助你。

As it was proposed above adding db.DoctorsPrivateClinics.AsEnumerable() was what solved my problem. 正如上面提到的那样,添加db.DoctorsPrivateClinics.AsEnumerable()就解决了我的问题。 After improving a little bit my function here is the solution: 在改进了一点之后我的功能就是解决方案:

                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;

where the function CalcualateDistance is: 其中函数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;
    }

Even though this worked form me it not an efficient way to use LINQ. 尽管这对我有用,但它并不是使用LINQ的有效方法。 So I searched for a better solution that is to re write my function in T-SQL so the improved solution is: 所以我搜索了一个更好的解决方案,即在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;

Where the function written in T-SQL is: 用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 

If you used ef, there are ways to write sql and map linq to it, but in the simplest case you can retrieve all the data and then execute your custom function: 如果您使用了ef,有办法编写sql并将linq映射到它,但在最简单的情况下,您可以检索所有数据,然后执行自定义函数:

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

Use Sql functions 使用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,
                    };

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM