65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 SQL Server 2012、Entity Framework 5 和 ASP.NET MVC 中的空间数据

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.82/5 (7投票s)

2012年8月25日

CPOL

3分钟阅读

viewsIcon

48084

Entity Framework 5 中备受期待的功能之一是空间支持。

引言

自 SQL 2008 发布以来,许多开发人员一直在请求在 Entity Framework 中支持空间数据类型。对于 Microsoft ORM 用户来说,能够使用空间数据快速创建 .NET 业务应用程序是一个梦想。今年五月,Entity Framework 5 (EF5) 的发布候选版发布了。与早期 EF 版本相比,此版本提高了性能,并且还支持空间类型。EF5 中的空间功能需要 .NET 4.5。

EF5 中的空间功能需要 .NET 4.5。这意味着您需要安装 Visual Studios 2012。您可以在此处下载 VS 2012 的发布候选版:http://www.microsoft.com/visualstudio/en-us  

背景

Entity Framework 中的空间数据

在 Entity Framework 5.0 on .NET 4.5 之前,要使用上述数据,需要使用存储过程或原始 SQL 命令来访问空间数据。然而,在 Entity Framework 5 中,Microsoft 引入了新的 DbGeometryDbGeography 类型。这些不可变的位置类型提供了大量用于处理几何函数中的空间点(进而可用于执行我上面 SQL 语法中所述的常见空间查询)的功能。

DbGeography/DbGeometry 类型是不可变的,这意味着一旦创建,就无法对其进行写入。它们有点特殊,您需要使用工厂方法来实例化它们——它们没有构造函数(),并且您无法为其属性(如 Latitude 和
Longitude)赋值。

值得一提的是,这些类型定义在 System.Data.Spatial 命名空间中的 System.Data.Entity 程序集中。到目前为止,您可能已经使用了定义在 Microsoft.SqlServer.Types 命名空间中的 SqlGeometry
SqlGeography 类型。

例如,一个名为 world 的实体包含一个类型为 DbGeometry 的 geom 属性。

[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=true)]
[DataMemberAttribute()]
public global::System.Data.Spatial.DbGeometry geom
{
    get
    {
        return _geom;
    }
    set
    {
        OngeomChanging(value);
        ReportPropertyChanging("geom");
        _geom = StructuralObject.SetValidValue(value, true, "geom");
        ReportPropertyChanged("geom");
        OngeomChanged();
    }
}
private global::System.Data.Spatial.DbGeometry _geom;
partial void OngeomChanging(global::System.Data.Spatial.DbGeometry value);
partial void OngeomChanged();  

使用代码

使用 Entity Framework 5 RC 和空间数据的 ASP.NET MVC 4 应用程序。

控制器 (Controller)

控制器返回一个包含 UI 控件的视图,这些控件显示空间元素。

数据维护在控制器中实现。本文也侧重于 MVC 的这部分。

#region DashboardJs
public ActionResult DashboardJs()
{
    ViewBag.Message = "Spatial Data Dashboard";

    return View();
}
#endregion //DashboardJs

空间数据维护

当您拥有来自 DbGeometry / DbGeography 类型的数据时,您无法对其进行序列化。有两种选择:

将空间数据类型转换为 WKT(文本标记语言),并作为 JSON 或 XML 的一部分发送到客户端(视图)。

  • 使用您可以序列化自己的类。
  • 此示例演示了第二种方法。

您需要有一个方法可以将结果序列化为 JSON,以便在视图中使用。

#region CountryByName
[OutputCache(VaryByParam = "countryName", Duration = 120)]
public JsonResult CountryByName(string countryName)
{
    switch (countryName)
    {
        case "UK":
            countryName = "United Kingdom";
            break;
        case "USA":
            countryName = "United States";
            break;
    }
    var results = spDemo.worlds.Where(x => x.CNTRY_NAME == countryName);


    List<CountryInfo> ret = new List<CountryInfo>();
    foreach (world country in results)
    {
        CountryInfo info = new CountryInfo
        {
            Id = country.ID,
            Code = country.CODE,
            CountryName = country.CNTRY_NAME,
            Population = country.POP_CNTRY,
            Extend = GetGeometryBoundary(country)
        };
        ret.Add(info);
    }

    var retVal = Json(ret, JsonRequestBehavior.AllowGet);
    return retVal;
}
#endregion //CountryByName

您还需要使用几个辅助方法来获取表示 DbGeometry 实例包络的点列表。请记住,DbGeometry/DbGeography 的点索引从 1 开始!

#region GetGeometryBoundary
public static SpatialRect GetGeometryBoundary(world country)
{
    List<SpatialPoint> multiPoints = new List<SpatialPoint>();
    var numPoints = country.geom.Envelope.ElementAt(1).PointCount;
    for (int i = 1; i <= numPoints; i++)
    {
        SpatialPoint pnt = new SpatialPoint((double)(
           country.geom.Envelope.ElementAt(1).PointAt(i).XCoordinate), 
           (double)(country.geom.Envelope.ElementAt(1).PointAt(i).YCoordinate));
        multiPoints.Add(pnt);

    }
    SpatialRect rect = multiPoints.GetBounds();
    return rect;
}
#endregion //GetGeometryBoundary

用于序列化数据的辅助类

#region CountryInfo
public class CountryInfo
{
    public int Id { get; set; }
    public string Code { get; set; }
    public string CountryName { get; set; }
    public long? Population { get; set; }
    public SpatialRect Extend { get; set; }

}
#endregion //CountryInfo

用于保存点数据的辅助类。

#region SpatialPoint
public class SpatialPoint
{
    public SpatialPoint(double x, double y)
    {
        this.X = x;
        this.Y = y;
    }

    public double X { get; set; }
    public double Y { get; set; }
}
#endregion //SpatialPoint

用于保存几何对象(此处为国家)范围的辅助类。

#region SpatialRect
public struct SpatialRect
{
    public SpatialRect(double pLeft, double pTop, double pWidth, double pHeight)
    {
        left = pLeft;
        top = pTop;
        width = pWidth;
        height = pHeight;
    }

    public double left; 
    public double top; 
    public double width; 
    public double height; 
}
#endregion //SpatialRect

用于获取点列表边界的扩展方法。

#region Extensions
public static class Extensions
{

    #region GetBounds
    public static SpatialRect GetBounds(this IList<SpatialPoint> points)
    {
        double xmin = Double.PositiveInfinity;
        double ymin = Double.PositiveInfinity;
        double xmax = Double.NegativeInfinity;
        double ymax = Double.NegativeInfinity;

        SpatialPoint p;
        for (var i = 0; i < points.Count; i++)
        {
            p = points[i];
            xmin = Math.Min(xmin, p.X);
            ymin = Math.Min(ymin, p.Y);

            xmax = Math.Max(xmax, p.X);
            ymax = Math.Max(ymax, p.Y);
        }

        if (Double.IsInfinity(xmin) || Double.IsInfinity(ymin) || 
                  Double.IsInfinity(ymin) || Double.IsInfinity(ymax))
        {
            return new SpatialRect(0.0, 0.0, 0.0, 0.0);
        }

        return new SpatialRect(xmin, ymin, xmax - xmin, ymax - ymin);
    }
    #endregion //GetBounds
} 
#endregion //Extensions

视图

视图显示一个带有 UI 组件的仪表板。

示例中最重要的一部分是如何查询控制器返回空间数据的(此处为国家范围的)方法。

关于一种可能的解决方案的更多细节,您可以在我的博客 这里 找到。

在此处下载源代码:此处

兴趣点 

此方法可用于不同的平台。本文提到了 JSON 序列化,但您可以使用不同的 Web 服务(如 WCF、OData)以不同的方式(二进制、XML 或 JSON)序列化空间数据。希望这种方法对各种客户端平台的开发人员有用。

历史  

我将尽快尝试为移动 Web 平台添加实现。

© . All rights reserved.