.NET6 MAUI中IPublicClientApplication依赖注入错误解决咨询
.NET 6 MAUI 登录服务依赖注入配置方案
先解决初始错误
你遇到的Cannot instantiate implementation type 'Interface' for service type 'Interface'错误,是因为注册服务时把接口当成了实现类。比如错误写法:
// 错误:第二个参数不能是接口 services.AddScoped<ILoginService, ILoginService>();
正确写法应该是把实际实现类(LoginService)作为第二个参数:
// 正确:服务类型是接口,实现类型是具体类 services.AddScoped<ILoginService, LoginService>();
处理两个IPublicClientApplication实例的注入问题
因为LoginService需要两个不同的IPublicClientApplication实例,直接重复注册同类型服务会导致DI只会注入最后一个实例,下面提供三种可行方案:
方案1:使用Keyed Services(推荐,.NET 6+支持)
通过给两个实例添加唯一标识(Key),在创建LoginService时指定获取对应Key的实例:
- 注册带Key的
IPublicClientApplication实例:
// 注册第一个实例,Key为"AuthApp1" services.AddKeyedScoped<IPublicClientApplication>("AuthApp1", (sp, key) => { var config = sp.GetRequiredService<IConfiguration>(); return PublicClientApplicationBuilder .Create(config["AzureAd:AppId1"]) .WithRedirectUri($"msal{config["AzureAd:AppId1"]}://auth") .Build(); }); // 注册第二个实例,Key为"AuthApp2" services.AddKeyedScoped<IPublicClientApplication>("AuthApp2", (sp, key) => { var config = sp.GetRequiredService<IConfiguration>(); return PublicClientApplicationBuilder .Create(config["AzureAd:AppId2"]) .WithRedirectUri($"msal{config["AzureAd:AppId2"]}://auth") .Build(); });
- 注册
ILoginService时,通过工厂方法获取两个带Key的实例:
services.AddScoped<ILoginService>(sp => { var app1 = sp.GetRequiredKeyedService<IPublicClientApplication>("AuthApp1"); var app2 = sp.GetRequiredKeyedService<IPublicClientApplication>("AuthApp2"); return new LoginService(app1, app2); });
方案2:直接在工厂方法中创建实例
如果这两个实例只给LoginService使用,不需要复用,可以直接在创建LoginService时生成两个实例:
services.AddScoped<ILoginService>(sp => { var config = sp.GetRequiredService<IConfiguration>(); // 创建第一个认证应用实例 var app1 = PublicClientApplicationBuilder .Create(config["AzureAd:AppId1"]) .WithRedirectUri($"msal{config["AzureAd:AppId1"]}://auth") .Build(); // 创建第二个认证应用实例 var app2 = PublicClientApplicationBuilder .Create(config["AzureAd:AppId2"]) .WithRedirectUri($"msal{config["AzureAd:AppId2"]}://auth") .Build(); return new LoginService(app1, app2); });
方案3:用自定义接口区分实例
创建两个空接口继承自IPublicClientApplication,通过不同接口来区分实例:
- 定义自定义接口:
public interface IAuthApp1 : IPublicClientApplication { } public interface IAuthApp2 : IPublicClientApplication { }
- 注册这两个接口的实现:
services.AddScoped<IAuthApp1>(sp => { var config = sp.GetRequiredService<IConfiguration>(); // 强制转换为自定义接口(PublicClientApplication实现了IPublicClientApplication,所以可以转换) return (IAuthApp1)PublicClientApplicationBuilder .Create(config["AzureAd:AppId1"]) .WithRedirectUri($"msal{config["AzureAd:AppId1"]}://auth") .Build(); }); services.AddScoped<IAuthApp2>(sp => { var config = sp.GetRequiredService<IConfiguration>(); return (IAuthApp2)PublicClientApplicationBuilder .Create(config["AzureAd:AppId2"]) .WithRedirectUri($"msal{config["AzureAd:AppId2"]}://auth") .Build(); });
- 修改
LoginService的构造函数,接收自定义接口:
public class LoginService : ILoginService { private readonly IAuthApp1 _authApp1; private readonly IAuthApp2 _authApp2; public LoginService(IAuthApp1 authApp1, IAuthApp2 authApp2) { _authApp1 = authApp1; _authApp2 = authApp2; } // 登录相关业务方法 }
- 正常注册
ILoginService:
services.AddScoped<ILoginService, LoginService>();
内容的提问来源于stack exchange,提问作者eengstroem
相关产品推荐
相关产品推荐

