Resolving TargetException "Non-static method requires a target" in LINQ
Photo by Ioann-Mark Kuznietsov on Unsplash
I recently came across a situation in which a seemingly innocuous piece of LINQ threw a very opaque exception when used with Entity Framework 6.
The exception was this:
Call Stack: System.Reflection.TargetException: at System.Reflection.RuntimeMethodInfo.Invoke (System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e) at System.Reflection.RuntimePropertyInfo.GetValue (System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e) at
...
System.Linq.Enumerable.SingleOrDefault (System.Linq, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a) at MyProject.MyClass`1+d__3.MoveNext (MyProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null: /path/to/src/MyProject/MyClass.cs:67)
Message: Non-static method requires a target.
The real stack trace was 84 lines long, but I've cut it down to the relevant parts.
The line of code referenced at the end of the stack trace was the closing brace of a method. Cutting the method down to its basic parts, it went something like this:
private void MyMethod(int input)
{
// more code here
var obj = GetObj(input);
var data = _context.Data.SingleOrDefault(d => d.Value = obj.Value);
// more code here
} // this is the line referenced in the stack trace
It's more obvious with the rest of the method cut out, but the problem is the SingleOrDefault
. You can see that in the penultimate line of the stack trace excerpt above, and happily my method only had one SingleOrDefault
in it.
This error means that obj
is null, so obj.Value
can't be translated to SQL for executing against the database context. You might expect this to throw a NullReferenceException
, but it doesn't. I won't pretend to know exactly why this is, but I assume it's to do with LINQ building up the query as an expression tree and only actually evaluating obj.Value
later. If I had extracted obj.Value
to the line above the LINQ, I would expect a NullReferenceException
:
private void MyMethod(int input)
{
// more code here
var obj = GetObj(input);
var val = obj.Value; // I believe this would throw a NullReferenceException
var data = _context.Data.SingleOrDefault(d => d.Value = val);
// more code here
}
Once you know what the TargetException
means, the fix is straightforward. I did a null check on obj
and everything was fine. In my case, obj
was only null because of bad data creating an invalid situation, so I threw a custom exception:
private void MyMethod(int input)
{
// more code here
var obj = GetObj(input);
if (obj is null)
{
throw new MyCustomException("Explanation of what is wrong with the data in this scenario and how to fix it.");
}
var data = _context.Data.SingleOrDefault(d => d.Value = obj.Value);
// more code here
}