简体   繁体   English

Linq OrderBy属性计算(带连接)

[英]Linq OrderBy calculation of properties (with join)

I have two database tables that map to models in my MVC app: 我有两个数据库表映射到我的MVC应用程序中的模型:

public class BuildingLocation
{
    public int id { get; set; }
    public double Lat { get; set; }
    public double Lng { get; set; }
}

public class Building
{
    public int id { get; set; }
    public string title { get; set; }
    //OTHER STUFF
}

Using Linq / Entities, I'm trying to get a list of Buildings, ordered by distance from a given point on a map. 使用Linq / Entities,我正在尝试获取建筑物列表,按照距地图上给定点的距离排序。

DefaultConnection db = new DefaultConnection();    

 public IEnumerable<dynamic> GetBuildings(double north, double south, double east, double west)
    {
        double centreX = (east - west) / 2;
        double centreY = (north - south) / 2;
        var query = from b in db.Building
                    join l in db.BuildingLocation on
                    b.id equals l.id
                    select new {b.id, b.title, l.Lat, l.Lng,
                            dist = Math.Sqrt(((centreX - l.Lat) * (centreX - l.Lat)) + ((centreY - l.Lng) * (centreY - l.Lng)))
                    };

        query = query.Where(l => l.Lat > west);
        query = query.Where(l => l.Lat < east);
        query = query.Where(l => l.Lng > south);
        query = query.Where(l => l.Lng < north);
        query = query.OrderBy(c => c.dist);
        return query.AsEnumerable();
       }

So, obviously this doesn't work at all. 所以,显然这根本不起作用。 I've never used Linq before. 我以前从未使用过Linq。 How can I set the OrderBy based on a calculation? 如何根据计算设置OrderBy?

Try this : 尝试这个 :

public IEnumerable<dynamic> GetBuildings(double north, double south, double east, double west)
{
    double centreX = (east - west) / 2;
    double centreY = (north - south) / 2;
    var query = db.Building.Join(db.BuildingLocation.Where(l=> 
                             l.Lat > west &&  l.Lat < east 
                             && l.Lng > south && l.Lng < north), 
                             b => b.id , l => l.id, 
                             (b,l) => new {
                                            ID = b.id,
                                            Title = b.title,
                                            Lat = l.lat,
                                            Lng = l.Lng,
                                            dist =  Math.Sqrt(((centreX - l.Lat) * (centreX - l.Lat)) + ((centreY - l.Lng) * (centreY - l.Lng)))
                                          }).OrderBy(Q=>Q.dist);

    return query;
}

Above query is written in lambda expression. 上面的查询是用lambda表达式编写的。 If you want this in query expression use below code : 如果您想在查询表达式中使用以下代码:

var query =  from result in (from b in db.Building
                join l in db.BuildingLocation on
                b.id equals l.id
                where l.Lat > west &&  l.Lat < east && l.Lng > south && l.Lng < north
                select new {b.id, b.title, l.Lat, l.Lng,
                        dist = Math.Sqrt(((centreX - l.Lat) * (centreX - l.Lat)) + ((centreY - l.Lng) * (centreY - l.Lng))) })
                order by result.dist select result;

This will fix your issue. 这将解决您的问题。

If you use Entity framework you probably got the following exception message: 如果您使用Entity框架,您可能会收到以下异常消息:

"LINQ to Entities does not recognize the method 'Double Sqrt(Double)' method, and this method cannot be translated into a store expression." “LINQ to Entities无法识别方法'Double Sqrt(Double)'方法,并且此方法无法转换为商店表达式。”

So i would solve this in two steps, the inner query should only get data from tables, and in the outer (now in-memory query) will do the distance calculation and ordering: 所以我将分两步解决这个问题,内部查询应该只从表中获取数据,而在外部(现在是内存中查询)将进行距离计算和排序:

double centreX = (east - west) / 2;
double centreY = (north - south) / 2;

var query = (from result in
                ((from b in db.Building
                    join l in db.BuildingLocation on b.id equals l.id
                    where l.Lat > west && l.Lat < east && l.Lng > south && l.Lng < north
                    select new { b.id, b.title, l.Lat, l.Lng }).AsEnumerable()
                )
            select new
            {
                id = result.id,
                title = result.title,
                Lat = result.Lat,
                Lng = result.Lng,
                dist = Math.Sqrt(((centreX - l.Lat) * (centreX - l.Lat)) + ((centreY - l.Lng) * (centreY - l.Lng)))
            }).OrderBy(e => e.dist); 

Off the top of my hat, and just for the buildings, I'd do something like this: 在我的帽子顶部,只是为了建筑物,我会做这样的事情:

public IEnumerable<Building> GetBuildings(double north, double south, double east, double west)
{
    double centreX = (east - west) / 2;
    double centreY = (north - south) / 2;

    db.BuildingLocation.Where(l => l.Lat > west && l.Lat < east && l.Lng > south && l.Lng < north)
        .Join(
            db.Building, 
            b => b.id, 
            l => l.id, 
            (l, b) =>  new {Building = b, l.Lat, l.Lng })
        .AsEnumerable()
        .OrderBy(l => Math.Sqrt(((centreX - l.Lat) * (centreX - l.Lat)) + ((centreY - l.Lng) * (centreY - l.Lng))))
        .Select(l => l.Building)
}

I'm assuming it doesn't work because the distance calculation can't be translated to SQL, so I've inserted the AsEnumerable call before the OrderBy -- that means the filtering happens on the database side, but the ordering happens in-memory with Linq to Objects. 我假设它不起作用因为距离计算无法转换为SQL,所以我在OrderBy之前插入了A​​sEnumerable调用 - 这意味着过滤发生在数据库端,但是顺序发生在 - Linq到Objects的内存。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM