Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Fixed NH-3050 - Unable to cast object of type 'NHibernate.Impl.ExpandedQueryExpression' to type 'NHibernate.Linq.NhLinqExpression' #134

Closed
wants to merge 3 commits into from

2 participants

EamonHetherton Alexander Zaytsev
EamonHetherton

No description provided.

EamonHetherton EamonHetherton Added fixture for NH-3050
Signed-off-by: Eamon Hetherton <eamonhetherton@gmail.com>
a26e70b
EamonHetherton EamonHetherton cleanup...
Signed-off-by: Eamon Hetherton <eamonhetherton@gmail.com>
6b37ad4
EamonHetherton EamonHetherton fix for NH-3050.
Added constructor overload to HQLQueryPlanKey that allows it
to discriminate between different IQueryExpression type.

In particular NhLinqExpression vs ExpandedQueryExpression.

Signed-off-by: Eamon Hetherton <eamonhetherton@gmail.com>
853d906
Alexander Zaytsev
Owner

Applied with squash and minor changes 2a4041e

Alexander Zaytsev hazzik closed this
EamonHetherton EamonHetherton deleted the branch
EamonHetherton EamonHetherton restored the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 3 unique commits by 1 author.

Aug 01, 2012
EamonHetherton EamonHetherton Added fixture for NH-3050
Signed-off-by: Eamon Hetherton <eamonhetherton@gmail.com>
a26e70b
EamonHetherton EamonHetherton cleanup...
Signed-off-by: Eamon Hetherton <eamonhetherton@gmail.com>
6b37ad4
Aug 02, 2012
EamonHetherton EamonHetherton fix for NH-3050.
Added constructor overload to HQLQueryPlanKey that allows it
to discriminate between different IQueryExpression type.

In particular NhLinqExpression vs ExpandedQueryExpression.

Signed-off-by: Eamon Hetherton <eamonhetherton@gmail.com>
853d906
This page is out of date. Refresh to see the latest.
10 src/NHibernate.Test/NHSpecificTest/NH3050/Entity.cs
... ... @@ -0,0 +1,10 @@
  1 +using System;
  2 +
  3 +namespace NHibernate.Test.NHSpecificTest.NH3050
  4 +{
  5 + class Entity
  6 + {
  7 + public virtual Guid Id { get; set; }
  8 + public virtual string Name { get; set; }
  9 + }
  10 +}
128 src/NHibernate.Test/NHSpecificTest/NH3050/FixtureByCode.cs
... ... @@ -0,0 +1,128 @@
  1 +using System.Linq;
  2 +using NHibernate.Cfg.MappingSchema;
  3 +using NHibernate.Linq;
  4 +using NHibernate.Mapping.ByCode;
  5 +using NUnit.Framework;
  6 +using System.Collections.Generic;
  7 +using System;
  8 +
  9 +namespace NHibernate.Test.NHSpecificTest.NH3050
  10 +{
  11 + /// <summary>
  12 + /// Fixture using 'by code' mappings
  13 + /// </summary>
  14 + /// <remarks>
  15 + /// This fixture is identical to <see cref="Fixture" /> except the <see cref="Entity" /> mapping is performed
  16 + /// by code in the GetMappings method, and does not require the <c>Mappings.hbm.xml</c> file. Use this approach
  17 + /// if you prefer.
  18 + /// </remarks>
  19 + [TestFixture]
  20 + public class FixtureByCode : TestCaseMappingByCode
  21 + {
  22 + protected override HbmMapping GetMappings()
  23 + {
  24 + var mapper = new ModelMapper();
  25 + mapper.Class<Entity>(rc =>
  26 + {
  27 + rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
  28 + rc.Property(x => x.Name);
  29 + });
  30 +
  31 + return mapper.CompileMappingForAllExplicitlyAddedEntities();
  32 + }
  33 +
  34 + protected override void OnSetUp()
  35 + {
  36 + using (ISession session = OpenSession())
  37 + using (ITransaction transaction = session.BeginTransaction())
  38 + {
  39 + var e1 = new Entity { Name = "Bob" };
  40 + session.Save(e1);
  41 +
  42 + var e2 = new Entity { Name = "Sally" };
  43 + session.Save(e2);
  44 +
  45 + session.Flush();
  46 + transaction.Commit();
  47 + }
  48 + }
  49 +
  50 + protected override void OnTearDown()
  51 + {
  52 + using (ISession session = OpenSession())
  53 + using (ITransaction transaction = session.BeginTransaction())
  54 + {
  55 + session.Delete("from System.Object");
  56 +
  57 + session.Flush();
  58 + transaction.Commit();
  59 + }
  60 + }
  61 +
  62 + [Test]
  63 + public void NH3050_Reproduction()
  64 + {
  65 + //firstly to make things simpler, we set the query plan cache size to 1
  66 + Assert.IsTrue(TrySetQueryPlanCacheSize(Sfi, 1));
  67 +
  68 + using (ISession session = OpenSession())
  69 + using (session.BeginTransaction())
  70 + {
  71 + var names = new List<string>() { "Bob" };
  72 + var query = from e in session.Query<Entity>()
  73 + where names.Contains(e.Name)
  74 + select e;
  75 +
  76 + //create a future, which will prepare a linq query plan and add it to the cache (NhLinqExpression)
  77 + var future = query.ToFuture();
  78 +
  79 + //we need enough unique queries (different to our main query here) to fill the plan cache so that our previous plan is evicted
  80 + //in this case we only need one as we have limited the cache size to 1
  81 + (from e in session.Query<Entity>()
  82 + where e.Name == ""
  83 + select e).ToList();
  84 +
  85 + //garbage collection runs so that the query plan for our future which is a weak reference now in the plan cache is collected.
  86 + GC.Collect();
  87 +
  88 + //execute future which creates an ExpandedQueryExpression and adds it to the plan cache (generates the same cache plan key as the NhLinqExpression)
  89 + future.ToList();
  90 +
  91 + //execute original query again which will look for a NhLinqExpression in the plan cache but because it has already been evicted
  92 + //and because the ExpandedQueryExpression generates the same cache key, the ExpandedQueryExpression is returned and
  93 + //an exception is thrown as it tries to cast to a NhLinqExpression.
  94 + query.ToList();
  95 + }
  96 + }
  97 +
  98 + /// <summary>
  99 + /// Uses reflection to create a new SoftLimitMRUCache with a specified size and sets session factory query plan chache to it.
  100 + /// This is done like this as NHibernate does not currently provide any way to specify the query plan cache size through configuration.
  101 + /// </summary>
  102 + /// <param name="factory"></param>
  103 + /// <param name="size"></param>
  104 + /// <returns></returns>
  105 + private static bool TrySetQueryPlanCacheSize(NHibernate.ISessionFactory factory, int size)
  106 + {
  107 + var factoryImpl = factory as NHibernate.Impl.SessionFactoryImpl;
  108 + if (factoryImpl != null)
  109 + {
  110 + var queryPlanCacheFieldInfo = typeof(NHibernate.Impl.SessionFactoryImpl).GetField("queryPlanCache", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
  111 + if (queryPlanCacheFieldInfo != null)
  112 + {
  113 + var queryPlanCache = (NHibernate.Engine.Query.QueryPlanCache)queryPlanCacheFieldInfo.GetValue(factoryImpl);
  114 +
  115 + var planCacheFieldInfo = typeof(NHibernate.Engine.Query.QueryPlanCache).GetField("planCache", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
  116 + if (planCacheFieldInfo != null)
  117 + {
  118 + var softLimitMRUCache = new NHibernate.Util.SoftLimitMRUCache(size);
  119 +
  120 + planCacheFieldInfo.SetValue(queryPlanCache, softLimitMRUCache);
  121 + return true;
  122 + }
  123 + }
  124 + }
  125 + return false;
  126 + }
  127 + }
  128 +}
2  src/NHibernate.Test/NHibernate.Test.csproj
@@ -665,6 +665,8 @@
665 665 <Compile Include="NHSpecificTest\BagWithLazyExtraAndFilter\Domain.cs" />
666 666 <Compile Include="NHSpecificTest\BagWithLazyExtraAndFilter\Fixture.cs" />
667 667 <Compile Include="Component\Basic\ComponentWithUniqueConstraintTests.cs" />
  668 + <Compile Include="NHSpecificTest\NH3050\Entity.cs" />
  669 + <Compile Include="NHSpecificTest\NH3050\FixtureByCode.cs" />
668 670 <Compile Include="NHSpecificTest\NH3093\Domain.cs" />
669 671 <Compile Include="NHSpecificTest\NH3093\Fixture.cs" />
670 672 <Compile Include="NHSpecificTest\NH2806\Domain.cs" />
32 src/NHibernate/Engine/Query/QueryPlanCache.cs
@@ -74,7 +74,7 @@ public IQueryExpressionPlan GetHQLQueryPlan(IQueryExpression queryExpression, bo
74 74 {
75 75 string expressionStr = queryExpression.Key;
76 76
77   - var key = new HQLQueryPlanKey(expressionStr, shallow, enabledFilters);
  77 + var key = new HQLQueryPlanKey(queryExpression, shallow, enabledFilters);
78 78 var plan = (IQueryExpressionPlan)planCache[key];
79 79
80 80 if (plan == null)
@@ -179,9 +179,21 @@ private class HQLQueryPlanKey
179 179 private readonly bool shallow;
180 180 private readonly HashSet<string> filterNames;
181 181 private readonly int hashCode;
  182 + private readonly System.Type queryTypeDiscriminator;
182 183
183 184 public HQLQueryPlanKey(string query, bool shallow, IDictionary<string, IFilter> enabledFilters)
  185 + : this(typeof(object), query, shallow, enabledFilters)
184 186 {
  187 + }
  188 +
  189 + public HQLQueryPlanKey(IQueryExpression queryExpression, bool shallow, IDictionary<string, IFilter> enabledFilters)
  190 + : this(queryExpression.GetType(), queryExpression.Key, shallow, enabledFilters)
  191 + {
  192 + }
  193 +
  194 + protected HQLQueryPlanKey(System.Type queryTypeDiscriminator, string query, bool shallow, IDictionary<string, IFilter> enabledFilters)
  195 + {
  196 + this.queryTypeDiscriminator = queryTypeDiscriminator;
185 197 this.query = query;
186 198 this.shallow = shallow;
187 199
@@ -194,12 +206,17 @@ public HQLQueryPlanKey(string query, bool shallow, IDictionary<string, IFilter>
194 206 filterNames = new HashSet<string>(enabledFilters.Keys);
195 207 }
196 208
197   - int hash = query.GetHashCode();
198   - hash = 29 * hash + (shallow ? 1 : 0);
199   - hash = 29 * hash + CollectionHelper.GetHashCode(filterNames);
200   - hashCode = hash;
  209 + unchecked
  210 + {
  211 + int hash = query.GetHashCode();
  212 + hash = 29 * hash + (shallow ? 1 : 0);
  213 + hash = 29 * hash + CollectionHelper.GetHashCode(filterNames);
  214 + hash = 29 * hash + queryTypeDiscriminator.GetHashCode();
  215 + hashCode = hash;
  216 + }
201 217 }
202 218
  219 +
203 220 public override bool Equals(object obj)
204 221 {
205 222 return this == obj || Equals(obj as HQLQueryPlanKey);
@@ -227,6 +244,11 @@ public bool Equals(HQLQueryPlanKey that)
227 244 return false;
228 245 }
229 246
  247 + if (!queryTypeDiscriminator.Equals(that.queryTypeDiscriminator))
  248 + {
  249 + return false;
  250 + }
  251 +
230 252 return true;
231 253 }
232 254

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.