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 实现按照距离排序 也就全部完成了。
上一篇: 如何用css3实现多个元素依次显示
下一篇: sql模糊查询语句实例讲解