NHibernate query runs only once, then throws InvalidCastException
Asked Answered
A

2

21

I have a simple query like below:

var employeeTeam = Session.Query<EmployeeTeam>()
                       .Where(x => x.StartEffective <= competency.FinalDate && // competency.FinalDate is a DateTime
                                   employeesIds.Contains(x.EmployeeId)) // employeeIds is a List<long>
                       .OrderByDescending(x => x.StartEffective)
                       .Select(x => new
                       {
                           x.EmployeeId,
                           x.StartEffective,
                           x.Team
                       }).ToList();

It successfully runs once, but when executed in the second time(or third, fourth and so son) it throws an invalid cast exception like:

Fatal Error:System.InvalidCastException: Cannot convert type 'System.Linq.EnumerableQuery`1[<>f__AnonymousType0`3[System.Int64,System.DateTime,Team]]' to 'System.Collections.Generic.IEnumerable`1[<>f__AnonymousType0`3[System.Int64,System.DateTime,Team]]'. in NHibernate.Linq.DefaultQueryProvider.Execute[TResult](Expression expression)

Rest of the stack trace supressed for bravety.

The query is always executed in database before the error. It returns no records, but its is ok. If I rebuild the solution and run again, the query is executed in first time again, and then start throwing the exception each other time I run it. Other queries runs everytime w/o any problems. I have no idea of what causes the error.

Its important to say that this code is running in an CSharpCodeProvider environment, but I don't know if it can make a difference.

UPDATE

It happens even with the most simple form of the query:

var employeeTeam = Session.Query<EmployeeTeam>()
                       .Select(x => new
                       {
                           x.Id
                       }).ToList();

It runs ok for the first time only. But if I change the annon object from { x.Id } to { x.TeamId }, for example, it runs ok in the first time, then the exceptions occurs again.

UPDATE 2

I just realize that if I add the following property to the annon object, the query works everytime:

Rnd = (new Random().Next(1, 999))

So, a cache issue maybe?

UPDATE 3

I updated the NHibernate from 3.3 to 4.0.0.4 and it solves almost all problems except by one query:

var query = session.Query<Holiday>()
                   .Select(x => new {
                         HoliDayCities = x.City.Select(c => c.Id).ToList(),
                         HoliDayStates = x.State.Select(s => s.Id).ToList(),
    Date = new DateTime((int)(x.Year.HasValue ? x.Year : competencia.InitialDate.Year), (int)x.Month, (int)x.Day)
                   }).ToList();

Error message:

GenericADOException: The value "{ HoliDayCities = System.Collections.Generic.List`1[System.Int64], HoliDayStates = System.Collections.Generic.List`1[System.Int64], Date = 01/02/2015 00:00:00 }" is not "<>f__AnonymousType1`3[System.Collections.Generic.List`1[System.Int64],System.Collections.Generic.List`1[System.Int64],System.DateTime]" and cannot be used on this collection. Parameter name: value

If I add the Rnd() function on Select scope as I mentioned before, it works fine. The problem occurres only with anonymous object.

Auschwitz answered 29/1, 2016 at 16:5 Comment(35)
What happens if you move OrderByDescending(x => x.StartEffective) to right after the projection?Gilreath
@GertArnold It doesn't changes the result. Even removing OrderByDescending the problem remains.Auschwitz
@GertArnold I have added new info.Auschwitz
What version of nhibernate are you using?Beaston
I'm able to run two consecutive queries with the same version of nhibernate that are nearly identical to the above. The only other things I could think to check are configuration around nhibernate itself and the mapping of this particular object. Another thing you may want to look at is the lifetime of the ISession object. It's supposed to be short lived and by the code above it's not clear on what the lifetime is exactly.Beaston
@ColeW sure. The entire project have thousands of queries, and they all run ok. The point here is that this query is on a dynamic compiled code, as said on last paragraph before the first update. There are other queries in it as well, but only this specific one has this issue. Did you checked my last update ? I have use a similar technique as in javascript to prevent cache on ajax requests, that is to add a timestamp param in the querystring. I have added a property with a random number, then I get to the query to work.Auschwitz
Does it happen if you remove the .ToList() call altogether?Barbette
@MattClark removing the .ToList() avoids the exception. I think the problem is in the ToList as well, because the query is executed in database actually, to problem comes after.Auschwitz
@Auschwitz do you NEED it to be a list? In particular is there any reason you need to use either: List.Count, or the operator []? If not, simply using an IEnumerable is a better practice anyways as it avoids FORCING the lazy method to run immediately and avoids an additional object allocation.Barbette
@MattClark it is important to be possible to get result as a list. What is strange is that the castings works pretty nice, there is no reason for the exception. Why it doesn't happens in first time ? There is any kind of caching by nhibernate ? The code is compiled and executed at run time, so the generated assembly is suposed to be garbage collected, it can't be an asp.net problem, I supose. It is really weird.Auschwitz
Can you do a "new List(employeeTeam)"? I'm still not clear on why having it as a list is important. It is a potentially expensive call.Barbette
@MattClark but then I will have an empty list, right? Sorry I didn't get your point. To make things clear, the feature I'm working is a code editor inside the system, where user can create a piece of code to be run in certain points/events at the system to manipulate a procces or data, it is like an SQL trigger. So it is important to be possbile to run any kind of nhibernate queries the way user wants. The performance is up to him. I can't make a limited tool to it. I will try to create a fiddle for that tomorrow, to help your guys to help me.Auschwitz
Let us continue this discussion in chat.Barbette
Perhaps try a StatelessSession instead of a Session? That would eliminate the possibility of any sort of first/second level caching issue.Hypercritical
@Hypercritical I have tried, but it doesn't works. I throws another exception. I don't have it now to show you. But I can get it again, you like.Auschwitz
The exception doesn't make sense since "EnumerableQuery<T>" could be casted to a "IEnumerable<T>". Have you checked whether the returning employeeTeam was what you wanted? Maybe you could take a look at [link]#17650281. Or you could set a breakpoint at DefaultQueryProvider::Execute<TResult> inside NHibernate and check what's the difference between the two calls.Gowen
@ShaneLu sure it doesn't makes sense, I agree. But how it runs ok in the first time? employeeTeam never gets a value, the exception is on .ToList() actually. I don't know exactly how can I debug a NH method, but I will try to figure out, it will be helpful indeed. Have you any resource of how I do that ?Auschwitz
I updated the NHibernate from 3.3 to 4.0.0.4 and it solves the error that I've posted here, but I got the same problem on another query. I updated the question.Auschwitz
@Auschwitz To debug the NHibernate, I think (I didn't do it before) you could download its source code from github and compile it yourself. Then reference the result assemblies instead of the original assemblies. Alternatively, could you reproduce this problem using a simpler environment? If you could, I could try reproducing the problem according to your reproduce steps and debugging it.Gowen
@ShaneLu ok, I will try to debug it, it can be interesting. About the environment, I will create if the debug doesn't helps. It is a little complex but I will try to get some time for it.Auschwitz
@ShaneLu I can't debug nh's source. I have downloaded it, compiled, pointed the resultant dlls but I don't how to reference it in my project. I always throws me an error on the build of missing type (IEntityPersister) and that the reference version is wrong and stuff... I just don't now how to solve this shitty issue. I'm sry. Thank you .net to suck at references (y)Auschwitz
@ShaneLu check out the last update.Auschwitz
@MattClark check out the last update.Auschwitz
@ColeW check out the last update.Auschwitz
@Auschwitz Sorry for the late reply. I downloaded the code and I can't reproduce the exception. the only change I made was to use windows authentication to login the local database.Gowen
@ShaneLu np, thank you for help!! You're saying that your queries runs w/o any exception? Same query multiple times ? If yes, its wierd since it throws an exception for me as well for my team mate which is helping me on this. We tested in our desktops and the exception occur on both. I don't know what to say anymore.Auschwitz
Does it happen when not using an anonymous type as return value?Rhoades
@Rhoades nope. Only with annon types.Auschwitz
@Auschwitz I've also cloned your repo and run it without being able to reproduce the exception. (I also changed it to use windows authentication, since that's what my local SQL instance is configured to use). The first time I ran it, I uncommented line 37 and the schema was created. I then changed the "source" string so that two identical queries are run one after the other (see pastebin.com/bnJ5KvUS) and they both succeeded (both returning zero results). Is that the sort of thing you meant by "same query multiple times"?Yahwistic
@DanRoberts I don't think so. My problem (if I remember well, was a long time ago) is that after first run, all other attempts resulted in an exception. The problem was related to queries with annon objects in the Select method. If the query brings the same result, it crashes, but if you add a random number hard-coded in a property of that annon object, it works. Got it?Auschwitz
@Auschwitz That's how I read your question initially but when I tried running the project repeatedly, I couldn't get it to fail and so I thought maybe the same query had to be run twice in the way that I showed in that pastebin example. Did you get this fixed in the end?Yahwistic
@DanRoberts I see. No, I didn't fixed it. I ended using .ToList() before .Select() like in the line you commented (lines 23, 42) or I use a class in select like .Select(x => new SomeClass() { a = x.a ... }). Unfortunately, I moved up from this issue to other stuff and I won't getting back to it soon..Auschwitz
Hi please put your repository back up again, or post your entities and mappings. Each of your updates hint towards different issues going on... Actually I think Update 3 is a completely different problem. My guess is that your first issue is a problem with a combination of caching and proxying of the Team property, but please post your entities and mappings for me to be sure.Hans
NHibernate does generate a query plan for queries and caches it, re-using it when the same query is executed later. In the case of Linq queries, they are first partially evaluated for reducing every in-memory evaluatable sub-expression to constants which are then extracted as parameters. The query plan is then fetched (or computed if not found) for the resulting partially evaluated and "parameterized" expression. Your problem may be linked to this query plan caching. If you can still reproduce it, maybe should you post a bug with the test case.Topgallant
@Frédéric Thank you for your comment. I just realized that the github link is broken, it is on a friend's account, unfortunately.Auschwitz
B
1

It looks like it is an issue with manipulating anonymous types and NHibernate. I would highly suggest returning a simple resultset, materializing the resultset with ToList(), and then doing projections on that resultset.

var employeeTeam = Session.Query<EmployeeTeam>()
                          .Select(x => x)
                          .Where(x => x.Id != 0)
                          .ToList();

var projectedTeam = employeeTeam.Select(x => new {x.Id});
Bouley answered 5/2, 2016 at 19:43 Comment(1)
Thank you for the answer. It works fine, indeed. But I still have to problem, I will try to solve it. Its important to figure out because it may happen somewhere else in near future in my app.Auschwitz
A
-2

I don't think the Nhib LINQ provider supports that projection being part of the deferred execution. I think you need to put your projection after ToList()

var employeeTeam = Session.Query<EmployeeTeam>()
                   .Where(x => x.StartEffective <= competency.FinalDate &&    // competency.FinalDate is a DateTime
                               employeesIds.Contains(x.EmployeeId)) //  employeeIds is a List<long>
                   .OrderByDescending(x => x.StartEffective)
                   .ToList()
                   .Select(x => new
                   {
                       x.EmployeeId,
                       x.StartEffective,
                       x.Team
                   });
Alcoholism answered 30/1, 2016 at 18:58 Comment(4)
Thank you for your answer. It doens't changes the result. I had even removed the OrderByDescending method, but the problem remains.Auschwitz
I have added new infoAuschwitz
you can project into an object anonymous or named. Using the projection ensures the query that is generated only has the values you need to retrieve.Bencion
NHibernate LINQ definitely supports projection, the generated SQL will contain a SELECT clause based on the projected properties.Sudarium

© 2022 - 2024 — McMap. All rights reserved.