我想获取用户输入的位置x英里范围内所有组织的列表。这将转换为长/纬度位置。
组织存储在数据库中,带有长和纬度。
我正在使用带有实体框架的 MVC 和具有存储库模式的工作单元来访问数据集。
这是我的实体存储库:
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();
当我尝试运行此查询时,我得到:
"无法将类型系统.设备.位置.geoT坐标隐式转换为布尔值">
谁能帮忙?
** 更新 - 工作解决方案 **
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);
}
感谢下面对这个解决方案的帮助,虽然似乎必须在 orfer 中查询所有对象才能正常工作,但似乎查询更适合在数据库上运行,我将不得不对此进行更多研究。
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();
请注意,实体框架会尝试从提供的表达式生成一些 SQL。恐怕x.GetDistanceTo(userLocation)
可能无法在.其中表达式,除非你将其转换为IEnumerable
,或调用 .AsEnumerable()
或.ToList()
或.ToArray()
,然后再呼叫.Where
。或者,EF 可能足够聪明,可以看到GeoCoordinate
未映射到表,然后停止生成 SQL。
编辑
您注释的代码将不起作用:
_UoW.OrganisationRepo.All.Select(x => new GeoCoordinate(x.Latitude, x.Longitude).GetDistanceTo(userLocation) < radius).ToList()
请注意,您选择布尔值列表是因为您选择的是结果,而不是按结果进行筛选。您不会知道哪些组织在半径范围内。这就是我们使用.选择和。在哪里分开。
尝试这样的事情:
_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();
似乎对您了解更多有关.哪里和.选择。它们非常有用。.其中是过滤器的基本内容,以及 .选择转换对象。由于匿名类型,您可以做任何您想做的事情。
请注意,这将从数据库中获取所有对象。您可能希望使用数据库的本机功能来处理地理数据,EF 可能支持它。
我认为您的问题出在您的第一个Where
上,因为您正在尝试创建一个新的 GeoCoordinate 类,并且Where
期望来自 lambda 而不是 GeoCoordinate 实例的布尔输出。
我建议进行以下更改:
var result = _UoW.OrganisationRepo.Where(x => new GeoCoordinate(x.Latitude, x.Longitude).GetDistanceTo(userLocation) < radius).ToList();
这将为您提供您感兴趣的半径内的组织列表。
更新
前面的不适用于 IQueryable,因为提供程序将无法创建 SQL 查询,因为 .GetDistanceTo 函数在 SQL 中是未知的。
作为替代方法,您不能在 EF 5 及更高版本中使用空间数据类型吗?
Rick Strahl 的这篇博客文章给出了一个按与给定位置的距离查询存储在 SQL 中的地理数据的示例
https://weblog.west-wind.com/posts/2012/jun/21/basic-spatial-data-with-sql-server-and-entity-framework-50