簡體   English   中英

Microsoft.Extensions.DependencyInjection:DI 代碼在 do.net CLI / Rider 默認 .NET 配置文件上運行良好,但崩潰 VS2022

[英]Microsoft.Extensions.DependencyInjection: DI'd code runs fine on dotnet CLI / Rider default .NET profile, but crashes VS2022

許多用戶報告了在我們的一個 OSS 存儲庫中運行一個非常基本的示例項目的錯誤: https://github.com/akkado.net/Akka.Hosting/issues/179

System.TypeLoadException異常

示例代碼如下所示:

public interface IReplyGenerator
{
    string Reply(object input);
}

public class DefaultReplyGenerator : IReplyGenerator
{
    public string Reply(object input)
    {
        return input.ToString()!;
    }
}

public class EchoActor : ReceiveActor
{
    private readonly string _entityId;
    private readonly IReplyGenerator _replyGenerator;
    public EchoActor(string entityId, IReplyGenerator replyGenerator)
    {
        _entityId = entityId;
        _replyGenerator = replyGenerator;
        ReceiveAny(message => {
            Sender.Tell($"{Self} rcv {_replyGenerator.Reply(message)}");
        });
    }
}

public class Program
{
    private const int NumberOfShards = 5;
    
    private static Option<(string, object)> ExtractEntityId(object message)
        => message switch {
            string id => (id, id),
            _ => Option<(string, object)>.None
        };

    private static string? ExtractShardId(object message)
        => message switch {
            string id => (id.GetHashCode() % NumberOfShards).ToString(),
            _ => null
        };

    public static void Main(params string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.Services.AddScoped<IReplyGenerator, DefaultReplyGenerator>();
        builder.Services.AddAkka("MyActorSystem", configurationBuilder =>
        {
            configurationBuilder
                .WithRemoting(hostname: "localhost", port: 8110)
                .WithClustering(new ClusterOptions{SeedNodes = new []{ "akka.tcp://MyActorSystem@localhost:8110", }})
                .WithShardRegion<Echo>(
                    typeName: "myRegion",
                    entityPropsFactory: (_, _, resolver) =>
                    {
                        return s => resolver.Props<EchoActor>(s);
                    },
                    extractEntityId: ExtractEntityId,
                    extractShardId: ExtractShardId,
                    shardOptions: new ShardOptions());
        });

        var app = builder.Build();

        app.MapGet("/", async (HttpContext context, IRequiredActor<Echo> echoActor) =>
        {
            var echo = echoActor.ActorRef;
            var body = await echo.Ask<string>(
                    message: context.TraceIdentifier, 
                    cancellationToken: context.RequestAborted)
                .ConfigureAwait(false);
            await context.Response.WriteAsync(body);
        });

        app.Run();    
    }
}

觸發失敗的關鍵行(只是有時:)在這里:

 entityPropsFactory: (_, _, resolver) =>
                    {
                        return s => resolver.Props<EchoActor>(s);
                    },

resolver.Props<TActor>方法是Akka.DependencyInjection的一部分,它在幕后執行以下操作(因此您可以確切地看到 Microsoft.Extensions.DependencyInjection 是如何被調用的:)

internal class ServiceProviderActorProducer : IIndirectActorProducer
    {
        private readonly IServiceProvider _provider;
        private readonly object[] _args;

        public ServiceProviderActorProducer(IServiceProvider provider, Type actorType, object[] args)
        {
            _provider = provider;
            _args = args;
            ActorType = actorType;
        }

        public ActorBase Produce()
        {
            return (ActorBase)ActivatorUtilities.CreateInstance(_provider, ActorType, _args);
        }

        public Type ActorType { get; }

        public void Release(ActorBase actor)
        {
            // no-op
        }
    }

您可以在此處查看上述代碼的當前 Akka.NET 版本

錯誤消息聽起來好像ActivatorUtilities class 正在嘗試使用定義的類型沒有的 0 參數構造函數 - 因此出現錯誤。

奇怪的是:通過do.net run或 Rider 2022.3 的默認“.NET 配置文件”啟動示例應用程序時,我們沒有看到此錯誤 該錯誤僅在 VS2022 中運行或使用 Rider 2022.3 的“.NET 啟動配置文件”時發生,這在某種程度上有所不同。 您可以在此處查看我們的測試結果列表: https://github.com/akkado.net/Akka.Hosting/issues/179#issuecomment-1372320831

為什么這段代碼在一種配置下可以正常工作,而在另一種配置下卻不行?

所以我在 Twitter 上得到了我的問題的答案 - 顯然問題確實是我們在沒有任何范圍可言的環境中創建IReplyGenerator作為范圍依賴項(Akka.NET 參與者沒有范圍):

builder.Services.AddScoped<IReplyGenerator, DefaultReplyGenerator>();

至於為什么這在某些環境 ( AS.NETCORE_ENVIRONMENT="Production" ) 中不是問題,但在其他環境 ( AS.NETCORE_ENVIRONMENT AS.NETCORE_ENVIRONMENT="Development" ) 中是問題 - 這是由於IServiceProvider實現中的此設置: https:// learn.microsoft.com/en-us/do.net/api/microsoft.extensions.dependencyinjection.serviceprovideroptions.validatescopes

MSFT 文檔中的 ServiceProviderOptions.ValidateScopes 屬性

在開發環境中,此值設置為true但在生產環境中它默認為false - 因此在生產環境中即使沒有范圍也會創建這些資源,但在開發環境中會引發錯誤以嘗試警告開發人員沒有此“作用域”依賴項的IScope

錯誤消息根本沒有說清楚,但希望這個答案能幫助其他人。

感謝 https://twitter.com/chton/status/1611024413314400260解決這個問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM