MulticastDelegate exception being thrown only in production environment
Asked Answered
C

3

5

I have a very weird issue occurring only in production environment. The exception has the message

"Delegate to an instance method cannot have null 'this'".

The method where the exception is being thrown is very simple, and worked for a long time, so the problem must be an obscure dependency in the environment, or something like that...

I'm using ASP.NET Web API, hosted in Azure, and the action method of controller is executed via AJAX.

Here is the code where the exception was thrown:

public class BlacklistService : IBlacklistService
{
    public bool Verify(string blacklist, string message)
    {
        if (string.IsNullOrEmpty(blacklist)) return true;
        var split = blacklist.ToLower().Split(';'); // exception is thrown here
        return !split.Any(message.Contains);
    }
}

Here is the relevant part of stack trace:

at System.MulticastDelegate.ThrowNullThisInDelegateToInstance() 
at System.MulticastDelegate.CtorClosed(Object target, IntPtr methodPtr) 
at MyApp.Business.Services.BlacklistService.Verify(String blacklist, String message)
at MyApp.Business.Services.ContactMessageFactory.GetVerifiedStatus(String mensagem)
at MyApp.Business.Services.ContactMessageFactory.GetMailMessage(ContactForm contactForm)
at MyApp.Business.ContactEmailService.Send(ContactForm contactForm)

Someone can figure out the possible causes of this exception? Thanks in advance.

Consulate answered 15/9, 2015 at 18:30 Comment(1)
Could you please share the code of delegate portion by which you are invoking Verify Method?Crumb
T
5

The problem lays with the fact that message is actually null. You can reproduce this quite easily:

void Main()
{
    Verify("hello", null);
}

public bool Verify(string blacklist, string message)
{
    if (string.IsNullOrEmpty(blacklist)) return true;
    var split = blacklist.ToLower().Split(';'); // exception is thrown here
    return !split.Any(message.Contains);
}

What happens is that message.Contains is passed to Func<string, bool> constructor via the method group conversion, it looks like this:

Func<string, bool> func = ((string)null).Contains;
return !split.Any(func);

And that is what causes MulticastDelegate to go bananas. You can also see that in the generated IL:

IL_0028:  ldftn       System.String.Contains
IL_002E:  newobj      System.Func<System.String,System.Boolean>..ctor
IL_0033:  call        System.Linq.Enumerable.Any

In order for this not to happen, make sure you null check message as well:

public bool Verify(string blacklist, string message)
{
    if (string.IsNullOrEmpty(blacklist)) return true;
    if (string.IsNullOrEmpty(message)) return false;

    var split = blacklist.ToLower().Split(';'); // exception is thrown here
    return !split.Any(message.Contains);
}
Terminus answered 15/9, 2015 at 18:47 Comment(2)
Correct. The exception type confused me. I missed the delegate in the method group, and I was expecting a NullReferenceException. Thanks.Consulate
Just to clarify the mistake, I figured out that in local environment the blacklist parameter is null due to configuration, so the code returns true and works.Consulate
E
4

The delegate having a null this is the method string.Contains() used towards the end, which uses your message variable as the this pointer. In other words, there is a call made where message is null.

Echinate answered 15/9, 2015 at 18:47 Comment(1)
Correct. The exception type confused me. I missed the delegate in the method group, and I was expecting a NullReferenceException.Consulate
L
1

Fails when message is null. Can use this

return !split.Any(part => (message != null && message.Contains(part)));
Laughter answered 15/9, 2015 at 18:41 Comment(3)
The form used in the OP's code is a shortcut for the code you have posted, so it is essentially identical.Echinate
This is just another syntax to express the same thing. I've used the method group syntax instead of lambda.Consulate
@LeonardoLimadaSilva yes, but null check is added here.Laughter

© 2022 - 2024 — McMap. All rights reserved.