代码之家  ›  专栏  ›  技术社区  ›  harriyott Erik Funkenbusch

使用nhibernate和SQL Server地理位置进行远程高级搜索

  •  1
  • harriyott Erik Funkenbusch  · 技术社区  · 15 年前

    我在一个存储库中有一个现有的高级搜索方法,它检查 FormCollection 对于搜索条件的存在,如果存在,则向搜索添加一个条件,例如

    public IList<Residence> GetForAdvancedSearch(FormCollection collection)
    {
      var criteria = Session.CreateCriteria(typeof(Residence))
        .SetResultTransformer(new DistinctRootEntityResultTransformer());
    
      if (collection["MinBedrooms"] != null)
      {
        criteria
          .Add(Restrictions.Ge("Bedrooms", int.Parse(collection["MinBedrooms"])));
      }
    
      // ... many criteria omitted for brevity
    
      return criteria.List<Residence>();
    }
    

    我还进行了一个基本的距离搜索,以确定每个住宅与搜索条件之间的距离。查询的HBM是

    <sql-query name="Residence.Nearest">
      <return alias="residence" class="Residences.Domain.Residence, Residences"/>
      <return-scalar column="Distance" type="float"/>
      SELECT R.*, dbo.GetDistance(:point, R.Coordinate) AS Distance
      FROM Residence R
      WHERE Distance < 10
      ORDER BY Distance
    </sql-query>
    

    我必须定义一个函数来计算距离,因为在地理函数中没有办法让nhibernate避开冒号:

     CREATE FUNCTION dbo.GetDistance
     (
      @firstPoint nvarchar(100), 
      @secondPoint GEOMETRY
     )
     RETURNS float
     AS
     BEGIN
      RETURN GEOGRAPHY::STGeomFromText(
      @firstPoint, 4326).STDistance(@secondPoint.STAsText()) / 1609.344
     END
    

    存储库调用命名查询,因此:

    return Session
       .GetNamedQuery("Residence.Nearest")
       .SetString("point", String.Format("POINT({0} {1})", latitude, longitude))
       .List();
    

    所以我的问题是:如何将两者结合起来(或者从头开始),这样我就可以过滤高级搜索结果,只包括搜索位置10英里范围内的住宅?

    更新 我尝试使用nhibernate.space,代码如下:

    criteria.Add(SpatialExpression.IsWithinDistance(
        "Coordinate", new Coordinate(latitude, longitude), 10));
    

    但是 SpatialExpression.IsWithinDistance 返回A System.NotImplementedException .

    2 回复  |  直到 10 年前
        1
  •  0
  •   John Rayner    15 年前

    你看到了吗 NHibernate.Spatial project ?这可能为您的问题提供一个简单的解决方案。

    另一种选择是创建自己的 ICriterion -如果你从 AbstractCriterion 而且您的目标是特定的数据库平台。这将允许您将距离函数与其他标准结合起来。

        2
  •  0
  •   harriyott Erik Funkenbusch    14 年前

    创建一个投影,该投影实际上会向结果中添加一个新的距离列,该距离列通过称为UDF计算,然后对其添加限制:

    var query = String.Format(
        "dbo.GetDistance('POINT({0} {1}', Coordinate) AS Distance", 
        latitude, longitude);
    criteria
        .Add(Restrictions.Le(Projections.SqlProjection(
            query, 
            new [] {"Distance"}, 
            new [] {NHibernateUtil.Double}), 10));
    

    更新

    注意:虽然我贴出来的时候这肯定是有效的,但它已经不起作用了。NHibernate不喜欢dbo后面的“.”,并说

    “无法解析属性:dbo,属于:residences.domain.residence”。

    如果我移除“dbo”,我会

    “'getDistance'不是可识别的内置函数名。”