使用 Coordinate 和存储库模式在 x 英里内获得结果
Get results within x miles Using GeoCoordinate with respository pattern
我想获取用户输入位置 x 英里范围内的所有组织的列表。这将转换为 long/lat 位置。
组织以经度和纬度存储在数据库中。
我正在使用带 entity framework 的 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();
}
为了查询我的数据上下文中的数据,我为每个实体使用一个服务 class,一个 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();
当我尝试 运行 这个查询时,我得到:
"cannot implicitly convert type system.device.location.geoCoordinate to 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);
}
感谢下面这个解决方案的帮助,虽然似乎所有对象都必须在 orfer 中查询才能工作,但似乎查询更适合数据库上的 运行,我'我必须再仔细研究一下。
Where
方法必须 return 一个布尔值。
_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
没有映射到 table,然后就停止生成 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 可能支持它。
我认为你的问题出在你的第一个 Where
上,因为你正在尝试创建一个新的 GeoCoordinate class 并且 Where
期望来自 lambda 的 bool 输出而不是 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 的这篇博客 post 给出了一个示例,该示例根据与给定位置
的距离查询存储在 SQL 中的地理数据
我想获取用户输入位置 x 英里范围内的所有组织的列表。这将转换为 long/lat 位置。
组织以经度和纬度存储在数据库中。
我正在使用带 entity framework 的 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();
}
为了查询我的数据上下文中的数据,我为每个实体使用一个服务 class,一个 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();
当我尝试 运行 这个查询时,我得到:
"cannot implicitly convert type system.device.location.geoCoordinate to 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);
}
感谢下面这个解决方案的帮助,虽然似乎所有对象都必须在 orfer 中查询才能工作,但似乎查询更适合数据库上的 运行,我'我必须再仔细研究一下。
Where
方法必须 return 一个布尔值。
_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
没有映射到 table,然后就停止生成 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 可能支持它。
我认为你的问题出在你的第一个 Where
上,因为你正在尝试创建一个新的 GeoCoordinate class 并且 Where
期望来自 lambda 的 bool 输出而不是 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 的这篇博客 post 给出了一个示例,该示例根据与给定位置
的距离查询存储在 SQL 中的地理数据