Skip to content

Commit e742271

Browse files
committed
fix Global filter dependency injection NullReferenceException #209
1 parent a9c3bc5 commit e742271

File tree

4 files changed

+93
-24
lines changed

4 files changed

+93
-24
lines changed
Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,35 @@
11
using ConsoleAppFramework;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using System.Threading.Tasks.Dataflow;
24

3-
4-
args = "some-command hello --global-flag flag-value -- more args here".Split(" ");
5+
// args = "some-command hello --global-flag flag-value -- more args here".Split(" ");
56

67
var app = ConsoleApp.Create();
8+
app.ConfigureServices(services => services.AddSingleton<MyService>());
79

8-
app.ConfigureGlobalOptions((ref ConsoleApp.GlobalOptionsBuilder builder) =>
9-
{
10-
var flag = builder.AddGlobalOption<string>("--global-flag");
11-
return new GlobalOptions(flag);
12-
});
13-
14-
app.UseFilter<SomeFilter>();
15-
app.Add<Commands>();
16-
app.Run(args);
10+
app.UseFilter<MyFilter>();
11+
app.Run(["cmd", "test"]);
1712

18-
internal record GlobalOptions(string Flag);
13+
public class MyService
14+
{
15+
public void Test() => Console.WriteLine("Test");
16+
}
1917

20-
internal class Commands
18+
internal class MyFilter(ConsoleAppFilter next, MyService myService) : ConsoleAppFilter(next)
2119
{
22-
[Command("some-command")]
23-
public void SomeCommand([Argument] string commandArg, ConsoleAppContext context)
20+
public override async Task InvokeAsync(ConsoleAppContext context, CancellationToken cancellationToken)
2421
{
25-
Console.WriteLine($"ARG: {commandArg}");
26-
Console.WriteLine($"ESCAPED: {string.Join(", ", context.EscapedArguments.ToArray()!)}");
22+
myService.Test();
23+
await Next.InvokeAsync(context, cancellationToken);
2724
}
2825
}
2926

30-
internal class SomeFilter(ConsoleAppFilter next) : ConsoleAppFilter(next)
27+
[RegisterCommands("cmd")]
28+
public class MyCommand
3129
{
32-
public override async Task InvokeAsync(ConsoleAppContext context, CancellationToken cancellationToken)
30+
[Command("test")]
31+
public int Test()
3332
{
34-
Console.WriteLine($"FLAG: {((GlobalOptions)context.GlobalOptions!).Flag}");
35-
await Next.InvokeAsync(context, cancellationToken);
33+
return 1;
3634
}
3735
}

src/ConsoleAppFramework/ConsoleAppBaseCode.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,11 @@ internal class ConsoleAppFrameworkGeneratorOptionsAttribute : Attribute
199199
public bool DisableNamingConversion { get; set; }
200200
}
201201
202+
internal interface IFilterFactory
203+
{
204+
public ConsoleAppFilter BuildFilter();
205+
}
206+
202207
internal delegate object FuncGlobalOptionsBuilderObject(ref ConsoleApp.GlobalOptionsBuilder builder);
203208
204209
[UnconditionalSuppressMessage("Trimming", "IL2026")]
@@ -559,7 +564,7 @@ public ConsoleAppBuilder ConfigureGlobalOptions(FuncGlobalOptionsBuilderObject c
559564
return this;
560565
}
561566
562-
async Task RunWithFilterAsync(string commandName, string[] args, int commandDepth, ConsoleAppFilter invoker, CancellationToken cancellationToken)
567+
async Task RunWithFilterAsync(string commandName, string[] args, int commandDepth, IFilterFactory filterFactory, CancellationToken cancellationToken)
563568
{
564569
using var posixSignalHandler = PosixSignalHandler.Register(Timeout, cancellationToken);
565570
try
@@ -587,6 +592,7 @@ async Task RunWithFilterAsync(string commandName, string[] args, int commandDept
587592
await startHostTask;
588593
}
589594
595+
var invoker = filterFactory.BuildFilter();
590596
await Task.Run(() => invoker.InvokeAsync(context, posixSignalHandler.Token)).WaitAsync(posixSignalHandler.TimeoutToken);
591597
}
592598
catch (Exception ex)

src/ConsoleAppFramework/Emitter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ void EmitLeafCommand(CommandWithId? command)
660660
{
661661
var invokerArgument = commandArgs.TrimStart(',', ' ');
662662
invokerArgument = (invokerArgument != "") ? $"this, {invokerArgument}" : "this";
663-
var invokeCode = $"RunWithFilterAsync(\"{command.Command.Name}\", args, {depth}, new Command{command.Id}Invoker({invokerArgument}).BuildFilter(), cancellationToken)";
663+
var invokeCode = $"RunWithFilterAsync(\"{command.Command.Name}\", args, {depth}, new Command{command.Id}Invoker({invokerArgument}), cancellationToken)";
664664
if (!isRunAsync)
665665
{
666666
sb.AppendLine($"{invokeCode}.GetAwaiter().GetResult();");
@@ -681,7 +681,7 @@ void EmitFilterInvoker(CommandWithId command)
681681
if (needsCommand) commandType = $"{commandType} command";
682682
if (!string.IsNullOrEmpty(commandType)) commandType = ", " + commandType;
683683

684-
using (sb.BeginBlock($"sealed class Command{command.Id}Invoker(ConsoleAppBuilder builder{commandType}) : ConsoleAppFilter(null!)"))
684+
using (sb.BeginBlock($"sealed class Command{command.Id}Invoker(ConsoleAppBuilder builder{commandType}) : ConsoleAppFilter(null!), IFilterFactory"))
685685
{
686686
using (sb.BeginBlock($"public ConsoleAppFilter BuildFilter()"))
687687
{

tests/ConsoleAppFramework.GeneratorTests/DITest.cs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,69 @@ class MyClass(string name)
3838
}
3939
""", args: "--x 10 --y 20", expected: "foo:10:20");
4040
}
41+
42+
[Fact]
43+
public void WithFilter()
44+
{
45+
verifier.Execute("""
46+
var app = ConsoleApp.Create();
47+
app.UseFilter<MyFilter>();
48+
app.Run(["cmd", "test"]);
49+
50+
public class MyService
51+
{
52+
public void Test() => Console.Write("Test");
53+
}
54+
55+
internal class MyFilter(ConsoleAppFilter next, MyService myService) : ConsoleAppFilter(next)
56+
{
57+
public override async Task InvokeAsync(ConsoleAppContext context, CancellationToken cancellationToken)
58+
{
59+
myService.Test();
60+
await Next.InvokeAsync(context, cancellationToken);
61+
}
62+
}
63+
64+
[RegisterCommands("cmd")]
65+
public class MyCommand
66+
{
67+
[Command("test")]
68+
public int Test()
69+
{
70+
return 1;
71+
}
72+
}
73+
74+
class MiniDI : IServiceProvider
75+
{
76+
System.Collections.Generic.Dictionary<Type, object> dict = new();
77+
78+
public void Register(Type type, object instance)
79+
{
80+
dict[type] = instance;
81+
}
82+
83+
public object? GetService(Type serviceType)
84+
{
85+
return dict.TryGetValue(serviceType, out var instance) ? instance : null;
86+
}
87+
}
88+
89+
namespace ConsoleAppFramework
90+
{
91+
partial class ConsoleApp
92+
{
93+
partial class ConsoleAppBuilder
94+
{
95+
partial void BuildAndSetServiceProvider(ConsoleAppContext context)
96+
{
97+
var di = new MiniDI();
98+
di.Register(typeof(MyService), new MyService());
99+
ConsoleApp.ServiceProvider = di;
100+
}
101+
}
102+
}
103+
}
104+
""", "cmd test", "Test");
105+
}
41106
}

0 commit comments

Comments
 (0)