/
AspNetCoreCSharpKernelExtensions.cs
116 lines (98 loc) · 4.98 KB
/
AspNetCoreCSharpKernelExtensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Threading;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.DotNet.Interactive.Commands;
using Microsoft.DotNet.Interactive.CSharp;
using Microsoft.Extensions.Hosting;
namespace Microsoft.DotNet.Interactive.AspNetCore;
public static class AspNetCoreCSharpKernelExtensions
{
private static readonly Assembly[] _references =
{
typeof(Host).Assembly, // Microsoft.Extensions.Hosting
typeof(WebHost).Assembly, // Microsoft.AspNetCore
typeof(Controller).Assembly, // Microsoft.AspNetCore.Mvc.ViewFeatures
typeof(DeveloperExceptionPageMiddleware).Assembly, // Microsoft.AspNetCore.Diagnostics
typeof(AspNetCoreCSharpKernelExtensions).Assembly, // Microsoft.DotNet.Interactive.AspNetCore
};
private static readonly string[] _namespaces =
{
typeof(HttpContext).Namespace, // Microsoft.AspNetCore.Http
typeof(IEndpointRouteBuilder).Namespace, // Microsoft.AspNetCore.Routing
typeof(EndpointRouteBuilderExtensions).Namespace, // Microsoft.AspNetCore.Builder
typeof(InteractiveEndpointRouteBuilderExtensions).Namespace, // Microsoft.DotNet.Interactive.AspNetCore
typeof(HttpClient).Namespace, // System.Net.Htttp
};
public static CSharpKernel UseAspNetCore(this CSharpKernel kernel)
{
InteractiveHost interactiveHost = null;
var directive = new Command("#!aspnet", "Activate ASP.NET Core")
{
Handler = CommandHandler.Create(async () =>
{
if (interactiveHost is {})
{
return;
}
var interactiveLoggerProvider = new InteractiveLoggerProvider();
kernel.AddMiddleware(async (command, context, next) =>
{
// REVIEW: Is there a way to log even when there's no command in progress?
// This is currently necessary because the #!log command uses KernelInvocationContext.Current in
// its LogEvents.Subscribe callback and KernelInvocationContext.Current is backed by an AsyncLocal.
// Is there a way to log to diagnostic output without KernelInvocationContext.Current?
using (command is SubmitCode ? interactiveLoggerProvider.SubscribePocketLogerWithCurrentEC() : null)
{
await next(command, context).ConfigureAwait(false);
}
});
// The middleware doesn't cover the current command's executions so we need this to capture startup logs.
using (interactiveLoggerProvider.SubscribePocketLogerWithCurrentEC())
{
// We could try to manage the host's lifetime, but for now just stop the kernel if you want to stop the host.
interactiveHost = new InteractiveHost(interactiveLoggerProvider);
var startHostTask = interactiveHost.StartAsync();
var rDirectives = string.Join(Environment.NewLine, _references.Select(a => $"#r \"{a.Location}\""));
var usings = string.Join(Environment.NewLine, _namespaces.Select(ns => $"using {ns};"));
await kernel.SendAsync(new SubmitCode($"{rDirectives}{Environment.NewLine}{usings}"), CancellationToken.None).ConfigureAwait(false);
await startHostTask.ConfigureAwait(false);
var httpClient = EnhancedHttpClient.Create(interactiveHost.Address, interactiveLoggerProvider);
await kernel.SetValueAsync("App", interactiveHost.App, typeof(IApplicationBuilder)).ConfigureAwait(false);
await kernel.SetValueAsync("Endpoints", interactiveHost.Endpoints, typeof(IEndpointRouteBuilder)).ConfigureAwait(false);
await kernel.SetValueAsync("HttpClient", httpClient, typeof(HttpClient)).ConfigureAwait(false);
}
})
};
kernel.AddDirective(directive);
kernel.AddDirective(new Command("#!aspnet-stop", "Stop ASP.NET Core host")
{
Handler = CommandHandler.Create(async () =>
{
if (interactiveHost is null)
{
return;
}
await interactiveHost.DisposeAsync();
interactiveHost = null;
})
});
kernel.RegisterForDisposal(() =>
{
interactiveHost?.Dispose();
interactiveHost = null;
});
return kernel;
}
}