0

First, this is not answered by this SO post, as that describes simple/basic object reference problems. What I'm experiencing is related to multithreaded async processing, which the other post doesn't solve.

I have a multithreaded .NET winforms app and I'm doing this:

if ( paramList != null ) {
   lock ( paramList ) {
      foreach ( DictionaryEntry param in paramList ) {
         command.Parameters.AddWithValue(param.Key.ToString(), param.Value);
      }
   }
}

paramList is an OrderedDictionary.

I sporadically get this error on the foreach line:

Object reference not set to an instance of an object.

enter image description here

As you can see, param.Key is null and param.Value is null. But this makes no sense because there are no nulls in paramList, as you can see here:

enter image description here

In the screenshot you can see only index 2, but I examined index 0 and 1 also, same thing, valid data, no nulls.

I'm not experienced with multithreaded apps, but I put that block in a lock() due to responses in this SO post. Before putting in the lock() I was sporadically getting the error Collection was modified; enumeration operation may not execute. After putting in the lock, that error went away, but now I'm getting the object reference as shown above.

What can I do to solve this?

EDIT

Taking the advice of a few posters, I did this:

private static object syncLock = new object();

and then down in the usage:

lock ( syncLock ) {
   if ( paramList != null ) {
       foreach ( DictionaryEntry param in paramList ) {
          command.Parameters.AddWithValue(param.Key.ToString(), param.Value);
       }
   }
}

That seems to have solved the object reference error (thanks everyone), but now I sporadically get:

Collection was modified; enumeration operation may not execute.

Because I got a completely different error after trying this new approach, I created a new SO question. I'm not sure if that was the right thing to do because now it seems these problems are related and I'm just seeing different symptoms of the same core problem.

Still looking for a solution if anyone has ideas.

HerrimanCoder
  • 6,835
  • 24
  • 78
  • 158
  • 1
    Try to lock different object not the `paramList` see the example : https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/lock-statement – Rampage64 Nov 10 '19 at 00:15
  • 1
    usually you want a: private readonly object myLock = new object(), if its a class variable you're trying to protect. Else make the lock static. 2nd: test for null before AddWithValue. 3rd you need to use the lock EVERYWHERE where you access your paramList, if you lock here, but don't lock at another position where something gets added or deleted, then you get this error. – Charles Nov 10 '19 at 00:23
  • 1
    Does this answer your question? [What is a NullReferenceException, and how do I fix it?](https://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-and-how-do-i-fix-it) – Trevor Nov 10 '19 at 04:16
  • I think that you are in a world of pain because you started doing multithreading without having a solid understanding of the concepts. If you have time to read, this is a good resource: [Threading in C#](http://www.albahari.com/threading/) by Joseph Albahari. – Theodor Zoulias Nov 10 '19 at 07:21

3 Answers3

1

(Preferred) Change your paramList so you don't need to lock it.

If you need to share then:

  1. Lock on a object that doesn't change e.g. private readonly object l = new object();, or private static readonly, if needed.
  2. Check that the code that fills paramList is also locked.
tymtam
  • 31,798
  • 8
  • 86
  • 126
  • 1
    I am really confused why this was downvoted. And I am not even the poster! – Christopher Nov 10 '19 at 01:44
  • I'm confused what you're suggesting. Can you please give sample code based on my posted code? – HerrimanCoder Nov 10 '19 at 02:48
  • @HerrimanCoder: It is frowned upon and I am not a fan of copying another posters work, but this is basically the same thing I said - use lock. I did add a few links and more explanations however. So I felt it was okay-ish. – Christopher Nov 10 '19 at 10:40
0

Your code will not work. What if it was not null during the null check, but is null when you come to the lock?

There is the standing advise to always have a dedicated thing to lock onto. A object _mutex = new object(); has become customary.

Edit: Private is implied, but you propably want to make it readonly as Charles suggested.

If you lock on variables/fields, you might run into this issues. Or boxing of primitives (wich does not do anything, because primitves are always put into different boxes). Or somebody else trying to lock it up the call stack, causing a deadlock.

Example code:

//_mutex is private, readonly and exist exclusively for this operation
lock(_mutex){
  //You only do null checks after you got a lock
  if ( paramList != null ){
    foreach ( DictionaryEntry param in paramList ) {
      command.Parameters.AddWithValue(param.Key.ToString(), param.Value);
    }
  }
}

All other code reading, writing or processing the variable paramList, any of it's Dictionary entires and their fields - including any copies* - also need to be in a lock(_mutex). So they can not collide with one another.

*copies of primitive types and string are the exception. Builtin Primitive types are inmutable. Also do not usually use reference mechanics. And the String class was also made inmutable, to allow it to work this way

Christopher
  • 9,634
  • 2
  • 17
  • 31
  • I'm confused what you're suggesting. Can you please give sample code based on my posted code? – HerrimanCoder Nov 10 '19 at 02:48
  • After trying this and running it, I now get `collection was modified`. – HerrimanCoder Nov 11 '19 at 01:50
  • @HerrimanCoder: Foreach does not work with collections, only enumerators. The conversion is done implicitly, but Enumerators have one rule: "If you repesent a collection and the underlying colletion changes, the Enumerators must become invalid - and throw that Excpetion". https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ienumerator-1#remarks – Christopher Nov 11 '19 at 10:06
  • @HerrimanCoder: So there are 2 options: 1) You have not show us that this code does something like removing elements from teh collection 2)some other code is changing paramList while it is in that foreach loop. Wich clearly says that this other code is *not* in a lock statement. Did you forgot some code that reads or deletes from the instance you put into paramList? – Christopher Nov 11 '19 at 10:08
0

This is the solution that solved all my problems:

var connection = getConnectionFromPool(sourceOrDC);

try {
   lock ( connection ) {
      using ( SqlCommand command = new SqlCommand(sql, connection) ) {
         command.CommandType = CommandType.Text;

         if ( paramList != null ) {
            foreach ( DictionaryEntry param in paramList ) {
               command.Parameters.AddWithValue(param.Key.ToString(), param.Value);
            }
         }

         command.CommandTimeout = 0;
         command.ExecuteNonQuery();
      }
   }

   return true;
}

I had to enclose more of the code in lock(), and lock the connection.

HerrimanCoder
  • 6,835
  • 24
  • 78
  • 158