Cysharp / Cysharp/ConsoleAppFramework

Add support for [AsParameters] attribute

Open
#133 14 comments 11 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C#
Stars
2.2k
Forks
127
PR merge metrics
No merged PRs in 30d

Description

Hi! First off, congrats, this a pretty cool library and I'm currently using it (with joy!) in production. I was wondering if it would be possible to add support for something like the [AsParameters] attribute that was added to AspNetCore from .net 7.0, to augment Minimal APIs. In Asp this attribute allows for grouping all request parameters into one request class/record/struct.

AspNetCore Example:

struct TodoItemRequest
{
    public int Id { get; set; }
    public string Description { get; set; }
}

app.MapPost("/api/todoitem", async ([AsParameters] TodoItemRequest request) =>
    Console.Writeline($"You posted item with Id: {request.Id}, and Description: {request.Description}");

You can see more details here

The advantage of being able to do that, is that along with the [FromServices] attribute, you will be able to have for every command delegate only two parameters. One parameter for dependency injecting all the services, and one for grouping all the arguments.

Example of how it could work
//program.cs
var services = new ServiceCollection();
services.AddLogging();
services.AddTransient<TestCommandHandler>();

using var serviceProvider = services.BuildServiceProvider();
ConsoleApp.ServiceProvider = serviceProvider;

var app = ConsoleApp.Create();

app.Add("test", async ([FromServices] TestCommandHandler commandHandler, 
        [AsParameters] TestCommandArgs args) => await commandHandler.Handle(args));

app.Run(args);

//in another file...

public record TestCommandArgs([Argument]string Param1, int? Param2) : ICommandArgs;

public class TestCommandHandler(ILogger<TestCommandHandler> logger) : ICommandHandler<TestCommandArgs>
{
    public async Task Handle(TestCommandArgs args)
    {
        logger.LogInformation(args.Param1);
        if (args.Param2.HasValue)
        {
            logger.LogInformation(args.Param2.ToString());
        }
    }
}

In the example above TestCommandHandler works as vehicle for injecting all necessary service dependency through its constructor. And TestCommandArgs is the vehicle for grouping all command args that become parameters, into a single record.

As you see in the example the TestCommandHandler and TestCommandArgs also implement some interfaces, name ICommandHandler and ICommandArgs. That's because another advantage of as [AsParameters] attribute is that it allows you to create one standard interface for all your command delegate handlers. Which can increase code readability, code writing speed (through IDE auto-completion) and also maintenance speed when refactoring. Here's some example interfaces for the above:

public interface ICommandHandler<in TCommandArgs> where TCommandArgs: ICommandArgs
{
    public Task Handle(TCommandArgs args);
}
public interface ICommandArgs;

This is by no means a necessity, but it would be a pretty cool feature to add. Thanks!

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the program.cs example, especially app.Add and the [AsParameters] usage, and compare it with the linked ASP.NET Core parameter-binding behavior. Done should mean command arguments can be grouped into a single annotated record, while the handler receives that grouped value alongside the injected service.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
cli
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.