Usando dados espaciais no MVC ASP.Net com SQL Server
Um dos recursos altamente esperados no Entity Framework 5 é um suporte espacial.
Muitos desenvolvedores têm solicitado, desde o lançamento do SQL 2008, suporte a tipos de dados espaciais no Entity Framework. Era um sonho para os usuários do Microsoft ORM criar aplicativos de negócios .NET rapidamente, usando dados espaciais. Em maio deste ano, foi anunciado o release candidate para o Entity Framework 5 (EF5). Esta versão aumentou o desempenho quando comparada à versão anterior do EF e também tem suporte para tipos espaciais. A funcionalidade espacial no EF5 requer o .NET 4.5.
A funcionalidade espacial no EF5 requer o .NET 4.5. Isso significa que você precisará do Visual Studios 2012 instalado. Você pode baixar o release candidate para o VS 2012 aqui: http://www.microsoft.com/visualstudio/en-us
Dados espaciais no Entity Framework
Antes do Entity Framework 5.0 no .NET 4.5, o consumo dos dados acima exigia o uso de procedimentos armazenados ou comandos SQL brutos para acessar os dados espaciais. No Entity Framework 5, no entanto, a Microsoft introduziu os novos tipos DbGeometry e DbGeography. Esses tipos de localização imutáveis fornecem várias funcionalidades para manipular pontos espaciais usando funções de geometria que, por sua vez, podem ser usadas para fazer consultas espaciais comuns, como descrevi na sintaxe SQL acima.
Os tipos DbGeography/DbGeometry são imutáveis, o que significa que você não pode gravar neles depois de criados. Eles são um pouco estranhos, pois você precisa usar métodos de fábrica para instanciá-los - eles não têm construtor () e você não pode atribuir a propriedades como Latitude e Longitude.
É importante mencionar que esses tipos são definidos no assembly System.Data.Entity no namespace System.Data.Spatial. Até agora, você provavelmente já usou os tipos SqlGeometry e SqlGeography, definidos no namespace Microsoft.SqlServer.Types
Criando um modelo com dados espaciais
Vamos começar criando um modelo simples do Entity Framework que inclui entidades de bancos de dados de exemplo Northwind e SpatialDemo. A entidade chamada world contém uma propriedade geom do tipo DbGeometry. O exemplo está usando o SQL Server 2012, mas você pode executá-lo com o SQL Server 2008.
[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();
Entidades Northwind

Banco de dados SpatialDemo de exemplo, incluindo dados espaciais. Tabela mundo a arquivado a partir do tipo Geometry, denominado "geom". Este campo contém os contornos dos países como polígonos

O mundo da entidade, gerado a partir do banco de dados SpatialDemo, contém um campo "geom" do tipo Geometry.

ASP.Net Aplicativo MVC 4 com Entity Framework 5 RC e dados espaciais
Agora é muito fácil usar dados espaciais em ASP.Net aplicativos MVC.
- Controller
O controlador retorna uma exibição que contém um painel com Infragistics controles jQuery.
#region DashboardJs
public ActionResult DashboardJs()
{
ViewBag.Message = "Spatial Data Dashboard";
return View();
}
#endregion //DashboardJs
- Spatial Data Maintenance
Quando você tem dados do tipo DbGeometry / DbGeography, não pode serializá-los. Existem duas opções:
- Para converter o tipo de dados espaciais em WKT (Well Known Text) e enviá-lo ao cliente (exibição) como parte de JSON ou XML
- Para usar suas próprias classes que podem ser serializadas
Este exemplo demonstra a segunda abordagem
O método CountryByName serializa os resultados em JSON para que seja possível usá-lo na exibição
#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 ret = new List();
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
GetGeometryBoundary é um método auxiliar usado para obter uma lista de pontos, representando um envelope de uma instância DbGeometry. Não se esqueça de que os índices de ponto DbGeometry/DbGeography começam em 1 !.
#region GetGeometryBoundary
public static SpatialRect GetGeometryBoundary(world country)
{
List multiPoints = new List();
var numPoints = country.geom.Envelope.ElementAt(1).PointCount;
for (int i = 1; i <= numpoints="" i="" pre="">
{
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
ContryInfo é uma classe auxiliar usada para serializar dados
#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
SpatialPoint é uma classe auxiliar para manter os dados de um ponto. Você pode usar:
#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
SpatialRect é uma classe auxiliar para manter uma extensão do país
#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
GetBounds é um método de extensão usado para obter um limite da lista de pontos.
#region Extensions
public static class Extensions
{
#region GetBounds
public static SpatialRect GetBounds(this IList 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
Vista
A visualização apresenta um painel de Infragistics jQuery Grid, Chart e Map.
A parte mais importante no exemplo é como consultar o método do controlador que retorna dados espaciais (o país estende, neste caso).
var countryUrl = "/Home/CountryByName?countryName=" + args.row.element[0].cells[1].textContent
...
$.getJSON(countryUrl,
function (json, text) {
$.each(json, function (index, value) {
var country = value;
var extend = country["Extend"];
var zoom = $("#map").igMap("getZoomFromGeographic", extend);
$("#map").igMap("option", "windowRect", zoom);
});
});
Infragistics jQuery Map instance definition.
$("#map").igMap({
width: "500px",
height: "500px",
panModifier: "control",
horizontalZoomable: true,
verticalZoomable: true,
windowResponse: "immediate",
overviewPlusDetailPaneVisibility: "visible",
seriesMouseLeftButtonUp: function (ui, args) {
var tets = args;
}
});
Infragistics jQuery Grid com zoom ao redor do país do cliente selecionado.
$('#grid').igGrid({
virtualization: false, height: 280, width: 650,
dataSource: "/Home/Customers",
autoGenerateColumns: false,
columns: [
{ headerText: "Customer ID", key: "CustomerID", width: "120px", dataType: "string" },
{ headerText: "Country", key: "Country", width: "150px", dataType: "string" },
{ headerText: "City", key: "City", dataType: "string" },
{ headerText: "Contact Name", key: "ContactName", dataType: "string" },
{headerText: "Phone", key: "Phone", dataType: "string" }
],
features:
[{
name: 'Selection',
mode: 'row',
multipleSelection: false,
rowSelectionChanged: function (ui, args) {
$("#chart").igDataChart({
dataSource: "/Home/Orders?userID=" + args.row.element[0].cells[0].textContent
});
selected = args.row.element[0].cells[0].textContent; //keep track of selected user
var countryUrl = "/Home/CountryByName?countryName=" + args.row.element[0].cells[1].textContent
$.getJSON(countryUrl,
function (json, text) {
$.each(json, function (index, value) {
var country = value;
var extend = country["Extend"];
var zoom = $("#map").igMap("getZoomFromGeographic", extend);
$("#map").igMap("option", "windowRect", zoom);
});
});
}
}
,
{
name: 'Sorting',
type: "remote"
},
{
name: 'Paging',
type: "local",
pageSize: 10
}]
})
Dados espaciais em ação
Execute o aplicativo e selecione o "Painel de Dados Espaciais" no menu.


Escolha um cliente no igGrid e veja como o mapa mostra o país de onde o cliente


Você pode baixar o código-fonte do exemplo aqui.
Para executar este exemplo, você pode baixar bancos de dados de exemplo Northwind e SpatialDemo
Como sempre, você pode nos seguir no Twitter: @mihailmateev e @ Infragistics, todos os tweets com hashtag #infragistcs e manter contato no Facebook, Google+, LinkedIn e Infragistics Grupo de Usuários de Amigos!