entity framework 实现按照距离排序[转]

原文地址: https://www.cnblogs.com/hucheng/p/9885573.html

 

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
                    ),
                ) + COS(30.611842 * PI() / 180) * COS(es_lat * PI() / 180) * POW(
                    SIN(
                        (
                            104.074666 * PI() / 180 - es_lon * PI() / 180
                        ) / 2
                    ),
                )
            )
        ) * 1000
    ) AS distance_um
FROM
    c_ershuai
ORDER BY
    distance_um ASC

 

以下是我采用的方案

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

/// <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; }
}

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

double user_lng = 113.46, user_lat = 22.27;  //用户经纬度
int pageIndex = 3; //当前页码
int pageSize = 10; //每页条数


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 实现按照距离排序 也就全部完成了。

上一篇:笔记


下一篇:布局 and BFC 清浮动