bit Source Generators
Less code to write, read and pay for
We care about how much code it takes to build an app. Every line a developer or an AI assistant writes has to be read, reviewed and kept working,
and with AI every line is also tokens you pay for. bit Source Generators write the repetitive code while your app compiles,
so you and your assistant only write what's specific to your app.
dotnet add package Bit.SourceGenerators
Why less code matters
- Lower AI costs. An assistant reads and writes fewer lines, so it uses fewer tokens, finishes sooner and has less room to get things wrong.
- Faster reviews. What's left to review is the code that matters.
- Never out of date. Generated code follows your declarations on every build, so an API client or a constructor can't drift from what it serves.
- Faster apps. Generated code does at compile time what would otherwise take reflection at runtime.
API clients, written for you
In a bit Boilerplate project, an API is declared once, as an interface the server and the client share:[Route("api/[controller]/[action]/")]
public interface IProductController : IAppController
{
[HttpGet("{id}")]
Task<ProductDto> Get(Guid id, CancellationToken cancellationToken);
[HttpPost]
Task<ProductDto> Create(ProductDto dto, CancellationToken cancellationToken);
}services.AddTypedHttpClients() registers them all, and the client injects the interface, here with [AutoInject] from the next section, and calls the API like a local service:
[AutoInject] private IProductController productController = default!; var product = await productController.Get(id, CurrentCancellationToken);
- Route and query parameters, JSON bodies and streamed
IAsyncEnumerableresponses all work. - While a page prerenders, the response the server already fetched is handed to the browser, so the page doesn't ask twice.
- A route template that isn't valid fails the build with
BITGEN003, rather than a request at runtime.
[AutoInject]
Instead of a constructor that lists every service and assigns each one, mark the fields:public partial class OrderService
{
[AutoInject] private IOrderController orderController = default!;
[AutoInject] private ILogger<OrderService> logger = default!;
}[Inject] property behind each field, so a component injects services the same way.
The class has to be partial, and BITGEN001 says so when it isn't.
The attribute is your project's own, in the
Microsoft.Extensions.DependencyInjection namespace:
namespace Microsoft.Extensions.DependencyInjection;
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
public sealed class AutoInjectAttribute : Attribute
{
}Fast Blazor parameters
Blazor sets a component's parameters through reflection. For everypartial component with [Parameter] or [CascadingParameter] properties,
the generator writes a SetParametersAsync that assigns each one directly instead:
public override Task SetParametersAsync(ParameterView parameters)
{
var __hasUnknownParameters = false;
foreach (var parameter in parameters)
{
switch (parameter.Name)
{
case nameof(Title):
Title = (string)parameter.Value!;
break;
default:
__hasUnknownParameters = true;
break;
}
}
return base.SetParametersAsync(__hasUnknownParameters ? parameters : ParameterView.Empty);
}partial, and components render faster, most of all in the browser, where reflection costs the most.
A component that declares its own SetParametersAsync keeps it.