3

I'm trying to find a way to cause the executed query sent by NHibernate (sp_executesql) to the database to use a custom sql data type. I've got a Linq expression in C# that I'm trying to run and uses a custom HqlGenerator to support the IsDescendantOf method of HierarchyId.

My C# properties for the HierarchyId are currently being represented as strings ( didn't need the complexity of the Microsoft.SqlServer.Types representation of it).

But the problem is that the resulting string being sent to sp_executesql is indicating that the parameters being passed are NVARCHAR(400), regardless of anything I change with the mappings at all. But this is causing problems with the indexing of my HierarchyId field, the query is timing out because it's not using my index at all. But as soon as I change the parameter type to HierarchyId, it runs in 0 to 1 seconds. So I'm trying to find a way to get the sp_executesql statement to specify the parameters being sent are HierarchyId and not NVARCHAR(4000).

I've tried changing the type in the mapping, I've tried changing the custom SqlType in the mapping. I'm not sure how else I can influence NHibernate to manually specify the type for the call to sp_executesql.

I really need a solution at this point, but I'm not sure how the innards of NHibernate determine the type of a parameter.

Updated with code

C# Code simplified expression for the scenario I'm dealing with

var currentUserScopedHierarchy = "/11/";
session.Query<ClaimSubmission>()
    .Where(x => x.Hierarchy.IsDescendantOf(currentUserScopedHierarchy))
    .OrderByDescending(x => x.DateCreated).ToList();

the Hierarchy property on my ClaimSubmission class is a string field. With the HqlGenerator I have for IsDescendantOf, the resulting SQL observed in SQL Server Profiler is as follows

SQL

exec sp_executesql N'/* [expression] */select claimsubmi0_.ObjectId     as ObjectId22_,
       claimsubmi0_.DateCreated  as DateCrea2_22_,
       claimsubmi0_.DateModified as DateModi3_22_,
       claimsubmi0_.Name         as Name22_,
       claimsubmi0_.SearchText   as SearchText22_,
       claimsubmi0_.CreatedBy    as CreatedBy22_,
       claimsubmi0_.ModifiedBy   as ModifiedBy22_,
       claimsubmi0_.OwnerId      as OwnerId22_,
       claimsubmi0_.Hierarchy    as Hierarchy22_,
       claimsubmi0_.TypeId       as TypeId22_,
       claimsubmi0_.Terminate    as Terminate22_
from   PseudoObjects claimsubmi0_
where  /* removed extra code here that delt with discriminators that isn't relevant */
    claimsubmi0_.Hierarchy.IsDescendantOf(@p0) = 1
order  by claimsubmi0_.DateCreated desc'
,N'@p0 nvarchar(4000)' ,@p0=N'/11/'

The problem is that last bit where the parameter with a value of "/11/" is being passed as a NVARCHAR(400). As is, this query takes about 20 seconds (30+ once I add back in other logic that I removed to get a more simplified example), and it's because the data type is causing it to ignore my indexes. If I change the type to HierarchyId, the query is instant. If I run the query out side of a call to sp_executesql and inline the parameter value, it's instant as well.

HqlGenerator Related

Extension method

    public static bool IsDescendantOf(this string childHierarchy, string parentHierarchy)
    {
        return childHierarchy.StartsWith(parentHierarchy);
    }

If it's relevant, here's the HqlGenerator as well

public class IsDescendantOfGenerator : BaseHqlGeneratorForMethod
{
    public IsDescendantOfGenerator()
    {
        SupportedMethods = new[]
            {
                ReflectionHelper.GetMethodDefinition(() => ExtensionMethods.IsDescendantOf(null, null))
            };
    }

    public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
    {
        return treeBuilder.BooleanMethodCall("_IsDescendantOf", new[] { visitor.Visit(arguments[0]).AsExpression(), visitor.Visit(arguments[1]).AsExpression() });
    }
}

And as part of the NHibernate setup, I have this

Config.AddSqlFunction("_IsDescendantOf", new SQLFunctionTemplate(NHibernateUtil.Boolean, "?1.IsDescendantOf(?2) = 1"));
Nick Albrecht
  • 16,607
  • 10
  • 66
  • 101
  • Could you please provide a snippet of code about what you're actually doing? Are you issuing queries through criteria, or other? When you add parameters, there are lot of overrides with the actual parameter types. – Alberto Chiesa Aug 30 '16 at 00:39
  • If you are using Nhibernate mapping file, you can use type="AnsiString" to force nhibernate to use varchar data type and avoid nvarchar – FakeisMe Aug 30 '16 at 07:01
  • I've added some code for the situation I'm in, I didn't add it at first because I had worked it down to the specific need of just changing the data type in the call to `sp_executesql`. Would really appreciate some help with this stuff though. – Nick Albrecht Aug 30 '16 at 23:32
  • You try what is suggested in this [OpenStack post](http://stackoverflow.com/questions/3374904/sql-2008-hierarchyid-support-in-nhibernate)? – btberry Aug 31 '16 at 00:24
  • I did, but that is mainly targeting support for using the type defined in `Microsoft.SqlServer.Types` in your C# code on your model's propertues, and not about adding support for the HierarchyId type being passed back to the server – Nick Albrecht Aug 31 '16 at 22:24
  • Sadly I didn't get any help with this, so my bounty went to waste. I'd offer another bounty if I thought I'd get any traction but my perception is that NHibernate development/support has been growing stagnant as of late. :-/ – Nick Albrecht Sep 08 '16 at 19:18

0 Answers0