【问题标题】:Blazor: Unable to resolve service for type xx while attempting to activate yyyBlazor:尝试激活 yyy 时无法解析类型 xx 的服务
【发布时间】:2019-06-13 20:48:32
【问题描述】:

我已经阅读了许多关于同一主题的其他 SO 问题,但我找到的答案都不适用于我的案例。

我已经在我的 Startup.cs 中成功添加了 4 个服务,之前它运行良好。然后我添加了第 5 个,现在我意识到有些东西坏了 - 没有任何服务工作。即使我完全删除了第 5 个,其他的现在也因相同的错误而损坏。

尝试激活时无法解析类型 xx 的服务

这是我的 Startup.cs ConfigureServices.cs:

public void ConfigureServices(IServiceCollection services)
{
    services.AddStorage();
    services.AddSingleton<IMyLocalStorage, MyLocalStorage>();
    services.AddSingleton<IFrontEndService, FrontEndService>();
    services.AddSingleton<ISystemProvider, SystemProviderService>();
    services.AddSingleton<IAuthenticationService, AuthenticationService>();
}

这是我注意到错误的最后一个 AuthenticationService,但即使是以前工作的旧服务现在也失败了。

public interface IAuthenticationService
{
   // ... 
}

public class AuthenticationService : IAuthenticationService
{
    private readonly FrontEndService frontEndService;
    private readonly MyLocalStorage myLocalStorage;

    public AuthenticationService(FrontEndService frontEndService, MyLocalStorage myLocalStorage)
    {
        this.frontEndService = frontEndService;
        this.myLocalStorage = myLocalStorage;
    }

    // ...
}

服务简单;一个接口,该接口的一个实现,然后在 Startup.cs 中添加。我不知道它为什么停止工作。

因此,如果我删除 IAuthenticationService,则错误会显示在 FrontEndService 中,然后抱怨 MyLocalStorage:

public interface IFrontEndService
{
    Task<T> GetAsync<T>(string requestUri);
}

public class FrontEndService : IFrontEndService
{
    private readonly HttpClient client;
    private readonly MyLocalStorage myLocalStorage;

    public FrontEndService(HttpClient client, MyLocalStorage myLocalStorage)
    {
         // ...
    }
}

public class MyLocalStorage : IMyLocalStorage
{
    public MyLocalStorage(LocalStorage storage)
    {
        this.storage = storage;
    }
}

我在这里错过了什么?

【问题讨论】:

    标签: c# dependency-injection blazor


    【解决方案1】:

    当您调用 IServiceCollection 上的方法(例如 .AddSingleton&lt;IFrontEndService, FrontEndService&gt;())时,您是在对容器说:“每当您看到 IFrontEndService 依赖项时,就注入一个 FrontEndService 的实例。”现在如果你看看你的AuthenticationService:

    public class AuthenticationService : IAuthenticationService
    {
        private readonly FrontEndService frontEndService;
        private readonly MyLocalStorage myLocalStorage;
    
        public AuthenticationService(FrontEndService frontEndService, MyLocalStorage myLocalStorage)
        {
            this.frontEndService = frontEndService;
            this.myLocalStorage = myLocalStorage;
        }
    
        // ...
    }
    

    注意您是如何传递FrontEndServiceMyLocalStorage 的依赖项,而不是您注册的接口。这意味着容器无法识别它们,因此它不知道如何实现依赖图。

    您需要更改服务以依赖于接口,因为这些是您在容器中注册的:

    public class AuthenticationService : IAuthenticationService
    {
        private readonly IFrontEndService frontEndService;
        private readonly IMyLocalStorage myLocalStorage;
    
        public AuthenticationService(IFrontEndService frontEndService, IMyLocalStorage myLocalStorage)
        {
            this.frontEndService = frontEndService;
            this.myLocalStorage = myLocalStorage;
        }
    
        // ...
    }
    

    【讨论】:

    • 这很奇怪,因为我以前完全使用过这种方法,而且效果很好。如果您阅读文档,Microsoft 还使用具体类,而不是接口:docs.microsoft.com/en-us/aspnet/core/blazor/… 你确定吗? =)
    • @Ted 如果您仔细查看这些文档,您会发现它们正在使用以下接口:@inject IDataAccess DataRepositoryComponentBase 也是如此,其中属性注入是通过 [Inject] 属性使用的。如果您正在谈论使用 HttpClient 作为依赖项,那么这是一个已经为您注册的类型,因此可以注入它。
    • 真的,因为这是他们为注入服务而编写的:“复杂的服务可能需要额外的服务。在前面的示例中,DataAccess 可能需要 HttpClient 默认服务。@inject(或InjectAttribute) 不可用于服务。必须使用构造函数注入。通过向服务的构造函数添加参数来添加所需的服务。" 因此,它们使用@987654335 @,对吧?
    • @Ted 必须改用构造函数注入。 如果您使用已注册的接口,您会遇到这种情况。需要明确的是,.AddX() 方法有单一类型版本,您可以在其中指定具体类型而不是接口/具体类型对,这将注入具体类型的实例。只要您将注册的类型与实际注入的类型匹配,两者都可以工作。
    • @Ted: "Microsoft 也使用具体类" 这很有可能,但也注册了这些具体类。 DI 两者都会,只要保持一致即可。
    【解决方案2】:

    @特德, 您还记得几周前的一个问题,您在服务中使用了 LocalStorage 吗?在该服务中,您有一个带有 IStorage 参数的构造函数,但这会导致错误,原因是尽管 LocalStorage 类实现了 IStorage 接口,但该库的创建者将 LocalStorage 作为具体类添加到 DI 容器中:

    public static IServiceCollection AddStorage(this IServiceCollection services)
            {
                return services.AddSingleton<SessionStorage>()
                    .AddSingleton<LocalStorage>();
            } 
    

    因此,您必须使用

    (本地存储)

    而不是

    (IStorage 存储)

    上面的扩展方法,可以改写成这样:

    public static IServiceCollection AddStorage(this IServiceCollection services)
                {
                    return services.AddSingleton<IStorage, SessionStorage>()
                        .AddSingleton<IStorage, LocalStorage>();
                }  
    

    在这种情况下,您可以在构造函数中使用 IStorage 接口。

    现在你可以制定一个一般规则,并据此采取行动。

    泰德说:

    这很奇怪,因为我以前完全使用过这种方法,而且它 工作正常。如果您阅读文档,Microsoft 也会使用具体的 类,而不是接口

    HttpClient 派生自 HttpMessageInvoker。它没有实现任何接口。

    此代码-sn-p 显示了如何将 HttpClient 添加到服务容器中,并使其可用于在您的客户端 Blazor 中注入:

    services.AddSingleton<HttpClient>(s =>
            {
                // Creating the URI helper needs to wait until the JS Runtime is initialized, so defer it.
                var uriHelper = s.GetRequiredService<IUriHelper>();
                return new HttpClient
                {
                    BaseAddress = new Uri(WebAssemblyUriHelper.Instance.GetBaseUri())
                };
            });
    

    希望这会有所帮助...

    【讨论】:

    • 感谢您的澄清。然而,让我感到困惑的是,我确实在我的 Startup.cs 中使用了AddSingleton&lt;ISomething, Something&gt;,并且直到现在才起作用——我保证!该应用程序运行良好,即使在 Startup.cs 中使用上述方法调用,我也正在使用具体类。这真的让我感到困惑。嗯……
    • 您描述的解决方案是正确的,但我将答案授予 JohnH,因为他实际上是第一个。尽管如此,几天前它是如何工作的,仍然感到困惑。谢谢你的回答。
    • 我也很困惑,因为将 TService 中指定类型的单例服务与 TImplementation 中指定的实现类型添加到 DI 容器需要您在注入 TService 时使用抽象(接口)在组件中或使用构造函数注入时。请注意,抽象被称为服务,而不是具体类。如果它以前对您有效,但现在无效,可能是由于多种原因,包括错误等。测试在这里是必不可少的......
    • 是的,发生了一些事情,因为我 100% 确定它以前在使用接口方法并注入具体类时有效。我想知道 Win10 是否进行了一些更新,并且随之而来的是一些,嗯,.NET 更新或其他东西,这改变了行为?不过听起来有点牵强……呵呵。
    猜你喜欢
    • 2018-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-23
    • 1970-01-01
    • 2018-09-24
    • 1970-01-01
    相关资源
    最近更新 更多