Skip to content

Commit 9faa35c

Browse files
committed
NH-2218 - Add support for entity names to Query<T>()
1 parent 58ba22e commit 9faa35c

File tree

6 files changed

+160
-25
lines changed

6 files changed

+160
-25
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using System.Linq;
2+
using NHibernate.Linq;
3+
using NHibernate.Test.NHSpecificTest.NH0000;
4+
using NUnit.Framework;
5+
6+
namespace NHibernate.Test.NHSpecificTest.NH2218
7+
{
8+
public class Fixture : BugTestCase
9+
{
10+
protected override void OnSetUp()
11+
{
12+
using (var session = OpenSession())
13+
using (var transaction = session.BeginTransaction())
14+
{
15+
for (int i = 0; i < 4; i++)
16+
{
17+
session.Save("Entity1", new Entity {Name = "Mapping1 -" + i});
18+
}
19+
for (int i = 0; i < 3; i++)
20+
{
21+
session.Save("Entity2", new Entity {Name = "Mapping2 -" + i});
22+
}
23+
24+
session.Flush();
25+
transaction.Commit();
26+
}
27+
}
28+
29+
protected override void OnTearDown()
30+
{
31+
using (var session = OpenSession())
32+
using (var transaction = session.BeginTransaction())
33+
{
34+
session.Delete("from System.Object");
35+
36+
session.Flush();
37+
transaction.Commit();
38+
}
39+
}
40+
41+
[Test]
42+
public void SelectEntitiesByEntityName()
43+
{
44+
using (var session = OpenSession())
45+
using (session.BeginTransaction())
46+
{
47+
// verify the instance count for both mappings
48+
Assert.That(session.Query<Entity>("Entity1").Count(), Is.EqualTo(4));
49+
Assert.That(session.Query<Entity>("Entity2").Count(), Is.EqualTo(3));
50+
51+
// verify that all instances are loaded from the right table
52+
Assert.That(session.Query<Entity>("Entity1").Count(x => x.Name.Contains("Mapping1")), Is.EqualTo(4));
53+
Assert.That(session.Query<Entity>("Entity2").Count(x => x.Name.Contains("Mapping2")), Is.EqualTo(3));
54+
55+
// a query for Entity returns instances of both mappings
56+
// Remark: session.Query<Entity>().Count() doesn't work because only the count of the first mapping (4) is returned
57+
Assert.That(session.Query<Entity>().ToList().Count, Is.EqualTo(7));
58+
}
59+
}
60+
61+
[Test]
62+
public void SelectEntitiesByEntityNameFromStatelessSession()
63+
{
64+
using (var session = sessions.OpenStatelessSession())
65+
using (session.BeginTransaction())
66+
{
67+
// verify the instance count for both mappings
68+
Assert.That(session.Query<Entity>("Entity1").Count(), Is.EqualTo(4));
69+
Assert.That(session.Query<Entity>("Entity2").Count(), Is.EqualTo(3));
70+
71+
// verify that all instances are loaded from the right table
72+
Assert.That(session.Query<Entity>("Entity1").Count(x => x.Name.Contains("Mapping1")), Is.EqualTo(4));
73+
Assert.That(session.Query<Entity>("Entity2").Count(x => x.Name.Contains("Mapping2")), Is.EqualTo(3));
74+
75+
// a query for Entity returns instances of both mappings
76+
// Remark: session.Query<Entity>().Count() doesn't work because only the count of the first mapping (4) is returned
77+
Assert.That(session.Query<Entity>().ToList().Count, Is.EqualTo(7));
78+
}
79+
}
80+
}
81+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernate.Test" namespace="NHibernate.Test.NHSpecificTest.NH0000">
3+
4+
<class name="Entity" entity-name="Entity1">
5+
<id name="Id" generator="guid.comb" />
6+
<property name="Name" />
7+
</class>
8+
<class name="Entity" entity-name="Entity2">
9+
<id name="Id" generator="guid.comb" />
10+
<property name="Name" />
11+
</class>
12+
13+
</hibernate-mapping>

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,7 @@
723723
<Compile Include="NHSpecificTest\BagWithLazyExtraAndFilter\Fixture.cs" />
724724
<Compile Include="Linq\ByMethod\DistinctTests.cs" />
725725
<Compile Include="Component\Basic\ComponentWithUniqueConstraintTests.cs" />
726+
<Compile Include="NHSpecificTest\NH2218\Fixture.cs" />
726727
<Compile Include="NHSpecificTest\NH2931\Fixture.cs" />
727728
<Compile Include="NHSpecificTest\NH2931\Mappings.cs" />
728729
<Compile Include="NHSpecificTest\NH2931\Models.cs" />
@@ -3164,6 +3165,7 @@
31643165
<EmbeddedResource Include="NHSpecificTest\NH1291AnonExample\Mappings.hbm.xml" />
31653166
</ItemGroup>
31663167
<ItemGroup>
3168+
<EmbeddedResource Include="NHSpecificTest\NH2218\Mappings.hbm.xml" />
31673169
<EmbeddedResource Include="NHSpecificTest\NH3046\Mappings.hbm.xml" />
31683170
<EmbeddedResource Include="NHSpecificTest\NH3518\Mappings.hbm.xml" />
31693171
<EmbeddedResource Include="NHSpecificTest\NH3609\Mappings.hbm.xml" />

src/NHibernate/Linq/LinqExtensionMethods.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,24 @@ public static IQueryable<T> Query<T>(this ISession session)
1616
return new NhQueryable<T>(session.GetSessionImplementation());
1717
}
1818

19+
public static IQueryable<T> Query<T>(this ISession session, string entityName)
20+
{
21+
return new NhQueryable<T>(session.GetSessionImplementation(), entityName);
22+
}
23+
1924
public static IQueryable<T> Query<T>(this IStatelessSession session)
2025
{
2126
return new NhQueryable<T>(session.GetSessionImplementation());
2227
}
2328

29+
public static IQueryable<T> Query<T>(this IStatelessSession session, string entityName)
30+
{
31+
return new NhQueryable<T>(session.GetSessionImplementation(), entityName);
32+
}
33+
2434
public static IQueryable<T> Cacheable<T>(this IQueryable<T> query)
2535
{
26-
var method = ReflectionHelper.GetMethodDefinition(() => Cacheable<object>(null)).MakeGenericMethod(typeof (T));
36+
var method = ReflectionHelper.GetMethodDefinition(() => Cacheable<object>(null)).MakeGenericMethod(typeof(T));
2737

2838
var callExpression = Expression.Call(method, query.Expression);
2939

@@ -32,7 +42,7 @@ public static IQueryable<T> Cacheable<T>(this IQueryable<T> query)
3242

3343
public static IQueryable<T> CacheMode<T>(this IQueryable<T> query, CacheMode cacheMode)
3444
{
35-
var method = ReflectionHelper.GetMethodDefinition(() => CacheMode<object>(null, NHibernate.CacheMode.Normal)).MakeGenericMethod(typeof (T));
45+
var method = ReflectionHelper.GetMethodDefinition(() => CacheMode<object>(null, NHibernate.CacheMode.Normal)).MakeGenericMethod(typeof(T));
3646

3747
var callExpression = Expression.Call(method, query.Expression, Expression.Constant(cacheMode));
3848

@@ -41,7 +51,7 @@ public static IQueryable<T> CacheMode<T>(this IQueryable<T> query, CacheMode cac
4151

4252
public static IQueryable<T> CacheRegion<T>(this IQueryable<T> query, string region)
4353
{
44-
var method = ReflectionHelper.GetMethodDefinition(() => CacheRegion<object>(null, null)).MakeGenericMethod(typeof (T));
54+
var method = ReflectionHelper.GetMethodDefinition(() => CacheRegion<object>(null, null)).MakeGenericMethod(typeof(T));
4555

4656
var callExpression = Expression.Call(method, query.Expression, Expression.Constant(region));
4757

src/NHibernate/Linq/NhQueryable.cs

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,51 @@
66

77
namespace NHibernate.Linq
88
{
9-
/// <summary>
10-
/// Provides the main entry point to a LINQ query.
11-
/// </summary>
12-
public class NhQueryable<T> : QueryableBase<T>
13-
{
14-
// This constructor is called by our users, create a new IQueryExecutor.
15-
public NhQueryable(ISessionImplementor session)
16-
: base(QueryProviderFactory.CreateQueryProvider(session))
17-
{
18-
}
19-
20-
// This constructor is called indirectly by LINQ's query methods, just pass to base.
21-
public NhQueryable(IQueryProvider provider, Expression expression)
22-
: base(provider, expression)
23-
{
24-
}
25-
}
26-
}
9+
/// <summary>
10+
/// Interface to access the entity name of a NhQueryable instance.
11+
/// </summary>
12+
interface IEntityNameProvider
13+
{
14+
string EntityName { get; }
15+
}
16+
17+
/// <summary>
18+
/// Provides the main entry point to a LINQ query.
19+
/// </summary>
20+
public class NhQueryable<T> : QueryableBase<T>, IEntityNameProvider
21+
{
22+
// This constructor is called by our users, create a new IQueryExecutor.
23+
public NhQueryable(ISessionImplementor session)
24+
: this(session, typeof(T).FullName)
25+
{
26+
}
27+
28+
// This constructor is called by our users, create a new IQueryExecutor.
29+
public NhQueryable(ISessionImplementor session, string entityName)
30+
: base(QueryProviderFactory.CreateQueryProvider(session))
31+
{
32+
EntityName = entityName;
33+
}
34+
35+
36+
// This constructor is called indirectly by LINQ's query methods, just pass to base.
37+
public NhQueryable(IQueryProvider provider, Expression expression)
38+
: this(provider, expression, typeof(T).FullName)
39+
{
40+
}
41+
42+
// This constructor is called indirectly by LINQ's query methods, just pass to base.
43+
public NhQueryable(IQueryProvider provider, Expression expression, string entityName)
44+
: base(provider, expression)
45+
{
46+
EntityName = entityName;
47+
}
48+
49+
public string EntityName { get; private set; }
50+
51+
public override string ToString()
52+
{
53+
return "NHibernate.Linq.NhQueryable`1[" + EntityName + "]";
54+
}
55+
}
56+
}

src/NHibernate/Linq/Visitors/HqlGeneratorExpressionTreeVisitor.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -387,11 +387,10 @@ protected HqlTreeNode VisitConstantExpression(ConstantExpression expression)
387387
{
388388
if (expression.Value != null)
389389
{
390-
System.Type t = expression.Value.GetType();
391-
392-
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof (NhQueryable<>))
390+
IEntityNameProvider entityName = expression.Value as IEntityNameProvider;
391+
if (entityName != null)
393392
{
394-
return _hqlTreeBuilder.Ident(t.GetGenericArguments()[0].FullName);
393+
return _hqlTreeBuilder.Ident(entityName.EntityName);
395394
}
396395
}
397396

0 commit comments

Comments
 (0)