-
Notifications
You must be signed in to change notification settings - Fork 368
/
AspNetCoreCSharpKernelExtensions.cs
133 lines (113 loc) · 5.88 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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// 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.Invocation;
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.DotNet.Interactive.Formatting;
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 = HttpClientFormatter.CreateEnhancedHttpClient(interactiveHost.Address, interactiveLoggerProvider);
await kernel.SetValueAsync<IApplicationBuilder>("App", interactiveHost.App).ConfigureAwait(false);
await kernel.SetValueAsync<IEndpointRouteBuilder>("Endpoints", interactiveHost.Endpoints).ConfigureAwait(false);
await kernel.SetValueAsync<HttpClient>("HttpClient", 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;
})
});
Formatter.Register<HttpResponseMessage>((responseMessage, context) =>
{
// Formatter.Register() doesn't support async formatters yet.
// Prevent SynchronizationContext-induced deadlocks given the following sync-over-async code.
ExecutionContext.SuppressFlow();
try
{
HttpClientFormatter.FormatHttpResponseMessage(
responseMessage,
context).Wait();
}
finally
{
ExecutionContext.RestoreFlow();
}
return true;
}, HtmlFormatter.MimeType);
return kernel;
}
}
}