transferred from dotnet/aspnetcore#25844
When using the preview of AspNetCore Blazor WebAssembly and attempting to utilize the Microsoft.PowerShell.SDK package, Blazor WebAssembly is unable to load the System.Management.Automation.dll
Microsoft.PowerShell.SDK package referencePowerShell host Example repoit works
Exceptions:
Could not load file or assembly 'System.Management.Automation, Version=7.0.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies.
Could you test with Microsoft.PowerShell.SDK 7.1 RC1?
@iSazonov In april, I put a demo on twitter of a working prototype on Mono.Wasm/PowerShell 7 hosted on Github Pages.
I will submit a working PR soon for net5.0 RC1, but there is a lot of subjects like fileless (on startup, help system, module), trimming, preprocessor, async, sdk etc... Wasm is an embedded stack so most of the time, it's not a question, it's a behaviour : the minimal path/size/features.
For example, the PowerShell master repo is not well compatible with this kind of restriction : <ThreadPoolMaxThreads>1</ThreadPoolMaxThreads>, so the HttpClient is unusable from the API which is async only.
You can try on my previous github page demo :
if (-not $task) {
$task = [Net.Http.HttpClient]::new().GetStringAsync("https://gist.githubusercontent.com/IISResetMe/bcbee5f504c25b166003/raw/4ad303f09088ef38aa363863a93c33969080f6ae/Get-AST.ps1")
$task.Wait()
}
$task.Result
# 1st Execution : Exception calling "Wait" with "0" argument(s): "Cannot wait on monitors on this runtime."
# 2nd execution : OK
I will use this issue as a home to publish technical informations and read community's suggestions.
guard the call :
private static string GetLoggingDirectory()
{
//...
if (!OperatingSystem.IsBrowser())
{
string exePath = Process.GetCurrentProcess().MainModule.FileName;
string folder = Path.GetDirectoryName(exePath);
return Path.Combine(folder, "Logging");
}
else
{
return string.Empty;
}
}
mark the member as being unsupported :
[UnsupportedOSPlatform("browser")]
private static string GetLoggingDirectory()
{
//...
string exePath = Process.GetCurrentProcess().MainModule.FileName;
string folder = Path.GetDirectoryName(exePath);
return Path.Combine(folder, "Logging");
}
You have to explicitly indicate that you intend to support your project in Blazor Web Assembly by adding the
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<SupportedPlatform Include="browser" />
</ItemGroup>
</Project>
Entire assemblies :
Partial / Limited / Restrictions :
Source :
MSBuild Property Name | Default Value | Type | Description
-|-|-|-
PublishTrimmed | false | bool | Enable/Disable Trimming
TrimMode | CopyUsed | enum:CopyUsed/Link | CopyUsed: Assembly trimming, Link: Member trimming
SuppressTrimAnalysisWarnings | true| bool | Suppress trim analysis warnings
ILLinkWarningLevel | 999 | int | ILLink Warning Level
ILLinkTreatWarningsAsErrors | false | bool | ILLink treat warnings as errors
MSBuild Property Name | Description | Trimmed when | Blazor Specific
-- | -- | -- | --
DebuggerSupport | Any dependency that enables better debugging experience | False | No
EnableUnsafeUTF7Encoding | Insecure UTF-7 encoding | False | No
EnableUnsafeBinaryFormatterSerialization | BinaryFormatter serialization | False | No
EventSourceSupport | Any EventSource related code or logic | False | No
InvariantGlobalization | All globalization specific code and data | True | No
UseSystemResourceKeys| Any localized resources for system assemblies, such as error messages | True | No
HttpActivityPropagationSupport | Any dependency related to diagnostics support for System.Net.Http | False | No
TrimmerRemoveSymbols | Remove Symbols | False | No
BlazorEnableTimeZoneSupport | Remove TimeZone Support | False | Yes
MSBuild Property Name | AppContext Setting
-- | --
DebuggerSupport | System.Diagnostics.Debugger.IsSupported
EnableUnsafeUTF7Encoding | System.Text.Encoding.EnableUnsafeUTF7Encoding
EnableUnsafeBinaryFormatterSerialization | System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization
EventSourceSupport | System.Diagnostics.Tracing.EventSource.IsSupported
InvariantGlobalization | System.Globalization.Invariant
UseSystemResourceKeys | System.Resources.UseSystemResourceKeys
HttpActivityPropagationSupport | System.Net.Http.EnableActivityPropagation
BlazorEnableTimeZoneSupport | ?
source :
@SteveL-MSFT I will need some advices/directions about LogProvider. It's maybe a good candidat for your plugin system ?
With the new features in msbuild, we could add an external logger library, mark the consumer with UnsupportedOSPlatform and trim the library out to the other platforms when publish.( I will ask again for Android on PWSH7.2/net6.0 timeline, logprovider is a wall in the boot startup)
Temporary Workaround
internal class PSSysLogProvider : LogProvider
{
private static bool isBrowser;
private static SysLogProvider s_provider;
static PSSysLogProvider()
{
if (OperatingSystem.IsBrowser())
{
isBrowser = true;
}
else
{
s_provider = new SysLogProvider(PowerShellConfig.Instance.GetSysLogIdentity(),
PowerShellConfig.Instance.GetLogLevel(),
PowerShellConfig.Instance.GetLogKeywords(),
PowerShellConfig.Instance.GetLogChannels());
}
}
internal void WriteEvent(PSEventId id, PSChannel channel, PSOpcode opcode, PSTask task, LogContext logContext, string payLoad)
{
if (isBrowser)
{
Console.WriteLine($"Id: {id}\n" +
$"Channel: {channel}\n" +
$"Task: {task}\n" +
$"OpCode: {opcode}\n" +
$"Severity: {GetPSLevelFromSeverity(logContext.Severity)}\n" +
$"Context: \n{LogContextToString(logContext)}" +
$"PayLoad: {payLoad}" +
$"UserData: {GetPSLogUserData(logContext.ExecutionContext)}\n");
}
else
{
s_provider.Log(id, channel, task, opcode, GetPSLevelFromSeverity(logContext.Severity), DefaultKeywords,
LogContextToString(logContext),
GetPSLogUserData(logContext.ExecutionContext),
payLoad);
}
}
}
I will need some advices/directions about LogProvider. It's maybe a good candidat for your plugin system ?
We could think about ILogger
We could think about ILogger
With a dependency injection system or by adding an internal static property Provider to PSEtwLog ?
The problem is more about this area than ILogger. Logger is the second thing in a DI after Configuration, so it's maybe an interesting point for a plugin system in SMA.
@iSazonov About trimming, I know it's an area than interest you, I need a script to benchmark size/performance/GC of each MSBuild Property (there is too much properties, I need raw data to choose). Do you know if something already exists ? I will have a look of DotNetBenchmark.
With a dependency injection system or by adding an internal static property Provider to PSEtwLog ?
We have no need to use DI. I guess it will be simple custom implementation of the ILogger.
Existing PowerShell logging/tracing looks very expensive. With migrating to ILogger PowerShell would get better performance. It also fits well with the idea of PowerShell _subsystems_ that MSFT team is developing.
Do you know if something already exists ? I will have a look of DotNetBenchmark.
After reading how works this plugin system DotNetCorePlugins, I'm afraid that the compatibility with Wasm will be an extra cost to implement.
FI, this is a sample for lazy load assemblies in wasm (through the router and url browsing).
@inject Microsoft.AspNetCore.Components.WebAssembly.Services.LazyAssemblyLoader assemblyLoader
<Router AppAssembly="@typeof(Program).Assembly" AdditionalAssemblies="@lazyLoadedAssemblies" OnNavigateAsync="@OnNavigateAsync">...</Router>
@code {
private List<Assembly> lazyLoadedAssemblies = new List<Assembly>();
private async Task OnNavigateAsync(NavigationContext args)
{
if (args.Path.EndsWith("/robot"))
{
LazyLoadedAssemblies.AddRange(await assemblyLoader.LoadAssembliesAsync(new List<string>() { "robot.dll" }));
}
}
}
source : Lazy load assemblies in ASP.NET Core Blazor WebAssembly
About ILogger, I will continue for Blazor to use Console.WriteLine inside SMA Logger code until the new logger will be available. There is only one destination to ILogger and Console : the browser console, the difference is only about formatting, so it doesn't matter now.
For browser scenario, it seems that logging should just be disabled as it's not obvious to me where the logs would go to be something you can inspect later. Sending it to the console (which presumably would show up in output) might be ok for development purposes, but not general usage.
That just tells me that we need to have a pluggable logging destination, then. It not being obvious is a reason to leave it to the person implementing it in nonstandard scenarios, not disabling it entirely.
On logging, here's the "best practice" apparently: https://docs.microsoft.com/en-us/aspnet/core/blazor/fundamentals/logging?view=aspnetcore-3.1#blazor-webassembly
Learn about logging in Blazor apps, including log level configuration and how to write log messages from Razor components.
Microsoft.Extensions.* are the librairies from aspnetcore team, but starting net5.0, they are splitted into runtime and aspnetcore
dimportant update on dotnet/extensions
We need a layer with ILogger, and a way to specify an already ILogger to SMA in App Mode (through InitialSessionState ?).
The only one logger extension for Blazor is BlazorExtensions.Logging. It's an interesting Logger because it implements the JS API Console.Table.
https://github.com/BlazorExtensions/Logging
@SteveL-MSFT AspNetCore use the logger to write error in the console (developper tools) like Javascript. To debug the C# code, we use the Browser debugger, it's very weird, you trace C# powershell source inside the browser tools. More informations here : Blazor Client Side Debugging
GitHub
Microsoft Extension Logging implementation for Blazor - BlazorExtensions/Logging
Working :
$PSVersionTable | Out-String
Get-ChildItem -Path / -Recurse | Out-String
Get-ChildItem env: | Out-String
Get-Variable | Out-String
Get-Module -ListAvailable | Out-String
Get-Command | Out-String
$PSVersionTable | ConvertTo-Json | Out-String
ConvertFrom-Markdown -InputObject "test" | Out-String
Get-Module | ConvertTo-Html -Fragment | Out-String
Get-Help -Name Get-Command | Out-String
Not Working : Update Help + Web Cmdlets + More
# click two times on the Run as a workaround. Need 2 invocations to handle the async task.
if (-not $task) {
$task = [Net.Http.HttpClient]::new().GetStringAsync("https://gist.githubusercontent.com/IISResetMe/bcbee5f504c25b166003/raw/4ad303f09088ef38aa363863a93c33969080f6ae/Get-AST.ps1")
}
try {
$task.Wait() # Cannot wait on monitors on this runtime
}
catch {
$_ | Out-String
}
$task.Result | Out-String
Most helpful comment
Microsoft.Extensions.* are the librairies from aspnetcore team, but starting net5.0, they are splitted into runtime and aspnetcore
dimportant update on dotnet/extensions
We need a layer with ILogger, and a way to specify an already ILogger to SMA in App Mode (through InitialSessionState ?).
The only one logger extension for Blazor is BlazorExtensions.Logging. It's an interesting Logger because it implements the JS API Console.Table.
https://github.com/BlazorExtensions/Logging
@SteveL-MSFT AspNetCore use the logger to write error in the console (developper tools) like Javascript. To debug the C# code, we use the Browser debugger, it's very weird, you trace C# powershell source inside the browser tools. More informations here : Blazor Client Side Debugging