Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refitter Source Generator - generated code not being picked up by Refit's Source Generator #100

Closed
guillaumeserale opened this issue Aug 8, 2023 · 8 comments · Fixed by #102
Assignees
Labels
bug Something isn't working help wanted Extra attention is needed

Comments

@guillaumeserale
Copy link
Contributor

When using the new Refitter Source Generator to generate Refit Interfaces and classes, the generated code is not being picked up by Refit's Source Generator:

image

This makes the Refitter Source Generator unusable for now.

Link to repo to reproduce the issue.

Any ideas on how to fix this ?

@christianhelle
Copy link
Owner

Thanks for reporting @guillaumeserale

the source generator has so far been an experiment that I started playing around with lately

I cloned your example repo and updated the .csproj to this:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
    <CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Refit.HttpClientFactory" Version="7.0.0" />
    <PackageReference Include="Refitter.SourceGenerator" Version="0.7.2">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

  <Target Name="ExcludeSourceGeneratedFiles" BeforeTargets="CoreCompile">
    <ItemGroup>
      <Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs" />
    </ItemGroup>
  </Target>

  <Target Name="IncludeSourceGeneratedFiles" AfterTargets="CoreCompile">
    <ItemGroup>
      <Compile Include="$(CompilerGeneratedFilesOutputPath)/**/*.cs" />
    </ItemGroup>
  </Target>

</Project>

Which placed the generated files within the project and excludes the files right before compilation to avoid duplicate types. It looks like this in Visual Studio

image

So the problem here is that I have honestly only really tested that the project output code builds, I don't think I have actually ran any project using the source generated code yet.

You're absolutely right, it doesn't work to run the code at all! I just get this error

System.InvalidOperationException: IMyApiClient doesn't look like a Refit interface. Make sure it has at least one method with a Refit HTTP method attribute and Refit is installed in the project.
   at Refit.RestService.GetGeneratedType(Type refitInterfaceType) in /_/Refit/RestService.cs:line 169
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Refit.RestService.For(Type refitInterfaceType, HttpClient client, IRequestBuilder builder) in /_/Refit/RestService.cs:line 76
   at Refit.RestService.For[T](HttpClient client, IRequestBuilder`1 builder) in /_/Refit/RestService.cs:line 20
   at Refit.HttpClientFactoryExtensions.<>c__2`1.<AddRefitClient>b__2_3(HttpClient client, IServiceProvider serviceProvider) in /_/Refit.HttpClientFactory/HttpClientFactoryExtensions.cs:line 63
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass2_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at lambda_method1(Closure, Object, HttpContext)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)

No idea how to fix this yet, or if its even possible to fix it, since I'm not sure how to chain these source generators or how to ensure that Refitter generates code before Refit generate codes

This is definitely an interesting problem! I should probably deprecate the source generator NuGet for now and mark it as invisible

@christianhelle christianhelle added the bug Something isn't working label Aug 8, 2023
@christianhelle christianhelle self-assigned this Aug 8, 2023
@christianhelle christianhelle added the help wanted Extra attention is needed label Aug 8, 2023
@christianhelle
Copy link
Owner

christianhelle commented Aug 8, 2023

@guillaumeserale I found a dirty workaround...

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
    <CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Refit.HttpClientFactory" Version="7.0.0" />
    <PackageReference Include="Refitter.SourceGenerator" Version="0.7.2" />
  </ItemGroup>

  <Target Name="ExcludeSourceGeneratedFiles" BeforeTargets="CoreCompile">
    <ItemGroup>
      <Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/Refitter.SourceGenerators.RefitterSourceGenerator/*.cs" />
    </ItemGroup>
  </Target>

  <Target Name="IncludeSourceGeneratedFiles" AfterTargets="CoreCompile">
    <ItemGroup>
      <Compile Include="$(CompilerGeneratedFilesOutputPath)/**/Refitter.SourceGenerators.RefitterSourceGenerator/*.cs" />
    </ItemGroup>
  </Target>

</Project>

This will keep the Refit generated code and only exclude the Refitter generated code during compilation

image

@christianhelle
Copy link
Owner

I deprecated all versions of the source generator in nuget.org and marked them to have critical issues

image

image

thanks again for bringing this up @guillaumeserale

I hope there's a solution to this

@guillaumeserale
Copy link
Contributor Author

@christianhelle Thanks for the detailed explanation and analysis.
I wish I could help with a potential solution, but unfortunately, I am not well-versed in Roslyn's Source Generators 😕.

@guillaumeserale
Copy link
Contributor Author

FYI: There's an open issue in Roslyn about the sequence order of Source Generators.

@christianhelle
Copy link
Owner

christianhelle commented Aug 10, 2023

What do you think of this experience @guillaumeserale ?

Recording 2023-08-10 at 21 34 37

Recording 2023-08-10 at 21 50 25

The down side with this current source generator implementation is that you are required to commit the generated code to source control. On the hand, if you use the CLI Tool to generate code, then you are committing generated code to source control anyway

@guillaumeserale
Copy link
Contributor Author

@christianhelle I've played a bit with your latest change.
If I understand correctly, the classes are generated by Refitter.SourceGenerator during the build process into the Generated Folder.
Then, Refit.Generator.InterfaceStubGeneratorV2 picks them up to generate its code.

Pretty clever 👍

I really wished the Roslyn team would have added some execution priority mechanism in the SourceGenerator.
Until then, what you implemented is the next best thing.

@christianhelle
Copy link
Owner

@guillaumeserale Yes, that was the best I could do for now 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Extra attention is needed
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants