Geospatial Point Mapping in Fluent NHibernate

2019-04-15 21:30发布

问题:

I'm struggling to get Fluent NHibernate to play nice with SQL Server's Geospatial types. I want to store a geographic point in my Place class, but I keep getting an NHibernate configuration error when I run my ASP.NET MVC app:

Method 'SetParameterValues' in type 'NHibernate.Spatial.Type.GeometryType' from
assembly 'NHibernate.Spatial, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null' does not have an implementation.

Update: This is caused by an out-dated NHibernate.Spatial DLL. Referencing the latest version (2.2+) solves the problem. Kudos to psousa for leading me to a solution.

Place class:

using System;
using GisSharpBlog.NetTopologySuite.Geometries;
using NHibernate.Validator.Constraints;

namespace MyApp.Data.Entities
{
    public class Place
    {
        public virtual Guid Id { get; set; }
        public virtual string Name { get; set; }
        public virtual Point Location { get; set; }
    }
}

Fluent Place Mapping:

using MyApp.Data.Entities;
using FluentNHibernate.Mapping;
using NHibernate.Spatial.Type;

namespace MyApp.Data.Mappings
{
    public class PlaceMap : ClassMap<Place>
    {
        public PlaceMap()
        {
            ImportType<GisSharpBlog.NetTopologySuite.Geometries.Point>();

            Id(x => x.Id);
            Map(x => x.Name);

            Map(x => x.Location)
                .CustomType(typeof(GeometryType));
        }
    }
}

Fluent NHibernate Configuration:

var cfg = Fluently.Configure()
    .Database(MsSqlConfiguration.MsSql2008
        .ConnectionString(c => c.FromConnectionStringWithKey(connectionStringKey))
    .ShowSql()
    .Dialect("NHibernate.Spatial.Dialect.MsSql2008GeographyDialect,NHibernate.Spatial.MsSql2008"))
    .ExposeConfiguration(BuildSchema)
    .Mappings(x => x.FluentMappings.AddFromAssembly(typeof(UserMap).Assembly)
    .Conventions.AddFromAssemblyOf<ColumnNullabilityConvention>());

回答1:

You're using a Geography dialect but using a CustomType of Geometry on your mapping. You should use a custom type of Geography. Something like:

public class PlaceMap : ClassMap<Place>
{
    public PlaceMap()
    {
        Id(x => x.Id);
        Map(x => x.Name);

        Map(x => x.Location).CustomType(typeof(MsSql2008GeographyType)); //for SQL2008
    }
}

Also, there's something else that you may need to do. If your spatial column has an SRID different from 0 (zero), and if you want to skip NH xml mappings, you'll need to declare a custom type like this:

public class Wgs84GeographyType : MsSql2008GeographyType
{
    protected override void SetDefaultSRID(GeoAPI.Geometries.IGeometry geometry)
    {
        geometry.SRID = 4326;
    }
}

And then use it on your mapping:

public class PlaceMap : ClassMap<Place>
{
    public PlaceMap()
    {
        Id(x => x.Id);
        Map(x => x.Name);

        Map(x => x.Location).CustomType(typeof(Wgs84GeographyType));
    }
}

UPDATE:

You should be referencing NHibernate.Spatial.MsSql2008.dll, and I would advise you to use the strongly-typed Dialect method in your database configuration.

.Dialect<MsSql2008GeographyDialect>()