Skip to content

NH-3961 - Linq auto-parameterization failures #570

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions doc/reference/modules/query_linq.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
<para>
The Linq provider works as an extension of the <literal>ISession</literal>. It is defined in the
<literal>NHibernate.Linq</literal> namespace, so this namespace has to be imported for using the
Linq provider. Of course, the LINQ namespace is still needed too.
Linq provider. Of course, the Linq namespace is still needed too.
</para>
<programlisting><![CDATA[using System.Linq;
using NHibernate.Linq;]]></programlisting>
<para>
Note: NHibernate has another querying API which uses lambda, <link linkend="queryqueryover">QueryOver</link>.
It should not be confused with a LINQ provider.
It should not be confused with a Linq provider.
</para>

<sect1 id="querylinq-querystructure">
Expand Down Expand Up @@ -545,9 +545,10 @@ IList<Cat> cats =
}
}]]></programlisting>
<para>
It is required that at least one of the parameters of the method call has its value originating
from an entity. Otherwise, the Linq provider will try to evaluate the method call with .Net
runtime.
The method call will always be translated to SQL if at least one of the parameters of the
method call has its value originating from an entity. Otherwise, the Linq provider will try to
evaluate the method call with .Net runtime instead. Since NHibernate 5.0, if this runtime
evaluation fails (throws an exception), then the method call will be translated to SQL too.
</para>
</sect2>

Expand All @@ -565,7 +566,7 @@ IList<Cat> cats =
<para>&nbsp;</para>
<para>
As an example, here is how to add support for an <literal>AsNullable</literal> method which
would allow to call aggregates which lay yield <literal>null</literal> without to explicitly
would allow to call aggregates which may yield <literal>null</literal> without to explicitly
cast to the nullable type of the aggregate.
</para>
<programlisting><![CDATA[public static class NullableExtensions
Expand All @@ -579,7 +580,7 @@ IList<Cat> cats =
}
}]]></programlisting>
<para>
Adding support in Linq to NHibernate for this custom method requires a generator. For this
Adding support in Linq to NHibernate for a custom method requires a generator. For this
<literal>AsNullable</literal> method, we need a method generator, declaring statically its
supported method.
</para>
Expand Down Expand Up @@ -612,8 +613,8 @@ IList<Cat> cats =
</para>
<para>
For adding <literal>AsNullableGenerator</literal> in Linq to NHibernate provider, a new
generators registry should be used. Derive from the default one and merge it. (Static
declaration of method support case.)
generators registry should be used. Derive from the default one and merge it. (Here we
have a static declaration of method support case.)
</para>
<programlisting><![CDATA[public class ExtendedLinqToHqlGeneratorsRegistry :
DefaultLinqToHqlGeneratorsRegistry
Expand Down
10 changes: 10 additions & 0 deletions src/NHibernate.Test/NHSpecificTest/NH3386/Entity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;

namespace NHibernate.Test.NHSpecificTest.NH3386
{
class Entity
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
}
}
80 changes: 80 additions & 0 deletions src/NHibernate.Test/NHSpecificTest/NH3386/Fixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System.Linq;
using NHibernate.Linq;
using NUnit.Framework;
using System;
using NHibernate.SqlCommand;

namespace NHibernate.Test.NHSpecificTest.NH3386
{
[TestFixture]
public class Fixture : BugTestCase
{
protected override bool AppliesTo(Dialect.Dialect dialect)
{
return dialect is Dialect.MsSql2000Dialect;
}

protected override void OnSetUp()
{
using (ISession session = OpenSession())
using (ITransaction transaction = session.BeginTransaction())
{
var e1 = new Entity {Name = "Bob"};
session.Save(e1);

var e2 = new Entity {Name = "Sally"};
session.Save(e2);

session.Flush();
transaction.Commit();
}
}

protected override void OnTearDown()
{
using (ISession session = OpenSession())
using (ITransaction transaction = session.BeginTransaction())
{
session.Delete("from System.Object");

session.Flush();
transaction.Commit();
}
}

[Test]
public void ShouldSupportNonRuntimeExtensionWithoutEntityReference()
{
var sqlInterceptor = new SqlInterceptor();
using (ISession session = OpenSession(sqlInterceptor))
using (session.BeginTransaction())
{
var result = session.Query<Entity>()
.OrderBy(e => SqlServerFunction.NewID());

Assert.DoesNotThrow(() => { result.ToList(); });
Assert.That(sqlInterceptor.Sql.ToString(), Does.Contain(nameof(SqlServerFunction.NewID)).IgnoreCase);
}
}
}

public static class SqlServerFunction
{
[LinqExtensionMethod]
public static Guid NewID()
{
throw new InvalidOperationException("To be translated to SQL only");
}
}

public class SqlInterceptor: EmptyInterceptor
{
public SqlString Sql { get; private set; }

public override SqlString OnPrepareStatement(SqlString sql)
{
Sql = sql;
return base.OnPrepareStatement(sql);
}
}
}
9 changes: 9 additions & 0 deletions src/NHibernate.Test/NHSpecificTest/NH3386/Mappings.hbm.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernate.Test" namespace="NHibernate.Test.NHSpecificTest.NH3386">

<class name="Entity">
<id name="Id" generator="guid.comb" />
<property name="Name" />
</class>

</hibernate-mapping>
Loading