欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

entity framework 实现按照距离排序

程序员文章站 2022-04-08 11:39:12
在做项目时,经常会遇到“离我最近”这种需求。顾名思义,它需要根据用户的经纬度和事物的经纬度计算距离,然后进行排序,最后分页(当然这些操作要在数据库中进行,否则就变成假分页了)。 我们通常可以用sql语句来实现 但是我比较习惯使用 entity framework,于是我就想着能不能用 entity ......

在做项目时,经常会遇到“离我最近”这种需求。顾名思义,它需要根据用户的经纬度和事物的经纬度计算距离,然后进行排序,最后分页(当然这些操作要在数据库中进行,否则就变成假分页了)。

我们通常可以用sql语句来实现

select
    es_name,
    es_lon,
    es_lat,
    round(
        6378.138 * 2 * asin(
            sqrt(
                pow(
                    sin(
                        (
                            30.611842 * pi() / 180 - es_lat * pi() / 180
                        ) / 2
                    ),
                    2
                ) + cos(30.611842 * pi() / 180) * cos(es_lat * pi() / 180) * pow(
                    sin(
                        (
                            104.074666 * pi() / 180 - es_lon * pi() / 180
                        ) / 2
                    ),
                    2
                )
            )
        ) * 1000
    ) as distance_um
from
    c_ershuai
order by
    distance_um asc

但是我比较习惯使用 entity framework,于是我就想着能不能用 entity framework 实现按照距离排序。

 

以下是我采用的方案

首先定义一个接口,用来表示具有经纬度信息的实体。

    /// <summary>
    /// 具有经纬度
    /// </summary>
    public interface ihaslngandlat
    {
        /// <summary>
        /// 经度
        /// </summary>
        double lng { get; set; }
        /// <summary>
        /// 纬度
        /// </summary>
        double lat { get; set; }
    }

然后创建泛型类,用来包装计算的距离。

    /// <summary>
    /// 带距离的数据
    /// </summary>
    /// <typeparam name="tentity"></typeparam>
    public class datawithdistance<tentity>
    {
        /// <summary>
        /// 距离(km)
        /// </summary>
        public double distance { get; set; }
        /// <summary>
        /// 实体数据
        /// </summary>
        public tentity entity { get; set; }
    }

最后编写根据距离排序的扩展方法

注意:这个方法是采用的 sqlfunctions 类,所以仅支持sqlserver数据库,如果是其它数据库,需要将 sqlfunctions 更换成对应的类

    /// <summary>
    /// iqueryable扩展类
    /// </summary>
    public static class queryableextension
    {
        /// <summary>
        /// 根据距离排序
        /// </summary>
        /// <typeparam name="tentity"></typeparam>
        /// <param name="queryable"></param>
        /// <param name="lng">经度</param>
        /// <param name="lat">纬度</param>
        /// <returns></returns>
        public static iqueryable<datawithdistance<tentity>> orderbydistance<tentity>(this iqueryable<tentity> queryable, double lng, double lat) where tentity : class, ihaslngandlat
        {
            var rtn = from q in queryable
                      let radlat1 = lat * math.pi / 180.0
                      let radlat2 = q.lat * math.pi / 180.0
                      let a = radlat1 - radlat2
                      let b = lng * math.pi / 180.0 - q.lng * math.pi / 180.0
                      let s = 2 * sqlfunctions.asin(sqlfunctions.squareroot(math.pow((double)sqlfunctions.sin(a / 2), 2) +
               sqlfunctions.cos(radlat1) * sqlfunctions.cos(radlat2) * math.pow((double)sqlfunctions.sin(b / 2), 2))) * 6378.137
                      let d = math.round((double)s * 10000) / 10000
                      orderby d
                      select new datawithdistance<tentity> { entity = q, distance = d };

            return rtn;
        }
    }

以上就完成了 entity framework 按照距离排序的功能。

 

接下来我们用它来写一个小小的demo

首先创建一个商店实体类,具有经纬度字段,实现了  ihaslngandlat 接口。

    /// <summary>
    /// 商店实体
    /// </summary>
    public class shop : ihaslngandlat
    {
        /// <summary>
        /// 主键
        /// </summary>
        public int id { get; set; }
        /// <summary>
        /// 商店名称
        /// </summary>
        [required]
        [stringlength(64)]
        public string shopname { get; set; }
        /// <summary>
        /// 经度
        /// </summary>
        public double lng { get; set; }
        /// <summary>
        /// 纬度
        /// </summary>
        public double lat { get; set; }
    }

然后创建ef上下文类

    /// <summary>
    /// ef上下文
    /// </summary>
    public class demodbcontext : dbcontext
    {
        public demodbcontext()
            : base("name=demodbcontext")
        {
        }
        public virtual dbset<shop> shop { get; set; }
    }

最后我们分页查询商店,并按照距离由近到远排序

            #region 入参
            double user_lng = 113.46, user_lat = 22.27;  //用户经纬度
            int pageindex = 3; //当前页码
            int pagesize = 10; //每页条数
            #endregion

            using (demodbcontext context = new demodbcontext())
            {
                var queryable = context.shop.asnotracking().asqueryable();
                iqueryable<datawithdistance<shop>> sort_queryable = queryable.orderbydistance(user_lng, user_lat);  //按照用户的距离从近到远排序
                list<datawithdistance<shop>> data = sort_queryable.skip((pageindex - 1) * pagesize).take(pagesize).tolist();   //分页并执行sql查询获取数据

                //todo:将查到的数据映射成dto对象,并返回给客户端
            }

好了,entity framework 实现按照距离排序 也就全部完成了。