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

generate extension-method for registering generated classes with Microsoft.Extensions.DependencyInjection #63

Open
earloc opened this issue Dec 28, 2022 · 3 comments
Labels
Milestone

Comments

@earloc
Copy link
Owner

earloc commented Dec 28, 2022

The source-generator should emit an extension-method for use with IServiceCollection, which registers all types being generated, to reduce the amount of boilerplate-code necessary in order to utilize injection of typealized resources.

@earloc earloc added enhancement New feature or request dependencies Pull requests that update a dependency file labels Dec 28, 2022
@earloc earloc added this to the v0.8 milestone Dec 28, 2022
@earloc earloc mentioned this issue Dec 29, 2022
@earloc earloc changed the title generate extension-method for registering IStringTypealizR<T> with Microsoft.Extensions.DependencyInjection generate extension-method for registering classes with Microsoft.Extensions.DependencyInjection Dec 30, 2022
earloc added a commit that referenced this issue Dec 30, 2022
@earloc earloc modified the milestones: v0.8, v0.9 Dec 30, 2022
@earloc
Copy link
Owner Author

earloc commented Jan 12, 2023

think about the following project structure, where all projects have TypealizR consumed:

  • Solution
    • Library A
    • Library B
      • references Microsoft.Extensions.DependencyInjection.Abstractions
    • Library C
      • references Library B
    • Library D
    • Library E
      • references Library D
      • references Microsoft.Extensions.DependencyInjection.Abstractions
    • App
      • references Library A
      • referencesLibrary B
      • references Library C
      • does not reference Library D

      • references Library E
      • references Microsoft.AspNetCore
        • references Microsoft.Extensions.Logging
          • references Microsoft.Extensions.DependencyInjection.Abstractions

The generated method(s) would need to register all generated types from

  • Library A
  • Library B
  • Library C
  • Library D
  • Library E

Ideally in a way, that does not need knowledge within App of which libraries are actually referenced. At least within user-code.
Ideally2: without the need to reflect on said references, though...falling back to reflection actually defeats the whole purpose of source generation.

Problem here: source-generators cannot be stacked upon each other:
https://stackoverflow.com/a/68506422/4136104
However, it looks like assembly references affect the order of invocations within a solution.

That said, the expected order of invocations would be:

  1. Library A
  2. Library B
  3. Library C
  4. Library D
  5. Library E
  6. App <--this is the assembly that´d actually call the extension-method

If that´s true, we could choose to scan all direct and indirect references of Appfor types emitted by the source-generator and generate an extension-method which registers the relevant types with IServiceCollection

Just wondering how we could determine when to actually emit such an extension-method - which assembly sits "on top" enough to actually benefit from such method and needs to actually register dependencies?

Is it OK to jsut emit variants on all projects?
Then theses would need to exist on seperate namespaces, though...

Oh, and it should not degrade build-times in a noticable way^^

@earloc
Copy link
Owner Author

earloc commented Jan 12, 2023

Most simple solution would probably be to generate said extension-method for all of the above assemblies, which (directly or indirectly) reference Microsoft.Extensions.DempendencyInjection.Abstractions within a namespace preventing collisions.

In above sample solution, that would be in the following namespaces:

  • Microsoft.Extensions.DependencyInjection.TypealizR.LibraryB
  • Microsoft.Extensions.DependencyInjection.TypealizR.LibraryC
  • Microsoft.Extensions.DependencyInjection.TypealizR.LibraryE
  • Microsoft.Extensions.DependencyInjection.TypealizR.App

or the other way around:

  • TypealizR.LibraryB.Microsoft.Extensions.DependencyInjection
  • TypealizR.LibraryC.Microsoft.Extensions.DependencyInjection
  • TypealizR.LibraryE.Microsoft.Extensions.DependencyInjection
  • TypealizR.App.Microsoft.Extensions.DependencyInjection

(With or without the TypealizR-prefix)

Such an approach would also work when auto-registrations would need to be generetaed for other containers...

@earloc earloc changed the title generate extension-method for registering classes with Microsoft.Extensions.DependencyInjection generate extension-method for registering generated classes with Microsoft.Extensions.DependencyInjection Jan 12, 2023
@earloc
Copy link
Owner Author

earloc commented Jan 13, 2023

An alternative approach could be to let the source-generator scan referenced assemblies for existence of any present occurence of the generated extension method and incorporate it within its own generation.
Performance benefit could be the fact that there´s a discrete naming-scheme to follow:

namespace Microsoft.Extensions.DependencyInjection.TypealizR.Library.A;
public static TypealizRExtensions{
  public static IServiceCollection AddTypealizR(this IServiceCollection services) {
      services.AddScoped(x => x.GetRequiredService<IStringLocalizer<ResourcesFromA>>().Typealize());
      return services;
  }
}
namespace Microsoft.Extensions.DependencyInjection.TypealizR.Library.B;
public static TypealizRExtensions{
  public static IServiceCollection AddTypealizR(this IServiceCollection services) {
      services.AddScoped(x => x.GetRequiredService<IStringLocalizer<ResourcesFromB>>().Typealize());
      return services;
  }
}
namespace Microsoft.Extensions.DependencyInjection.TypealizR.Library.C;
public static TypealizRExtensions {
  public static IServiceCollection AddTypealizR(this IServiceCollection services) {
      services.AddScoped(x => x.GetRequiredService<IStringLocalizer<ResourcesFromC>>().Typealize());
      Microsoft.Extensions.DependencyInjection.TypealizR.Library.B.TypealizRExtensions.AddTypealizR(services);
      return services;
  }
}
namespace Microsoft.Extensions.DependencyInjection.TypealizR.Library.D;
public static TypealizRExtensions {
  public static IServiceCollection AddTypealizR(this IServiceCollection services) {
      services.AddScoped(x => x.GetRequiredService<IStringLocalizer<ResourcesFromD>>().Typealize());
      return services;
  }
}
namespace Microsoft.Extensions.DependencyInjection.TypealizR.Library.E;
public static TypealizRExtensions {
  public static IServiceCollection AddTypealizR(this IServiceCollection services) {
      services.AddScoped(x => x.GetRequiredService<IStringLocalizer<ResourcesFromE>>().Typealize());
      Microsoft.Extensions.DependencyInjection.TypealizR.Library.D.TypealizRExtensions.AddTypealizR(services);
      return services;
  }
}
namespace Microsoft.Extensions.DependencyInjection.TypealizR.App;
public static TypealizRExtensions {
  public static IServiceCollection AddTypealizR(this IServiceCollection services) {
      services.AddScoped(x => x.GetRequiredService<IStringLocalizer<ResourcesFromApp>>().Typealize());
      Microsoft.Extensions.DependencyInjection.TypealizR.Library.A.TypealizRExtensions.AddTypealizR(services);
      Microsoft.Extensions.DependencyInjection.TypealizR.Library.B.TypealizRExtensions.AddTypealizR(services);
      Microsoft.Extensions.DependencyInjection.TypealizR.Library.C.TypealizRExtensions.AddTypealizR(services);
    //Microsoft.Extensions.DependencyInjection.TypealizR.Library.D.TypealizRExtensions.AddTypealizR(services);
      Microsoft.Extensions.DependencyInjection.TypealizR.Library.E.TypealizRExtensions.AddTypealizR(services);
      return services;
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant