LINQ to NHSpatial

48 views
Skip to first unread message

David Pfeffer

unread,
Aug 13, 2010, 1:54:20 PM8/13/10
to nhibernate-development
Hello all,

So I'm having a bit of difficulty trying to build a LINQ to NH Spatial provider. Specifically, I have not found a way to specify the user type for IGeometry types. So, even though I have the backend LINQ parser code plugged in, queries fail due to "No persister for: GeoAPI.Geometries.IGeometry."

In Criteria and HQL specifying the user type is easy. How do I specify the user type for a LINQ query so that my backend code is invoked?

Thanks!

Fabio Maulo

unread,
Aug 16, 2010, 10:53:42 AM8/16/10
to nhibernate-...@googlegroups.com

David Pfeffer

unread,
Aug 16, 2010, 11:14:27 AM8/16/10
to nhibernate-...@googlegroups.com
I would attempt to write a fix, but this isn't just a bug. There's a design issue to be resolved here as to how to go about implementing this.

One idea I have is to create an extension method, WithUserType(this object obj, IType type) that will be used to inform the LINQ provider as to the type (UserType, etc.).
If this sounds like a good idea, would anyone be able to provide a few pointers as to where to look to hook this in? I will take a look and see if I can patch.

Thanks.

Fabio Maulo

unread,
Aug 16, 2010, 5:16:52 PM8/16/10
to nhibernate-...@googlegroups.com

El 16/08/2010, a las 12:14, David Pfeffer <byt...@bytenik.com> escribió:

I would attempt to write a fix, but this isn't just a bug. There's a design issue to be resolved here as to how to go about implementing this.

I'm not so sure about " a design issue"...  


One idea I have is to create an extension method, WithUserType(this object obj, IType type) that will be used to inform the LINQ provider as to the type (UserType, etc.).

Try to write an example where the Type can't be inferred.
Thanks.

David Pfeffer

unread,
Aug 19, 2010, 10:02:40 AM8/19/10
to nhibernate-...@googlegroups.com
An example from NH Spatial --

I have a type...

public class Breadcrumb
{
     public virtual int Id { get; set; }
     public virtual IGeometry Position { get; set; }
     public virtual string Description { get; set; }
}

...and in my code I do the following...

IGeometry center;
// code here to set the center to a useful point
var breadcrumbsWithinDistance = session.Query<Breadcrumb>().Where(x => x.Position.Distance(center) < 500);

Now, how would the LINQ engine know the mapping of center? Since it isn't a property of a mapped class there's nothing already specified here for what user type to use.

I propose that line could be replaced as...

var breadcrumbsWithinDistance = session.Query<Breadcrumb>().Where(x => x.Position.Distance(center.AsUserType(new NHibernate.Type.CustomType(typeof(NHibernate.Spatial.Type.MsSql2008GeographyType), null) )) < 500);

(or whatever other user type is appropriate for that database engine)

What do you think?

Fabio Maulo

unread,
Aug 19, 2010, 11:41:01 AM8/19/10
to nhibernate-...@googlegroups.com
I think that first we have to fix issues related to Linq-Provider and custom-types then we have to add some linq-extensions inside NHibernate-Spatial (as it has HQL extensions).
--
Fabio Maulo

David Pfeffer

unread,
Aug 19, 2010, 2:46:04 PM8/19/10
to nhibernate-...@googlegroups.com
Right, which is exactly what I'm working on right now...

What I'm proposed is a fix for the issues relating to the LINQ provider custom types. What do you all think of the solution?

Fabio Maulo

unread,
Aug 19, 2010, 9:58:20 PM8/19/10
to nhibernate-...@googlegroups.com
ExpressionParameterVisitor
NHibernateUtil.GuessType(expression.Type)

We can't guess the type without the "usage context".
myClass.MyProperty == something

to really guess/know the type of "something" we have to know the mapping of "MyProperty".

We may have an ugly patch-extension method where the user can specify the NH's IType but... well... that will be a patch not a solution.
--
Fabio Maulo

David Pfeffer

unread,
Aug 19, 2010, 10:41:48 PM8/19/10
to nhibernate-...@googlegroups.com
That might work for instances of direct comparison, but what of an extension method where the arguments are different... for example this theoretical method:

public static bool ExampleMethod(this int x, ExampleType y) { ... }

In this example you have a usage but the usage isn't going to help you guess the mapping type for ExampleType. The only way I can think of doing that is with the extension method patch. If you have another idea I'd be excited to hear it though. Otherwise would that be an acceptable solution and should I go ahead with it?

Fabio Maulo

unread,
Aug 19, 2010, 10:44:36 PM8/19/10
to nhibernate-...@googlegroups.com

David Pfeffer

unread,
Aug 19, 2010, 10:46:57 PM8/19/10
to nhibernate-...@googlegroups.com
I've seen this blog post but I'm not sure how it applies. I'm aware of how you'd implement ExampleMethod but not how you'd specify the user type for mapping of ExampleType.

Fabio Maulo

unread,
Aug 19, 2010, 10:51:32 PM8/19/10
to nhibernate-...@googlegroups.com
In the implementation of of your Generator for your extension you can specify the IType.
In this way you have a real LINQ extension working in RAM as it will work using NHibernate.

If you are in a "safe-zone" where you can have the reference to NHibernate dll, you can use any other query-system provided by NHibernate, you don't need LINQ.
--
Fabio Maulo

David Pfeffer

unread,
Aug 19, 2010, 10:56:07 PM8/19/10
to nhibernate-...@googlegroups.com
How would I provide that? As far as I know, you'd have to use the IHqlExpressionVisitor argument to the BuildHql method to "visit" that argument of the extension method. Otherwise any expression passed to that argument would not be evaluated. How would I specify the user type in this instance?

Fabio Maulo

unread,
Aug 19, 2010, 11:14:48 PM8/19/10
to nhibernate-...@googlegroups.com
I don't know what "ExampleMethod" and "ExampleType" are but if "ExampleType" have some meaning for persistence you can translate it to IType as we are doing for any parameter.

David, I know that perhaps it doesn't look easy to implement but this is OSS.

btw... backing to the title of this thread...
NHSP has the registration of some HQLFunction you can map the name of the extension with the name of the HQLFunction (using exactly the same name or using a dictionary) and then you can use
treeBuilder.MethodCall(theHqlFunctionName, visitor.Visit(expression).AsExpression());

or even more easy you can use LinqExtensionMethodAttribute to mark your extension and there specify the HQLFunction's name
--
Fabio Maulo

David Pfeffer

unread,
Aug 20, 2010, 6:51:37 AM8/20/10
to nhibernate-...@googlegroups.com
That is roughly what I have. However, when you call visitor.Visit on the argument in question there is the exception because of it not knowing which IType to map to.

The ExampleMethod and ExampleType I refer to was from my example in an earlier email...


public static bool ExampleMethod(this int x, ExampleType y) { ... }

Even if I map this method using a generator with buildHql method, I still don't have a way to specify the IType for ExampleType from the above example.

Fabio Maulo

unread,
Aug 20, 2010, 8:00:40 AM8/20/10
to nhibernate-...@googlegroups.com
Can you transform ExampleMethod and ExampleType in something concrete.... for example Distance and IGeography or something like that ?

Have you the set of extensions methods for NHSP ?
--
Fabio Maulo

David Pfeffer

unread,
Aug 20, 2010, 8:10:54 AM8/20/10
to nhibernate-...@googlegroups.com
Yes, I was trying to make it general so that it would be useful for someone else working on something different in the future... here's a concrete example:

There's a method called Distance, as you mentioned, which is:
public static double Distance(IGeometry that) { ... }

It isn't an extension method but rather is an implementation method.

So I go and make a class SpatialGenerator which implements IHqlGeneratorForMethod and whose SupportedMethods property returns a list containing that Distance method, gotten by
ReflectionHelper.GetMethodDefinition<IGeometry>(x => x.Distance(null))
Then BuildHql returns using the MethodCall as you mentioned earlier...
return treeBuilder.MethodCall("NHSP.Distance", visitor.Visit(targetObject).AsExpression(), visitor.Visit(arguments[0]).AsExpression());
The generator is hooked in by a child of DefaultLinqToHqlGeneratorsRegistry that calls RegisterGenerator, and in my test's config I do
myconfig.SetProperty(Environment.LinqToHqlGeneratorsRegistry,
                                                          typeof (Extensions.SpatialGeneratorsRegistry).
                                                              AssemblyQualifiedName)
I attempt to run a query next...
var test = s.Query<TestModel.PositionRecord>().Where(x => x.Position.Distance(mylocation) <= 500).ToList();
If this were to work properly, I should receive back all PositionRecord entities where the distance from that entity to mylocation is within 500 meters. But of course I get the "No persister for: GeoAPI.Geometries.IGeometry" exception because there's no way for it to know to use the IUserType I have for IGeometry. I haven't seen any place to plug this in as of yet.

Steve Strong

unread,
Aug 30, 2010, 2:55:44 PM8/30/10
to nhibernate-...@googlegroups.com
I've assigned the Jira issue to myself and will take a look to determine what the fix should be.  If it's easy, thne I'll just drop it in, if I need help then I may enlist you David :)
Reply all
Reply to author
Forward
0 new messages