zoukankan      html  css  js  c++  java
  • entity framework 实现按照距离排序

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

    我们通常可以用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 实现按照距离排序 也就全部完成了。

     

  • 相关阅读:
    vue箭头函数问题
    JS函数知识点梳理
    因tensorflow版本升级ImportError: No module named 'tensorflow.models.rnn'
    数据库优化,以实际SQL入手,带你一步一步走上SQL优化之路!
    在 IntelliJ IDEA 中这样使用 Git,效率提升2倍以上
    百万级高并发mongodb集群性能数十倍提升优化实践
    阿里巴巴Java开发手册正确学习姿势是怎样的?刷新代码规范认知
    50道Redis面试题史上最全,以后面试再也不怕问Redis了
    没想到Spring Boot居然这么耗内存,有点惊讶
    源码角度分析-newFixedThreadPool线程池导致的内存飙升问题
  • 原文地址:https://www.cnblogs.com/hucheng/p/9885573.html
Copyright © 2011-2022 走看看