从零开始的 Blazor 项目 + nuget:"There was no runtime pack for Microsoft.AspNetCore.App available for the specified RuntimeIdentifier 'browser-wasm'"

Blazor proj from scratch+nuget: "There was no runtime pack for Microsoft.AspNetCore.App available for the specified RuntimeIdentifier 'browser-wasm'"

[最终更新 07.12.2020 23:50 --> 给所有寻求答案的人]

您可能正在添加一些 nuget 包,该包在字段 DEPENDENCIES .NET Core 3.1 中具有。例如 Serilog.AspNetCore 3.4.0。在这个例子中,您可以包含 Serilog.AspNetCore 3.2.0,因为它在依赖项中只有 .NetStandard 2.0。在@Ogglas 答案中查看更多解释。

地球上没有足够的 space 来问唯一的问题:“为什么,为什么是 Microsoft?”:) 该消息没有明确地将问题与解决方案相关联 - 这是最轻松的委婉说法我有能力做:)

对我来说,Blazor 非常有前途,但它仍然是测试版。不仅对我而言。 startblazoring 的家伙声称,Blazor 有时会变幻无常。我完全同意;)我一直为此祈祷。但我想只有在 .NET 6.0 之后它才会是一个“现成的产品”。

[原题]

这太令人沮丧了:)

我讨厌错误,当我不能简单地交互并理解错误的来源时。我知道,我很无知,如果我多关注“blazor 应用程序是如何创建的”,我眨眼就会明白。

但现在只是令人沮丧。什么是?错误信息:

Severity Code Description Project File Line Suppression State Error NETSDK1082 There was no runtime pack for Microsoft.AspNetCore.App available for the specified RuntimeIdentifier 'browser-wasm'. TestBlazorAppNoRuntimeError.Client C:\Program Files\dotnet\sdk.0.100\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.FrameworkReferenceResolution.targets 387

真的,你能在这里解决什么问题?:D 嗯嗯。我知道 .NET 5 是一个“新事物”。我知道 Blazor 是一个“新事物”但真的吗?真的很容易“破解”;)(用一条什么都不说的消息)

那我做了什么?

我是否创建了包含数十万个文件的非常复杂的项目?

我是不是添加了几十万个nuget包。

只需创建 Blazor Webassembly 应用程序并将一个 nuget 包添加到共享(公共)库(仅供参考:IdentityServer4 包)。就这些了。如果您现在愿意,请多多称赞,因为这个软件包专门导致了这个问题(因为我只是错误地发现了):

There was no runtime pack for Microsoft.AspNetCore.App available for the specified RuntimeIdentifier 'browser-wasm'.

真的,这里有很大的相关性:错误消息和正在添加的包之间(如果有人错过了,这是讽刺:))

因此,如果你想重现它,请创建“Blazor Webassembly”:

我认为选择的选项不会产生影响。但如果是这种情况 - 你可以看到我选择了什么选项。然后只需将 IdentityServer4 nuget 包添加到共享库:

https://www.nuget.org/packages/IdentityServer4/

就这些了

如果你太懒了 - 下载这个项目并自己检查一下:

https://ufile.io/751l5wgq

我看到了数百个关于此消息的 SOF 主题:

There was no runtime pack for Microsoft.AspNetCore.App available for the specified RuntimeIdentifier 'browser-wasm'.

但是其中 none 很容易重现。

我为什么要创建这个主题?

因为我确实尝试实现(通过复制粘贴到我的解决方案)人们在此处的 Blazor 样板中创建的内容: https://github.com/enkodellc/blazorboilerplate

一开始,我错误地添加了 Identity4 NuGet 而不是 Identity4.storage,这就是为什么我确实观察到这种行为(这种错误)并将此包与此错误相关联的原因。当我从 Identity4 回滚到 Identity4.storage 时,一切都再次正确。但在那之后我从这个存储库中做了很多代码传输。一旦我结束,现在,一切都在编译,但我又一次拥有:

There was no runtime pack for Microsoft.AspNetCore.App available for the specified RuntimeIdentifier 'browser-wasm'.

这让我发疯。也许你会有一些想法,如何找出是什么导致了这个错误(与之前 Identity4 nuget 包导致它的原因完全相同)

[2020 年 12 月 6 日更新 12:30]

这里的问题更多:“为什么会发生以及如何找到导致问题的包”我知道我可以猜测是什么导致出现此消息。

我做了一点调查。此目标中显示此错误:

<ResolveRuntimePackAssets FrameworkReferences="@(FrameworkReference)"
                          ResolvedRuntimePacks="@(ResolvedRuntimePack)"
                          UnavailableRuntimePacks="@(UnavailableRuntimePack)"
                          SatelliteResourceLanguages="$(SatelliteResourceLanguages)"
                          DesignTimeBuild="$(DesignTimeBuild)">
  <Output TaskParameter="RuntimePackAssets" ItemName="RuntimePackAsset" />
</ResolveRuntimePackAssets>

<ItemGroup>
  <ReferenceCopyLocalPaths Include="@(RuntimePackAsset)"
                           Condition="'$(CopyLocalLockFileAssemblies)' == 'true' and ('$(SelfContained)' == 'true' or '%(RuntimePackAsset.RuntimePackAlwaysCopyLocal)' == 'true')" />
</ItemGroup>

所以我做了 google ResolveRuntimePackAssets 并找到了这个 repo 和这个文件:

https://github.com/dotnet/sdk/blob/release/5.0.2xx/src/Tasks/Microsoft.NET.Build.Tasks/ResolveRuntimePackAssets.cs 这样的代码:

foreach (var unavailableRuntimePack in UnavailableRuntimePacks)
{
    if (frameworkReferenceNames.Contains(unavailableRuntimePack.ItemSpec))
    {
        //  This is a runtime pack that should be used, but wasn't available for the specified RuntimeIdentifier
        //  NETSDK1082: There was no runtime pack for {0} available for the specified RuntimeIdentifier '{1}'.
        Log.LogError(Strings.NoRuntimePackAvailable, unavailableRuntimePack.ItemSpec,
            unavailableRuntimePack.GetMetadata(MetadataKeys.RuntimeIdentifier));
    }
}

看起来是“导致”问题的代码(这需要在消息中写下以下信息:unavailableRuntimePack.ItemSpec - 这样会容易得多;))

所以现在唯一的事情就是找到填充这个的东西:UnavailableRuntimePacks 属性。我没有看到任何代码这样做,所以它必须是一些反思。我找到了可能正在执行此操作的代码:

https://github.com/dotnet/sdk/blob/release/5.0.2xx/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs

还有这个方法:

private void ProcessRuntimeIdentifier(
    string runtimeIdentifier,
    KnownRuntimePack selectedRuntimePack,
    string runtimePackVersion,
    List<string> additionalFrameworkReferencesForRuntimePack,
    HashSet<string> unrecognizedRuntimeIdentifiers,
    List<ITaskItem> unavailableRuntimePacks,
    List<ITaskItem> runtimePacks,
    List<ITaskItem> packagesToDownload,
    string isTrimmable,
    bool addToPackageDownload)
{
    var runtimeGraph = new RuntimeGraphCache(this).GetRuntimeGraph(RuntimeGraphPath);
    var knownFrameworkReferenceRuntimePackRuntimeIdentifiers = selectedRuntimePack.RuntimePackRuntimeIdentifiers.Split(';');

    string runtimePackRuntimeIdentifier = NuGetUtils.GetBestMatchingRid(
            runtimeGraph,
            runtimeIdentifier,
            knownFrameworkReferenceRuntimePackRuntimeIdentifiers,
            out bool wasInGraph);

    if (runtimePackRuntimeIdentifier == null)
    {
        if (wasInGraph)
        {
            //  Report this as an error later, if necessary.  This is because we try to download
            //  all available runtime packs in case there is a transitive reference to a shared
            //  framework we don't directly reference.  But we don't want to immediately error out
            //  here if a runtime pack that we might not need to reference isn't available for the
            //  targeted RID (e.g. Microsoft.WindowsDesktop.App for a linux RID).
            var unavailableRuntimePack = new TaskItem(selectedRuntimePack.Name);
            unavailableRuntimePack.SetMetadata(MetadataKeys.RuntimeIdentifier, runtimeIdentifier);
            unavailableRuntimePacks.Add(unavailableRuntimePack);
        }
        else if (!unrecognizedRuntimeIdentifiers.Contains(runtimeIdentifier))
        {
            //  NETSDK1083: The specified RuntimeIdentifier '{0}' is not recognized.
            Log.LogError(Strings.RuntimeIdentifierNotRecognized, runtimeIdentifier);
            unrecognizedRuntimeIdentifiers.Add(runtimeIdentifier);
        }
    }
    else if (addToPackageDownload)
    {
        foreach (var runtimePackNamePattern in selectedRuntimePack.RuntimePackNamePatterns.Split(';'))
        {
            string runtimePackName = runtimePackNamePattern.Replace("**RID**", runtimePackRuntimeIdentifier);

            if (runtimePacks != null)
            {
                TaskItem runtimePackItem = new TaskItem(runtimePackName);
                runtimePackItem.SetMetadata(MetadataKeys.NuGetPackageId, runtimePackName);
                runtimePackItem.SetMetadata(MetadataKeys.NuGetPackageVersion, runtimePackVersion);
                runtimePackItem.SetMetadata(MetadataKeys.FrameworkName, selectedRuntimePack.Name);
                runtimePackItem.SetMetadata(MetadataKeys.RuntimeIdentifier, runtimePackRuntimeIdentifier);
                runtimePackItem.SetMetadata(MetadataKeys.IsTrimmable, isTrimmable);

                if (selectedRuntimePack.RuntimePackAlwaysCopyLocal)
                {
                    runtimePackItem.SetMetadata(MetadataKeys.RuntimePackAlwaysCopyLocal, "true");
                }

                if (additionalFrameworkReferencesForRuntimePack != null)
                {
                    runtimePackItem.SetMetadata(MetadataKeys.AdditionalFrameworkReferences, string.Join(";", additionalFrameworkReferencesForRuntimePack));
                }

                runtimePacks.Add(runtimePackItem);
            }

            TaskItem packageToDownload = new TaskItem(runtimePackName);
            packageToDownload.SetMetadata(MetadataKeys.Version, runtimePackVersion);

            packagesToDownload.Add(packageToDownload);
        }
    }
}

经过分析,Identity4(例如)导致此问题的原因是因为 runtimePackRuntimeIdentifier 为 null 而 wasInGraph 为 true :) 但是这意味着什么,我们必须进一步挖掘。这里的关键是方法 GetBestMatchingRid:

public static string GetBestMatchingRid(RuntimeGraph runtimeGraph, string runtimeIdentifier,
    IEnumerable<string> availableRuntimeIdentifiers, out bool wasInGraph)
{
    wasInGraph = runtimeGraph.Runtimes.ContainsKey(runtimeIdentifier);

    HashSet<string> availableRids = new HashSet<string>(availableRuntimeIdentifiers);
    foreach (var candidateRuntimeIdentifier in runtimeGraph.ExpandRuntime(runtimeIdentifier))
    {
        if (availableRids.Contains(candidateRuntimeIdentifier))
        {
            return candidateRuntimeIdentifier;
        }
    }

    //  No compatible RID found in availableRuntimeIdentifiers
    return null;
}

所以在这里我们看到 runtimePackRuntimeIdentifier 是空的,因为它没有在 availableRuntimeIdentifiers 上找到。这个变量定义为:

var knownFrameworkReferenceRuntimePackRuntimeIdentifiers = selectedRuntimePack.RuntimePackRuntimeIdentifiers.Split(';');

那么让我们看看它是如何定义的(在下面的代码中变量 runtimePackForRuntimeIDProcessing 是上面提到的 selectedRuntimePack

KnownRuntimePack runtimePackForRuntimeIDProcessing;
if (knownFrameworkReference.Name.Equals(knownFrameworkReference.RuntimeFrameworkName, StringComparison.OrdinalIgnoreCase))
{
    //  Only add runtime packs where the framework reference name matches the RuntimeFrameworkName
    //  Framework references for "profiles" will use the runtime pack from the corresponding non-profile framework
    runtimePackForRuntimeIDProcessing = selectedRuntimePack.Value;
    includeInPackageDownload = true;
}
else if (!knownFrameworkReference.RuntimePackRuntimeIdentifiers.Equals(selectedRuntimePack?.RuntimePackRuntimeIdentifiers))
{
    // If the profile has a different set of runtime identifiers than the runtime pack, use the profile.
    runtimePackForRuntimeIDProcessing = knownFrameworkReference.ToKnownRuntimePack();
    includeInPackageDownload = true;
}
else
{
    // For the remaining profiles, don't include them in package download but add them to unavaliable if necessary.
    runtimePackForRuntimeIDProcessing = knownFrameworkReference.ToKnownRuntimePack();
    includeInPackageDownload = false;
}

但这是我所能做到的。如果不进一步调查,我不明白这个 selectedRuntimePack.RuntimePackRuntimeIdentifiers 应该如何定义。这是这里的关键。显然由于某些原因 IdentityServer4 不属于 selectedRuntimePack.RuntimePackRuntimeIdentifiers,这就是我正在寻找的答案:)

IdentityServer4 4.1.1(最新版本)依赖于.NETCoreApp 3.1

https://www.nuget.org/packages/IdentityServer4/

如果您使用 .NET Core 3.1 创建 Blazor WebAssembly App,它将在 Server 应用程序中运行。

然而,当使用 .NET Core 3.1 时,ClientShared 都使用 .NET Standard 2.1,因此不兼容。这些错误消息虽然很清楚:

Error Package restore failed. Rolling back package changes for 'BlazorApp.Shared'.

Error NU1202 Package IdentityServer4 4.1.1 is not compatible with netstandard2.1 (.NETStandard,Version=v2.1). Package IdentityServer4 4.1.1 supports: netcoreapp3.1 (.NETCoreApp,Version=v3.1) BlazorApp.Shared

正如我在此 link 中提到的,似乎存在与以下软件包有关的问题。因此,从您的项目和您的 Blazor 项目引用的所有其他项目中删除包(右键单击项目,选择编辑项目文件,然后删除以下内容):

<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.0" />

在我的例子中(希望在你的例子中也是如此)这个包的唯一用途是使用字段成员 JwtBearerDefaults.AuthenticationScheme 获取常量字符串 "Bearer"。因此,与其简单地使用 "Bearer" string.