0

我想获取用户输入的位置 x 英里内的所有组织的列表。这将转换为长/纬度位置。

组织以long 和lat 存储在数据库中。

我正在使用带有实体框架的 MVC 和带有存储库模式的工作单元来访问数据集。

这是我的EntityRepository:

        public IQueryable<T> All
    {
        get
        {
            return dbSet;
        }
    }

    public IQueryable<T> AllIncluding(params System.Linq.Expressions.Expression<Func<T, object>>[] includeProperties)
    {
        IQueryable<T> query = dbSet;
        foreach (var includeProperty in includeProperties)
        {
            query = query.Include(includeProperty);
        }

        return query;
    }

    public IEnumerable<T> Where(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
    {
        return dbSet.Where(predicate).AsEnumerable();
    }

为了查询我的数据上下文中的数据,我为每个实体使用一个服务类,一个 UOW 被注入到每个服务中。对组织的服务要求是:

 public class OrgService :IOrgService
{
    private IUnitOfWork _UoW;

    public OrgService(IUnitOfWork UoW)
    {
        _UoW = UoW;
    }

    public Organisation GetOrgByID(int OrgID)
    {

        return _UoW.OrganisationRepo.Find(OrgID);
    }

    public IList<Organisation> GetAllOrgs()
    {
        return _UoW.OrganisationRepo.All.ToList();  
    }

    public IList<Organisation> GetOrgsByLocation(double lat, double lng, int range)
    {

      /// I need to return a list of all organisations within X miles

    }

 }

所有其他查询都按应有的方式工作,但是我没有尝试编写 GetOrgsByLocation() 方法。这是我认为我需要得到结果的查询:

 var userLocation = new GeoCoordinate(lat, lng);
 var result = _UoW.OrganisationRepo.Where(x => new GeoCoordinate(x.Latitude, x.Longitude))
                              .Where(x => x.GetDistanceTo(userLocation) < radius).ToList();

当我尝试运行此查询时,我得到:

“无法将类型 system.device.location.geoCoordinate 隐式转换为 bool”

任何人都可以帮忙吗?

** 更新 - 工作解决方案 **

 var userLocation = new GeoCoordinate(lat, lng);
        var nearbyOrganizations = _UoW.OrganisationRepo.All.ToList()
         .Select(x => new
         { //use an anonymous type or any type you want
             Org = x,
             Distance = new GeoCoordinate(x.Latitude, x.Longitude).GetDistanceTo(userLocation)
         }) 
         .Where(x => x.Distance < 50000)
         .ToList();

        foreach (var organisation in nearbyOrganizations)
        {
            Console.WriteLine("{0} ({1:n0} meters)", organisation.Org, organisation.Distance);
        }

多亏了以下对此解决方案的帮助,尽管似乎必须查询所有对象才能使其正常工作,但似乎查询更适合在数据库上运行,我将不得不对此进行更多研究。

4

2 回答 2

2

Where方法必须返回一个布尔值。

_UoW.OrganisationRepo.Where(x => new GeoCoordinate(x.Latitude, x.Longitude))

也许你打算在.Select那里使用?下面的代码会起作用吗?

_UoW.OrganisationRepo.Select(x => new GeoCoordinate(x.Latitude, x.Longitude))
                          .Where(x => x.GetDistanceTo(userLocation) < radius).ToList();

请注意,Entity Framework 会尝试从提供的表达式生成一些 SQL。恐怕这x.GetDistanceTo(userLocation)在 .Where 表达式中可能不起作用,除非您将其转换为IEnumerable, 或调用 。AsEnumerable().ToList().ToArray()之前调用.Where. 或者,也许 EF 足够聪明,可以看到它GeoCoordinate没有映射到表,然后就在那里停止生成 SQL。

编辑

您评论的代码将不起作用:

_UoW.OrganisationRepo.All.Select(x => new GeoCoordinate(x.Latitude, x.Longitude).GetDistanceTo(userLocation) < radius).ToList()

请注意,您选择的是布尔列表,因为您选择的是结果而不是按它们过滤。您不会知道范围内有哪些组织。这就是我们分别使用 .Select 和 .Where 的原因。

尝试这样的事情:

_UoW.OrganisationRepo.All
     .Select(x => new GeoCoordinate(x.Latitude, x.Longitude))
     .ToEnumerable() //or .ToList() or .ToArray(), make sure it's outside of EF's reach (prevent SQL generation for this)
     .Where(x=> x.GetDistanceTo(userLocation) < radius).ToList()

但是,如果您想知道半径范围内有哪些组织,则需要沿途携带更多信息。

var nearbyOrganizations = _UoW.OrganisationRepo.All.ToList()
     .Select(x => new 
      { //use an anonymous type or any type you want
          Org = x, 
          Distance = new GeoCoordinate(x.Latitude, x.Longitude).GetDistanceTo(userLocation) 
     }) //it's probably outside EF's SQL generation step by now, but you could add one more .Select here that does the math if it fails (return the GeoCoordinate object)
     .Where(x=> x.Distance < radius)
     .ToList();

似乎对您了解更多有关 .Where 和 .Select 的信息很有用。它们非常有用。.Where 本质上是一个过滤器,而 .Select 转换对象。由于匿名类型,你可以做任何你想做的事。

请注意,这将从数据库中获取所有对象。您可能希望使用数据库的本机功能来处理地理数据,而 EF 可能支持它。

于 2017-07-27T12:09:14.370 回答
1

我认为您的问题出在您的第一个问题上,Where因为您正在尝试创建一个新的 GeoCoordinate 类,并且Where期望 lambda 的 bool 输出而不是 GeoCoordinate 实例。

我建议进行以下更改:

var result = _UoW.OrganisationRepo.Where(x => new GeoCoordinate(x.Latitude, x.Longitude).GetDistanceTo(userLocation) < radius).ToList();

这将为您提供您感兴趣的半径内的组织列表。

更新

由于 .GetDistanceTo 函数在 SQL 中未知,因此提供程序将无法创建 SQL 查询,因此先前的方法不适用于 IQueryable。

作为替代方案,您能否不使用 EF 5 及以后的空间数据类型?

Rick Strahl 的这篇博文给出了一个查询存储在 SQL 中的地理数据的示例,该数据按距给定位置的距离

https://weblog.west-wind.com/posts/2012/jun/21/basic-spatial-data-with-sql-server-and-entity-framework-50

于 2017-07-27T12:10:04.850 回答