Kategorier
software development Troubleshooting tutorials

C# dotnet standard and .Net Framework causing MissingMethodException in mscorlib

Brief

The Aspose.Email library is used to build emails and send using SMTP.

  • reason for using Aspose at first is that the project have the Aspose.Total license, including Email, and other Aspose libraries used for handling documents.

The Solution structure and implementation of Aspose.Email

  • ”EmailProcessor” a dotnet standard 2.0 Library implementing Aspose.Email
  • ”EmailProcessor.Test” a dotnet core 3.1 unit test project
  • ”WindowsService” a .Net Framework (4.8) service project;
    Framework and not dotnet core, is decided due to the environment. However, the Library (EmailProcessor) was supposed to be in dotnet standard.

In EmailProcessor I have a wrapper class, EmailDispatcher, calling the Aspose smtp client to send email.

The following is the code of interest.

//inside the using class, lets call it EmailConsumer.cs
_emailDispatcher.SendEmail(context.ReplyMessage)
// inside EmailDispatcher.cs
await _smtpClient.SendAsync(message);

Hands on troubleshooting (i.e. banging my head in the wall)

While developing and debugging with my xUnit integration test (dotnet core project) everything runs fine. But when installing the windows service the process ends in EmailConsumer at the calling row _emailDispatcher.SendEmail(context.ReplyMessage)
when Debugging the windows server it takes some time to pinpoint this, and no exception is caught in my debugging (however, later on, I can correlate the following line in visual studio ”output” window)

  • Exception thrown: ’System.MissingMethodException’ in mscorlib.dll

With my emailDispatcher,
note: I assumed the debugger would take me inside the EmailDispatcher class and my breakpoint on ”smtpClient.SendAsync”, but (probably due to the class using Aspose.Email) I’m not able
to step into ”emailDispatcher.SendEmail”
the logged error, ’System.MissingMethodException’, is shown many times, therefore I was not sure if it had anything to do with the error I had, until I put a brakepoint on the row ”_emailDispatcher.SendEmail(context.ReplyMessage)”, and executed the same code in visual studio ”Immediate Window” ; that gave me a better detailed error message as below

Exception thrown: 'System.MissingMethodException' in mscorlib.dll
Method not found: 'System.Threading.Tasks.Task Aspose.Email.Clients.Smtp.SmtpClient.SendAsync(Aspose.Email.MailMessage)'.


   At EmailProcessor.Email.EmailDispatcher.<SendEmail>d__4.MoveNext() in C:\Git\EmailProcessor\Email\EmailDispatcher.cs:line 107
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.Start[TStateMachine](TStateMachine& stateMachine)
   at EmailProcessor.Email.EmailDispatcher.SendEmail(MailMessage message)

Stackoverflow to the rescue (?)

Some good answers for this type of error are found in this thread https://stackoverflow.com/questions/8058832/system-missingmethodexception-method-not-found

  • ”This is a problem which can occur when there is an old version of a DLL still lingering somewhere around. Make sure that the latest assemblies are deployed and no duplicated older assemblies are hiding in certain folders. Your best bet would be to delete every built item and rebuild/redeploy the entire solution.”
  • ”In particular, be sure an old version is not in the GAC”

So looking in GAC, the global cache, (run ”gacutil /l” in powershel for VS, to list all cached)

I realized Aspose libraries are not available in GAC (mostly Microsoft stuff here)

  • Double check, more than once, that nuget packages are correct (note: the windows service does not reference the Aspose libraries directly)
  • And once more validate I have the ”RestoreProjectStyle” set to PackageReference (see more on that issue in this github page , this can also be an issue when mixing dotnetstandard with .NET Framework
  • I Erase Bin and Obj folders for all projects and rebuild. (yeah, sometimes it’s just as simple)
  • Still not working

Now some more digging,
the SmtpClient (netstandard) decompiled looks like this.

// Decompiled with JetBrains decompiler
// Type: Aspose.Email.Clients.Smtp.SmtpClient
// Assembly: Aspose.Email, Version=20.1.0.0, Culture=neutral, PublicKeyToken=716fcc553a201e56
// Assembly location: C:\Users\Hemma\.nuget\packages\aspose.email\20.1.0\lib\netstandard2.0\Aspose.Email.dll
...

I confirm that the method, SendAsync, does exist;

  • sure I already knew this since I can use it while coding, but still. magic things and unicorns do exists in computers.
  • and I can use it with the real expected result when running from my xunit (dotnet core)

This is what SendAsync looks like in the decompiled code (just confirming what we know)

...
/// <summary>Send the specified message.</summary>
/// <param name="message">The MailMessage that represents an email-message.</param>
/// <returns>Task object, with delegate for this operation</returns>
public System.Threading.Tasks.Task SendAsync(MailMessage message)
{
...

However… since it does not work from my .Net Framework (4.8, or 4.7.2). and I get the error pointed out above. I installed the nuget package Aspose.Email in the windows service project, since this is also a known possible workaround when referencing dotnetstandard projects from .NET Framework

The ”last resort”, before changing things or removing Aspose.Email, is to try with the Aspoe.Email nuget package installed directly into the windows service project (.Net Framework).

  • the result is the same error, mscorlib MissingMethodException

Now.
Trying to use the the SendAsync method in the Windows Service (framework project) just to see what would happen
I could see that the APIs are not the same.
In the net40 framework version there is no ”SendAsync” method, however.. there is a ”BeginSend” method that is possible to use in a similar manner.

  • IAsyncResult asyncResult = _smtpClient.BeginSend(message);
    (also documented in https://github.com/aspose-email/Aspose.Email-for-.NET/blob/master/Examples/CSharp/SMTP/SendEmailAsynchronously.cs)
  • and this method, BeginSend, is available in the dotnetstandard version – BUT, marked as deprecated and is not as convenient to use.

Validating the correct version on file level, matching the version 20.1 used in the EmailProcessor Library ; here, decompiled inside the .NET Framework project
(notice that this is the ”net40” target and not the ”dotnetstandard”)

// Decompiled with JetBrains decompiler
// Type: Aspose.Email.Clients.Smtp.SmtpClient
// Assembly: Aspose.Email, Version=20.1.0.0, Culture=neutral, PublicKeyToken=716fcc553a201e56
// Assembly location: C:\Users\Hemma\.nuget\packages\aspose.email\20.1.0\lib\net40\Aspose.Email.dll

Worth noting,

is that the Api reference, at Aspose.com, actually does not display the async methods, if it is by intention or not I do not know.
And, none of the examples in the Github repo shows the SendAsync method. – however, the examples are from up to 2018, and might be outdated)
https://apireference.aspose.com/email/net/aspose.email.clients.smtp/smtpclient/methods/send/index
https://github.com/aspose-email/Aspose.Email-for-.NET/tree/master/Examples/CSharp/SMTP

Final thoughts and lessons learned.

  • Look for exceptions even if things seem to work.
  • Use the Debug -> Windows -> Modules (available when debugging) to see where the assembly was loaded from.
  • If you are mixing different frameworks,
    check if there are differences in dependendent API implementations targeting the different frameworks (e.g. neststandard, net40)

Solution / Workaround

As a first workaround for this error I replaced ”SendAsync” with the ”Send” method (available in both netstandard and net40 Api) wrapped in a Task.

//does not work.
//await _smtpClient.SendAsync(message);

await Task.Run(() =>
    {
        _smtpClient.Send(message);

    }, cancellationToken);
  • This might be good enough.

But perhaps I will use MailKit https://github.com/jstedfast/MailKit instead (open source with good documentation and working examples),
I already switched to ImapClient from MailKit since it was smoother to use and with cancellation and had a good code example for IMAP Idling.
But I already use Aspoe to build email message, and that might need to be changed of changing the smtp sender (perhaps it’s easy to convert he message between the libraries as a mime stream).

Thats all, I hope this points you in a direction to solve whatever problem you have

assuming you don’t read things like this for fun 😉

Kategorier
event-sourcing Learning software development tutorials Uncategorized

EventStore(DB) Getting Started

https://github.com/JimiSweden/eventstore-getting-started

Here is the repository for my getting started project following the guide from eventstore.com

To be continued… Eventually