Skip to content

Instantly share code, notes, and snippets.

@GeeWee
Last active September 13, 2021 06:30
Show Gist options
  • Save GeeWee/c8dd754fe17ebfd3665c8c3ed220f328 to your computer and use it in GitHub Desktop.
Save GeeWee/c8dd754fe17ebfd3665c8c3ed220f328 to your computer and use it in GitHub Desktop.
Fun with attributes
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// This uses Scrutor to scan for all classes with a [Transient] [Scoped] or [Singleton] attribute and adds them as any matching interfaces
// No need to add any extra logic in Startup.cs when defining classes
services.Scan(i =>
i.FromCallingAssembly()
.AddClasses(c => c.WithAttribute<TransientAttribute>())
.AsImplementedInterfaces()
.WithTransientLifetime()
.AddClasses(c => c.WithAttribute<ScopedAttribute>())
.AsImplementedInterfaces()
.WithScopedLifetime()
.AddClasses(c => c.WithAttribute<SingletonAttribute>())
.AsImplementedInterfaces()
.WithSingletonLifetime()
);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
// Note the two attributes [Transient] which ensures that the lifetime is set as transient
// but notice also [AutoGenerateInterface]. This attribute means that the ISomeThirdClass interface is generated automatically
// with all the public methods of SomeThirdClass specified. No need to write a single line of code if you want the interface
// and the class to match 1:1
[Transient]
[AutoGenerateInterface]
public class SomeThirdClass : ISomeThirdClass
{
public string Bar()
{
return "SomeThirdClass return value";
}
}
// Notice here that this is close to the class above, but the lifetime is Singleton, so the "Counter" variable keeps its state.
[Singleton]
[AutoGenerateInterface]
public class SomeOtherClass : ISomeOtherClass
{
public int Counter { get; set; } = 0;
public string Foo()
{
Counter += 1;
return $"SomeOtherClass counter value: {Counter}";
}
}
// Notice that here there's also a "AutoGenerateConstructor" attribute. This Source Generator reads all fields of the class
// and automatically generates a constructor based on the fields, that ASP.NET Core can use to inject the dependencies.
// You can also use this constructor to instantiate the class in e.g. tests.
// Notice that thi class also usees the auto-generated interfaces from the other classes and not the classes directly.
[AutoGenerateConstructor]
[AutoGenerateInterface]
[Transient]
public partial class SomeClass : ISomeClass
{
private ISomeOtherClass _someClass1;
private ISomeThirdClass _someClass2;
public string Test()
{
return _someClass1.Foo() + "\n" + _someClass2.Bar();
}
}
}
// Auto-generating constructors also work in the controller layer!
[AutoGenerateConstructor]
[ApiController]
[Route("/test")]
public partial class TestController : ControllerBase
{
private ISomeClass _someClass;
[HttpGet]
public ActionResult<string> Index()
{
return _someClass.Test();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment