unosquare / embedio

A tiny, cross-platform, module based web server for .NET
http://unosquare.github.io/embedio
Other
1.45k stars 175 forks source link

Static method requires null instance, non-static method requires non-null instance. #542

Closed GF-Huang closed 2 years ago

GF-Huang commented 2 years ago

I use Stylet as MVVM framework, it contains a IoC component for DI.

Screenshots This work:

image

This not work:

image

Desktop (please complete the following information):

Additional context Add any other context about the problem here.

rdeago commented 2 years ago

Hello @GF-Huang, thanks for using EmbedIO!

While this could be a bug in WebApiModuleBase, I have not been able to reproduce it.

Can you post the exception's stack trace? It will probably help determine the exact cause of the issue.

GF-Huang commented 2 years ago

image

image

GF-Huang commented 2 years ago

BTW, does EmbedIO supports model validation in post method while body is json?

rdeago commented 2 years ago

Hello @GF-Huang, sorry for the late answer.

EmbedIO does no model validation as of now. It will probably be easier to add it in version 4, because the deserialization APIs will be more open. For more details, keep an eye on #546.

As for the original issue, I think it may have to do with the actual value of _apiControllerFactory. Are you passing a static or instance method to ApiService's constructor?

GF-Huang commented 2 years ago

As for the original issue, I think it may have to do with the actual value of _apiControllerFactory. Are you passing a static or instance method to ApiService's constructor?

I'm not sure, but this depends on Stylet implementation, and this is its docs about Func<T> factory.

rdeago commented 2 years ago

@GF-Huang thanks for the link. I've had a look at both the docs and StyletIoC's source code.

If I understand correctly, you register ApiController as an injectable type, then your constructor needs a Func<ApiController>, so StyletIoC automnatically generates a factory function for you.

How it generates the factory function is the interesting part IMHO: it uses types in the System.Reflection.Emit namespace to generate IL code directly; it's almost as if a C++ function wrote a machine code function in a memory buffer and returned a pointer to the buffer. 😱

There are actually no "functions" in .NET, only methods. A method, as you know, may be static or non-static; a non-static method requires a "this" reference at runtime. Every method has some associated metadata, among which there's an "IsStatic" flag that tells whether the method needs a "this" reference (IsStatic = 0) or not (ISStatic = 1).

When you generate methods using System.Reflection.Emit, you can do pretty much anything, including screwing up badly. 😁 For instance, you can generate an instance of a delegate that points to a non-static method but with a null "this". I suspect this is what happens here.

When WebModuleBase uses the delegate instance in a LINQ expression, System.Linq.Expression checks the congruence between the method's metadata and the "this" reference contained in the delegate instance, finds IsStatic=0 and this=null, and throws an exception.

In other words, this is most probably a bug in Stylet. The delegate usually works because, despite it being non-static, never references "this" in code.

Your workaround (enclosing a call to the delegate in a lambda, () => _apiControllerFactory()) is correct. The contents of the delegate instance are unknown at compile time and the compiled lambda does no congruence check at run time, so the delegate is called and, since it doesn't use "this", it runs without problems.

Closing this issue since it doesn't seem to be a bug in EmbedIO after all. Feel free to reopen if further examination proves me wrong (which is totally possible, as I'm no expert when it comes to IL code and System.Reflection.Emit).

GF-Huang commented 2 years ago

Got it, and thanks your detailed analysis. 🌹