【问题标题】:How can I call a local function inside a LINQ query?如何在 LINQ 查询中调用本地函数?
【发布时间】:2012-10-28 01:01:00
【问题描述】:

我试过了:

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

【问题讨论】:

  • “似乎不起作用”是什么意思?
  • 如果我没有这条线,我的结果会是一样的: where GetDistance(db, d)
  • 数据库提供商应该如何将您的 C# 函数转换为 SQL?我不知道是否有更新、更强大的解决方案,但对于初学者来说,请查看模型定义的函数。 blogs.msdn.com/b/efdesign/archive/2009/01/07/…
  • 其实你应该在这里得到NotSupportedException
  • 我的代码的另一部分有错误,所以是的,当我修复我得到那个异常时!!

标签: c# asp.net linq function


【解决方案1】:

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

如果这不是问题:

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

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

【讨论】:

  • 我按照您上面的建议更改了我的代码,但仍然没有任何反应。我似乎没有执行函数GetDistance,因为我在声明函数的地方设置了一个断点并且程序的执行没有被中断。我已经发布了 Getdistance 函数以防万一。
  • @orestispan:嗯。也许db.DoctorsPrivateClinics.Count()==0
  • 我的代码的另一部分有错误,所以当我修复您的建议有效时是的!谢谢
【解决方案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 

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 我按照您上面的建议更改了我的代码,但仍然没有任何反应。我似乎没有执行函数GetDistance,因为我在声明函数的地方设置了一个断点并且程序的执行没有被中断。我已经发布了 Getdistance 函数以防万一。
    • 我的代码的另一部分有错误,所以当我修复您的建议有效时是的!谢谢
    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 2021-11-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-13
      • 2013-12-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多