2

I am using LINQ-to-Entities (EF 6.1.3) to perform the following query:

var users = msgList.Select(m => m.From)
                    .Union(msgList.Select(m => m.To))
                    .Distinct()
                    .Where(u => u.ID != userId) //userId is an assigned local var.
                    .ToList();

msgList is a List (already fetched, not a queryable and lazy loading is off) of Messages which consists of some fields like From and To which are guaranteed to be non-null. Both From and To were Included in the original query, so they are guaranteed to be non-null.

My User object is also guaranteed to be non-null, so there's nothing that can actually be null.

However, this line is sometimes throwing a null pointer exception, and sometimes executing perfectly with the exact same user, exact same database, exactly same data (nothing altered). Load is not an issue as it's a code not yet in production and I'm the only one testing it.

The exception seems to be thrown at the Where call:

at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext() 
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)

How can this happen?

UPDATE: This is of course not a duplicate of What is a NullReferenceException, and how do I fix it?. Any sane developer with even a little knowledge in .NET/C#/OOP knows what that error is and that this question has nothing to do with it, even though it involves that exception as a part of it.

UPDATE 2: I've switched it to assigning to a list each line, as suggested below:

var msgListSelection = msgList.Select(m => m.From).ToList();
var union = msgListSelection.Union(msgList.Select(m => m.To)).ToList();
var distinct = union.Distinct().ToList();
var where = distinct.Where(u => u.ID != userId).ToList();
var users = where;

The exception occurs at the where line:

var where = distinct.Where(u => u.ID != User.ID).ToList();

If distinct returned null, it would have been thrown on ToList call of var distinct = union.Distinct().ToList(); on the line above.

Am I missing something?

UPDATE 2: My User class is a POCO C# type mapped to an Entity type in my database which has an ID property of long, and my Message class is again a POCO type mapped in Entity Framework, with navigation properties From and To to some User instances guaranteed to be non-null. They are annotated as Required and I've also checked them at the database level just to be sure.

UPDATE 3: My EF context lives from the beginning of the request (set at a delegating handler in the beginning of the request) to the end. I don't think the problem is related to the lifespan of the DbContext as there are many controllers with the same mechanism with tens of methods that access the context, and I'm only having such problem with this particular method.

UPDATE 4: I've added a null check on distincts:

var distinct = union.Distinct().ToList();
if(distinct == null)
{
    throw new Exception("distinct was null");
}
var where = distinct.Where(u => u.ID != userId).ToList();

It seems to pass that point with no problem, but throw the null pointer exception at the last line var where = distinct.Where(u => u.ID != userId).ToList(); which sorts out the possibility that distinct may be null.

UPDATE 5: I've wrote an API testing tool and sent about 250 requests to the same endpoint with the same user. The first one failed with this error, and all the rest succeeded successfully. There seems to be a problem with the first request.

Community
  • 1
  • 1
Can Poyrazoğlu
  • 29,145
  • 40
  • 152
  • 327
  • maybe your list is empty after union and select – DarkMakukudo Jun 09 '16 at 07:28
  • Which method throws? Can you post exception call stack? – Sinatr Jun 09 '16 at 07:29
  • Since `From` and `To` properties has guaranteed to be not null, you should either check User.ID property against null value or making sure that union result doesn't produce empty result. Have you encountered `Object reference not set to an instance of an object` error? – Tetsuya Yamamoto Jun 09 '16 at 07:31
  • @TetsuyaYamamoto yes, it's *the* `Object reference not set to an instance of an object` error, as I've told in the title (NullReferenceException) – Can Poyrazoğlu Jun 09 '16 at 07:32
  • At which operation does it fail? Try putting `ToList()` after each call, and place them on separate statements. – Bozhidar Stoyneff Jun 09 '16 at 07:33
  • @Sinatr see my updated question. – Can Poyrazoğlu Jun 09 '16 at 07:36
  • Possible duplicate of [What is a NullReferenceException, and how do I fix it?](http://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-and-how-do-i-fix-it) – Nasreddine Jun 09 '16 at 07:43
  • Have you ever run those 2 select queries separately with success? I wanna know that main query `msgList.Select(m => m.From).Where(u => u.ID != User.ID).Distinct()` and subquery `msgList.Select(m => m.To)` provide any results before putting them together in union. – Tetsuya Yamamoto Jun 09 '16 at 07:51
  • @Nasreddine are you joking? as a senior C# developer I *do know* what a NullReferenceException is. I'm trying to figure out why the query is producing a null result somewhere. – Can Poyrazoğlu Jun 09 '16 at 07:56
  • It seems the condition in the Where method causes the exception. Are you absolutely sure your User object is not null? Can you update your question with that piece of code? – Wicher Visser Jun 09 '16 at 08:13
  • If your User property is public (or even if not), it may change after you did the null-check. Better to copy the reference to a local variable and operate on that for your null-check and the LINQ query. – Wicher Visser Jun 09 '16 at 08:37
  • @CanPoyrazoğlu I'd suggest you [debug it with .NET Framework source](https://msdn.microsoft.com/en-us/library/cc667410.aspx) and see what object causes the exception. – Ripple Jun 09 '16 at 12:05
  • You say that `From` and `To` properties are *guaranteed* to be not-null. Just how sure are you of that guarantee? Perhaps a little debugging code to check that is in order? – Jim Mischel Jul 06 '16 at 12:29
  • @JimMischel I think I've already explained them in the updates of my question. No, I can't debug as the code is running on Azure and VS remote debugging never works for me (unusably slow) for some reason. – Can Poyrazoğlu Jul 06 '16 at 12:35
  • @JimMischel also, the problem is occuring seemingly-randomly and rarely, so there's almost no possibility that I can catch it anyway. – Can Poyrazoğlu Jul 06 '16 at 12:37
  • 1
    It wouldn't hurt to put a `.Where(u => u != null)` before (or perhaps after) your `Distinct`, just to ensure that your assumptions are correct. It's been my experience that when faced with something that can't possibly happen, it pays to check *all* of my assumptions. Invariably, one of them is wrong. – Jim Mischel Jul 06 '16 at 13:23
  • @JimMischel I can try for sure, but the error is occuring randomly for the same input. Even if I don't get the error for some time, it would be just some coincidence, too. But I'll give it a try. At least, if I'm still getting the error, that would imply it's not that line. – Can Poyrazoğlu Jul 06 '16 at 13:41
  • "Same input" is another one of those assumptions that I've found at times to be unreliable. – Jim Mischel Jul 06 '16 at 15:00
  • @JimMischel but it really is the same input. it's not an app that is live. I'm not changing anything, just fetching some information from the same user that hasn't been altered. there is no difference between the queries/requests. – Can Poyrazoğlu Jul 07 '16 at 10:07
  • You forget that you are dealing with a multi-threaded application. Race conditions and asynchronize processes can wreck havoc with your assumptions. As @JimMischel is saying, best to verify all your assumptions when faced with unexplainable behavior. Catching anomalies will lead you to the cause. – Wicher Visser Jul 07 '16 at 11:37
  • @JimMischel that seems to have avoided the error BTW, and I've noticed something else, see my updated question. I still don't know why is `u` null at the first place – Can Poyrazoğlu Jul 07 '16 at 12:59
  • @WicherVisser I wrote a simple test program and noticed that it's only the first request that's failing. see my updated question. – Can Poyrazoğlu Jul 07 '16 at 12:59
  • EF does need a few seconds warm-up for the first context instance you craete in your app or IIS instance. Although I have not experienced this to cause invalid data, perhaps this is causing it? Maybe the Include() method tries to load data from related tables into the EF model, whilst this model is not yet initialized by EF? – Wicher Visser Jul 07 '16 at 13:11
  • @WicherVisser that's possible. I'm thinking of a workaround. If it is really at the initial startup of IIS, it's not really *that* important as it will virtually not happen at all or very rarely when it goes live. but I must be sure. – Can Poyrazoğlu Jul 07 '16 at 14:41
  • By default, the IIS worker process recycle themselves every 27 hours (app pool recycling), and initialize at the first call to a service. The first user hitting your recycling website will suffer this hit. – Wicher Visser Jul 07 '16 at 15:08
  • @WicherVisser yes but it's on a free/shared hosting on Azure Web App service, and I'm constantly pushing new updates by publishing so it's constantly restarting (at least for now) – Can Poyrazoğlu Jul 08 '16 at 23:38
  • @Can Poyrazoğlu Did you find what was causing this? The exact thing is happening to me. Only the first request is giving me 0 items. – Vilhelm Jan 13 '18 at 11:01
  • 1
    @Vilhelm unfortunately I don't remember. But I suggest you to break down the calls to line by line (without calling the result of previous query as I did). It might help. – Can Poyrazoğlu Jan 13 '18 at 12:07
  • @Can Poyrazoğlu Ok thanks. – Vilhelm Jan 13 '18 at 12:18

1 Answers1

2

You may be experiencing what is caused by the closure principle. You reference the User property in your LINQ query. Because the LINQ query in itself is executed as an (anonymous) method delegate, the closure principle applies.

Quoting the above link:

In essence, a closure is a block of code which can be executed at a later time, but which maintains the environment in which it was first created - i.e. it can still use the local variables etc of the method which created it, even after that method has finished executing.

The usage of the User property is subject to this principle. Its value can have changed upon the execution of the LINQ query. To protect against this, the User property should be copied to a local variable and that referenced in the LINQ query. Like so:

var user = User;
var users = msgList.Select(m => m.From)
                   .Union(msgList.Select(m => m.To))
                   .Distinct()
                   .Where(u => u.ID != user.ID)
                   .ToList();

Update

When using a local reference copy to the user property, another possibility for the NullReferenceException may lie with the Select-Union-Distinct methods. When calling ToList, the Where clause is executed on all items in the union of the two Select clauses. By default, Distinct executes the Equals method from the IQuality interface, which would be called on the elements from Select(m => m.From) . If this element is null, it would cause the NullReferenceException.

Community
  • 1
  • 1
Wicher Visser
  • 1,433
  • 11
  • 20
  • I've assigned `User.ID` to a local variable at the beginning of the function, I'm still getting the error. – Can Poyrazoğlu Jul 06 '16 at 10:21
  • what type is user and user.ID? Can you please post the whole method (or at least the relevant part thereof)? – Wicher Visser Jul 06 '16 at 10:31
  • I've updated my question to include more details about classes. also, I'm not using `User.ID` at any place in the query (I forgot to update one instance of it in the question, just fixed). – Can Poyrazoğlu Jul 06 '16 at 10:48
  • Thanks. Can you check if my second explanation applies? That is, you may have null values in your To and From fields. Even though you are saying these are guaranteed to be non-null (how?) – Wicher Visser Jul 06 '16 at 11:22
  • Also, what is the lifespan of your DB context? What version of EF are you using? Is it possible another thread alters the context cache and nullifies a To or From reference? – Wicher Visser Jul 06 '16 at 11:28
  • `From` and `To` have corresponding foreign keys `FromID` and `ToID` and they are annotated with `Required` attribute. I've also executed `select * from messages` raw SQL query from SQL Server Management Studio and there and all FromIDs and ToIds are set at the database level. – Can Poyrazoğlu Jul 06 '16 at 11:28
  • do you use AsNoTracking on the entities? – Wicher Visser Jul 06 '16 at 11:39
  • Nope, there is nothing related to that anywhere in the entire project. – Can Poyrazoğlu Jul 06 '16 at 11:51
  • That means EF caches the entities. From your problem description I deduce that it is most likely related to another process influencing the context. Can you write the msgList objects to output (console) before the exception occurs. It should show you what properties are null. – Wicher Visser Jul 06 '16 at 11:57
  • reg. your last update, I was referring to the To and From properties being null, not the message objects. – Wicher Visser Jul 06 '16 at 12:06
  • `To` and `From` should never be null, but caching may be the cause of the problem. I'll investigate it further. It also explains why it seems to happen "randomly". – Can Poyrazoğlu Jul 06 '16 at 12:07
  • Ok. Keep me informed. I am curious as to what is happening – Wicher Visser Jul 06 '16 at 12:09
  • I am using Entity Framework Extended's caching functionality (https://github.com/loresoft/EntityFramework.Extended) at a completely irrelevant controller, which cannot be sharing the same context (I have hashtags functionality at somewhere else in my app and it's used only to serve the hashtags of the same hashtag search query for a few minutes, saving a roundtrip to the db). While it has nothing to do with this, maybe could it be interfering with some properties of EF in general? – Can Poyrazoğlu Jul 06 '16 at 12:15
  • That is beyond my knowledge – Wicher Visser Jul 06 '16 at 12:47