dotnet / orleans

Cloud Native application framework for .NET
https://docs.microsoft.com/dotnet/orleans
MIT License
10.07k stars 2.03k forks source link

Using named C# 7 ValueTuples in grain interfaces results in a compilation error #3421

Closed Arshia001 closed 6 years ago

Arshia001 commented 7 years ago

Using a named C# 7 ValueTuple in the definition of a grain interface results in a compilation error.

This:

public interface ISomeGrain : IGrainWithIntegerKey
{
    Task<(bool someBool, string someString)> Func();
}

results in this error in the generated .Orleans.g.cs file:

CS8141  The tuple element names in the signature of method must match the tuple element names of interface method (including on the return type).

The same does not happen if the tuple is unnamed.

Arshia001 commented 7 years ago

However, even when using unnamed tuples as follows, trying to use the grain results in this error being thrown from the client's GetGrain method:

Task<(bool, string)> Func();
System.ArgumentException: 'Cannot find an implementation class for grain interface: ISomeGrainInterface. Make sure the grain assembly was correctly deployed and loaded in the silo.'
sergeybykov commented 7 years ago

To clarify, the workaround is to use Task<Tuple<bool, string>> Func() instead, correct?

Arshia001 commented 7 years ago

Correct. Any custom class will also do. The problem is specifically with the new ValueTuple syntax and class.

sergeybykov commented 7 years ago

I'll move it to backlog then, since there's a simple workaround.

Arshia001 commented 7 years ago

It's less about usability, more about the fact the reported errors are completely unrelated to the problem and they take a lot of time for the unsuspecting programmer to debug and fix.

sergeybykov commented 7 years ago

Understood. Hopefully, it's just a small tweak to the codegen. We would appreciate a contribution.

kutensky commented 6 years ago

@Arshia001 Could you try to reverify this issue? I couldn't reproduce it. For me it works fine. Codegen do its work and create ValueTuple class in ".orleans.g.cs". No warning, no errors. Clients await for valueTuple object and it receives it back without any errors.

Arshia001 commented 6 years ago

@kutensky Sure. I don't have access to the original source where I encountered this issue, but I'll see if I can't reproduce it.

Liversage commented 6 years ago

@kutensky, @Arshia001: I still face this issue. I have been on a beta release of version 2.0 for some time and after hitting this problem I decided it was time to update to the 2.0.0 release hoping that it would fix this problem. However, the problem still exists when I have a grain method that returns a ValueTuple. Any of these definitions will lead to a code generator warning:

Task<(int Foo, string Bar)> GetBlahBlahTupleAsync();
Task<(int, string)> GetBlahBlahTupleAsync();
Task<ValueTuple<int, string>> GetBlahBlahTupleAsync();

Here is the warning:

warn: Orleans.CodeGenerator.RoslynCodeGenerator[101723]
      Exception loading types from assembly 'Grains, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null':
      Exc level 0: System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
         at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
         at System.Reflection.RuntimeAssembly.get_DefinedTypes()
         at Orleans.Runtime.TypeUtils.GetDefinedTypes(Assembly assembly, ILogger logger) in D:\build\agent2\_work\8\s\src\Orleans.Core\CodeGeneration\TypeUtils.cs:line 561
      Exc level 1: System.TypeLoadException: Method 'GetBlahBlahTupleAsync' in type 'Grains.BlahBlahGrain' from assembly 'Grains, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation..
System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
   at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
   at System.Reflection.RuntimeAssembly.get_DefinedTypes()
   at Orleans.Runtime.TypeUtils.GetDefinedTypes(Assembly assembly, ILogger logger) in D:\build\agent2\_work\8\s\src\Orleans.Core\CodeGeneration\TypeUtils.cs:line 561

Looking into the code generated file (.orleans.g.cs) there is no code generated for the grain. So the effect is that my code compiles but when I start the silo I get exceptions when Orleans tries to activate BlahBlahGrain because there is no implementation of the grain interface.

I can work around this issue by using a Tuple instead of a ValueTuple:

Task<Tuple<int, string>> GetBlahBlahTupleAsync();

Another option is of course to use a custom class that represents the tuple.

kutensky commented 6 years ago

Hi @Liversage. I still have troubles reproducing this issue. I used a code you provided and everything work fine for me. No errors, no warning. ".orleans" file contains generated methods for "GetBlahBlahTupleAsync". And when I call these method from client, server grain answers as expected. Could you provide more information how it happens? Are you sure that you have at least .Net framework 4.6.3 ? HelloWorld.Interfaces.orleans.g.cs.txt

Liversage commented 6 years ago

Hi @kutensky,

It took me a while to find the time understand why I still have this problem. However, after some investigation I was able to reproduce it with a simple project configuration.

I have a Visual Studio solution with two .NET Standard library projects. One project contains the grain interface and has references to Microsoft.Orleans.Core.Abstractions and Microsoft.Orleans.OrleansCodeGenerator.Build. The other project contains the grain implementation and has references to Microsoft.Orleans.Core and Microsoft.Orleans.OrleansCodeGenerator.Build as well as the grain interface project.

The grain interface has a method that returns a Task<ValueTuple<..., ...>>.

With this setup everything works as you describe. E.g. the code generator is able to produce the right code even though ValueTuple is used in the grain interface.

However, I then change the target framework of my grain project to .NET 4.7. E.g. in the .csproj file I replace <TargetFramework>netstandard2.0</TargetFramework> with <TargetFramework>net47</TargetFramework>.

When I rebuild the project the code generator now reports the ReflectionTypeLoadException and there will be no code generated for my grain. However, while not including my grain a ton of other types are now included and the generated file will grow from 2K to 310K.

So this problem only occurs if the grain interface is declared in a .NET Standard library and the grain implementation is in a full .NET Framework library. To work around this problem I can either stop using ValueTuple or ensure that I don't mix frameworks across grain interface and grain implementation projects.

(The reason my project configuration is like this is because I want to build for .NET Core using .NET Standard. However, I still have a single dependency that is not .NET Standard so while I wait for the .NET Standard version of this dependency I use this somewhat unusual configuration.)

kutensky commented 6 years ago

@Liversage Thanks a lot for detailed answer. I'll see what we can do

tcz717 commented 6 years ago

I found a very interesting phenomenon: when I compile a method like Task DoSomeThingAsync((int X, int Y) pos) in Visual Studio 2017, I get the expected result; however, when I try the same code using dotnet build, I get an error like error CS8141: The tuple element names in the signature of method 'OrleansCodeGenSomethingReference.DoSomeThingAsync((int, int))' must match the tuple element names of interface method 'ISomething.DoSomeThingAsync((int X, int Y))' (including on the return type)

tcz717 commented 6 years ago

I think maybe this problem relates to here https://github.com/dotnet/roslyn/pull/20838

tcz717 commented 6 years ago

Finally, I found the reason. This change https://github.com/dotnet/roslyn/pull/20838 was applied in MSBuild 15.5 version. If we update Visual Studio in time, we will successfully compile the code and the error CS8141 will not happen. However, if we use dotnet-sdk-2.0.* version, the MSBuild inside is 15.4. After I installed dotnet-sdk-2.1.3 (MSBuild 15.5.179.9764), the build succeeded, which proofs my finding.

Arshia001 commented 6 years ago

So, basically, if the code generator matches the tuple element names, the build will succeed, right?

tcz717 commented 6 years ago

@Arshia001 If you use 2.1 version sdk, the build will succeed even the tuple element names not matched

sergeybykov commented 6 years ago

This is resolved in 2.1.0.