Skip to content

This is a project to create Roslyn source generators for C# to lessen the boiler plate code associated with dependency injection.

License

Notifications You must be signed in to change notification settings

jerhon/hs-dependency-injection-source-generators

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Honlsoft.DependencyInjection.SourceGenerators

This is a project to test out creating a source generator(s) in Roslyn. It's a WIP and more for personal learning/ideas than for production code right now. If I decide to iterate on it further, it may become more stable.

.NET

Constructor Source Generator

Decorate a public partial class with the [Inject] attribute on fields to create a constructor which will expose them. This avoids having to write a constructor in simple situations where it's no more than assigning to a field.

public partial class MyClass {

    [Inject] private readonly string _paramter1;
    
    [Inject] private readonly string _paramter2;

}

The constructor should look something like this:

public MyClass(string _parameter1, string _parameter2) {
    this._parameter1 = _parameter1;
    this._parameter2 = _parameter2;
}

Doesn't support use cases such as inheritance.

Factory Source Generator

This project was meant to auto generate Factories for classes with constructors attributed with a [Factory] attribute. The source generator would auto implement a Factory for the class. I have a unit test that demonstrates this.

[Test]
public void TestExecutionOfSimpleFactory() 
{
    IInstanceClassFactory factory = new InstanceClassFactory(new InjectedClass());
    var testClass = factory.Create("Factory");
    Assert.AreEqual("Factory Injected!", testClass.ToString());
}

Where the class that the factory is autogenerated for is here:

public class InjectedClass {
    public override string ToString() {
        return "Injected!";
    }
}

public class InstanceClass {

    private readonly string _regularParameter;
    private readonly InjectedClass _injectedParameter;
    
    [Factory]
    public InstanceClass(string regularParameter, [Inject]InjectedClass injectedClass) {
        _regularParameter = regularParameter;
        _injectedParameter = injectedClass;
    }

    public override string ToString() {
        return $"{_regularParameter} {_injectedParameter}";
    }
}

I want to use the Factory pattern in order to create a class which has some constructor parameters which are dependencies that would be injected by say an DI container, and the others are not available until runtime.

Interface Source Generator

Generates an interface for a class. This is useful for reducing boilerplate code in situations where the interface is only used for mocking.

[Interface]
public partial class MyClass {

    [Interface]
    public void Method() {
    }
    
    [Interface]
    public void Method2() {
    }
}
public interface IMyClass {
    void Method();
    void Method2();
}
public partial MyClass : IMyClass {}

Can then DI in the interface, and use it for mocking while unit testing.

services.AddSingleton<IMyClass, MyClass>();

Combine [Inject] and [Factory] at the class level (TBD)

Add the ability to auto generate a factory when auto generating a constructor. With optional parameters provided. Allow combining the interface as well.

Lessons learned:

Just my personal experience trying to build them.

Debugging

It's clunky, visual studio requires a reload after each change to debug.

Rider is better for development

It seems like Visual Studio was keeping a reference to my old source generator after recompile. Had to restart visual studio sometimes for changes to take effect. I had better luck developing in Rider.

ISourceGenerator should not have shared variables

Originally, I had limited success.

While I was able to generate a factory, the editing experience in Rider and Visual Studio isn't working to my satisfaction. Full builds and rebuilds of the solution would work fine. However, editing in the project would cause the generated source to be lost and I would start getting errors that the generated factories don't exist.

Reading through this a common problem is having a shared field or property across execution runs: dotnet/roslyn#49249 The intent is that every time execute is run, it should not share state in the class as the two can be run out of sequence and/or by multiple threads.

ISyntaxReciever vs ISyntaxContextReceiver

ISyntaxReceiver = just gets the syntax nodes

ISyntaxContextReceiver = gets the syntax nodes along with the semantic model

Bad Documentation

There's really only a page or two in the official docs, and certain key markdown pages that are linked to are incomplete or weren't revisited after implementation.

About

This is a project to create Roslyn source generators for C# to lessen the boiler plate code associated with dependency injection.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages