简体   繁体   中英

Compute and return distance in EF 6 / LINQ to Entities / DbGeography?

Using .NET 4.5.1 and Entity Framework 6.0.2 , I've got something like the entities and query below.

Basically I am selecting a new entity and mashing the Distance into it, when I'd rather query a Person and have Distance embedded in that person's Location entity.

public class Person
{
    public string Id { get; set; }
    public string Name { get; set; }
    [...]

    public virtual ICollection<PersonLocation> PersonLocations { get; private set;}
}

public class Location
{
    public int Id { get; set; }
    public DbGeography Geocode { get; set; }
    // ... I want calculated Distance right here 
}

public class PersonLocation
{
    public int PersonId { get; set; }
    public int LocationId { get; set; }
    public bool AcceptingNewClients { get; set; }
    public Hours Hours { get; set; }
    [...]

    public virtual Person Person { get; private set; }
    public virtual Location Location { get; private set; }
}

Currently I have a query like this, where _geo is a DbGeography set to the desired search location and radius is an int = 15 :

IQueryable<PersonWithDistance> results =
    from pl in db.PersonLocations
    let distance = pl.Location.Geocode.Distance(_geo)
    where pl.Location.Geocode.IsEmpty == false
    where distance <= radius * 1609.344
    orderby distance
    select new { Person = pl.Person, Distance = Math.Round((double)(distance / 1609.344), 1) };

What I'd like instead is to have Distance added to each Location entity and just select the Person entity in the query.

Is there a way to do this? Should I be going a different route altogether?

What I ended up doing was this:

public class Location
{
    public int Id { get; set; }
    public DbGeography Geocode { get; set; }

    public double? Distance { get; set; }
}

In the DbContext setup, so that EF ignores the column as it relates to the database:

Ignore(x => x.Distance);

The search query becomes:

var results = (from pl in db.PersonLocations
                let distance = pl.Location.Geocode.Distance(_geo)
                where pl.Location.Geocode.IsEmpty == false
                where distance <= radius * 1609.344
                select pl.Person).Distinct();

And finally:

var personsWithDistance = results.ToList();

foreach (Person p in personsWithDistance)
{
    var pls = p.PersonLocations;

    foreach (PersonLocation pl in pls)
    {
        if (pl.Location.Geocode != null)
            pl.Location.Distance = Math.Round((double)(pl.Location.Geocode.Distance(_geo) / 1609.344), 1);
        else
            pl.Location.Distance = null;
    }
}

// order by distance
personsWithDistance = personsWithDistance.OrderBy(r => r.PersonLocations.FirstOrDefault().Location.Distance).ToList();

return personsWithDistance;

Essentially I created a fake column/property in the Location entity and told the DbContext to ignore it. I query for the resultset I want and then manually update the property and return the results.

It doesn't feel right somehow, but it works.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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