在 class 中实例化 IConfidentialClientApplication 是不好的做法吗?
Is it bad practice to instantiate an IConfidentialClientApplication within a class?
例如,我有一个 AuthorizationClient
在我的应用程序配置中注册为临时服务,它充当我的 API 和 Azure 之间授权令牌的客户端中介。在里面,有一个 IConfidentialClientApplication
对象 属性:
public class AuthorizationClient : IAuthorizationClient
{
private readonly string[] _resourceIds;
private IConfidentialClientApplication App;
public AuthorizationClient(IAuthenticationConfig AuthenticationConfig, IConfiguration configuration)
{
var scope = "/.default";
var resourceId = "api://" + configuration[AuthenticationConfig.ResourceID] + scope;
var clientId = configuration[AuthenticationConfig.ClientID];
var clientSecret = configuration[AuthenticationConfig.ClientSecret];
var instance = AuthenticationConfig.Instance;
var tenant = configuration[AuthenticationConfig.AzureTenantId];
var authority = string.Format(CultureInfo.InvariantCulture, instance, tenant);
_resourceIds = new string[] { resourceId };
try
{
App = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri(authority))
.Build();
}
catch(Exception ex)
{
//TODO logger
}
}
...
}
在此class的其他地方,我有成功获取令牌、检查令牌缓存等的方法
但是当我看到 IConfidentialClientApplication
的文档或存储库在使用时,它总是作为应用程序级别的服务添加,而不是在另一个对象中。我这样做的方式是否存在潜在的陷阱?
如果每个客户端请求都实例化一个新的,IConfidentialClientApplication
似乎更像是 heavy/large,但是当我在一个应用程序级服务的对象中实例化它时,似乎只有一个每个生命周期创建。
在你的目标中实例化依赖关系class是一种代码味道
陷阱:与实现细节的紧密耦合和违反显式依赖原则。
这些会使单独维护和测试您的 class 变得困难。
可以通过应用单一职责原则/关注点分离和显式依赖原则来解决这些设计问题,使您的代码更加可靠
创建一个 class 来存储客户端选项。根据提供的示例,它可能看起来像这样
public class AuthorizationClientOptions {
public string[] ResourceIds { get; set; }
}
重构 class 以明确依赖于执行其功能的实际需要
public class AuthorizationClient : IAuthorizationClient {
private readonly string[] _resourceIds;
private readonly IConfidentialClientApplication app;
public AuthorizationClient(IConfidentialClientApplication app, IOptions<AuthorizationClientOptions> options) {
this.app = app;
_resourceIds = options.Value.ResourceIds;
}
// ...
}
在组合根(启动)中配置必要的实现细节
//...
IConfiguration Configuration; //Populated either via constructor injection or manually
public void ConfigureServices(IServiceCollection services) {
//...
//Configure options
services.AddOptions<AuthorizationClientOptions>()
.Configure<IAuthenticationConfig,IConfiguration>(
(o, authConfig, config) => {
var scope = "/.default";
var resourceId = "api://" + config[authConfig.ResourceID] + scope;
o.ResourceIds = new string[] { resourceId };
});
//Configure dependency using factory delegate
services.AddSingleton<IConfidentialClientApplication>(sp => {
IAuthenticationConfig AuthenticationConfig = sp.GetRequiredService<IAuthenticationConfig>();
var instance = Configuration.Instance;
var tenant = Configuration[AuthenticationConfig.AzureTenantId];
var authority = string.Format(CultureInfo.InvariantCulture, instance, tenant);
var clientId = Configuration[AuthenticationConfig.ClientID];
var clientSecret = Configuration[AuthenticationConfig.ClientSecret];
return ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri(authority))
.Build();
});
services.AddScoped<IAuthorizationClient, AuthorizationClient>();
//...
}
The IConfidentialClientApplication
seems rather heavy/large if a new one is instantiated per every client request
上面例子中的IConfidentialClientApplication
是作为单例创建的,所以在实例化的时候会有一次性成本
例如,我有一个 AuthorizationClient
在我的应用程序配置中注册为临时服务,它充当我的 API 和 Azure 之间授权令牌的客户端中介。在里面,有一个 IConfidentialClientApplication
对象 属性:
public class AuthorizationClient : IAuthorizationClient
{
private readonly string[] _resourceIds;
private IConfidentialClientApplication App;
public AuthorizationClient(IAuthenticationConfig AuthenticationConfig, IConfiguration configuration)
{
var scope = "/.default";
var resourceId = "api://" + configuration[AuthenticationConfig.ResourceID] + scope;
var clientId = configuration[AuthenticationConfig.ClientID];
var clientSecret = configuration[AuthenticationConfig.ClientSecret];
var instance = AuthenticationConfig.Instance;
var tenant = configuration[AuthenticationConfig.AzureTenantId];
var authority = string.Format(CultureInfo.InvariantCulture, instance, tenant);
_resourceIds = new string[] { resourceId };
try
{
App = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri(authority))
.Build();
}
catch(Exception ex)
{
//TODO logger
}
}
...
}
在此class的其他地方,我有成功获取令牌、检查令牌缓存等的方法
但是当我看到 IConfidentialClientApplication
的文档或存储库在使用时,它总是作为应用程序级别的服务添加,而不是在另一个对象中。我这样做的方式是否存在潜在的陷阱?
如果每个客户端请求都实例化一个新的,IConfidentialClientApplication
似乎更像是 heavy/large,但是当我在一个应用程序级服务的对象中实例化它时,似乎只有一个每个生命周期创建。
在你的目标中实例化依赖关系class是一种代码味道
陷阱:与实现细节的紧密耦合和违反显式依赖原则。
这些会使单独维护和测试您的 class 变得困难。
可以通过应用单一职责原则/关注点分离和显式依赖原则来解决这些设计问题,使您的代码更加可靠
创建一个 class 来存储客户端选项。根据提供的示例,它可能看起来像这样
public class AuthorizationClientOptions {
public string[] ResourceIds { get; set; }
}
重构 class 以明确依赖于执行其功能的实际需要
public class AuthorizationClient : IAuthorizationClient {
private readonly string[] _resourceIds;
private readonly IConfidentialClientApplication app;
public AuthorizationClient(IConfidentialClientApplication app, IOptions<AuthorizationClientOptions> options) {
this.app = app;
_resourceIds = options.Value.ResourceIds;
}
// ...
}
在组合根(启动)中配置必要的实现细节
//...
IConfiguration Configuration; //Populated either via constructor injection or manually
public void ConfigureServices(IServiceCollection services) {
//...
//Configure options
services.AddOptions<AuthorizationClientOptions>()
.Configure<IAuthenticationConfig,IConfiguration>(
(o, authConfig, config) => {
var scope = "/.default";
var resourceId = "api://" + config[authConfig.ResourceID] + scope;
o.ResourceIds = new string[] { resourceId };
});
//Configure dependency using factory delegate
services.AddSingleton<IConfidentialClientApplication>(sp => {
IAuthenticationConfig AuthenticationConfig = sp.GetRequiredService<IAuthenticationConfig>();
var instance = Configuration.Instance;
var tenant = Configuration[AuthenticationConfig.AzureTenantId];
var authority = string.Format(CultureInfo.InvariantCulture, instance, tenant);
var clientId = Configuration[AuthenticationConfig.ClientID];
var clientSecret = Configuration[AuthenticationConfig.ClientSecret];
return ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri(authority))
.Build();
});
services.AddScoped<IAuthorizationClient, AuthorizationClient>();
//...
}
The
IConfidentialClientApplication
seems rather heavy/large if a new one is instantiated per every client request
上面例子中的IConfidentialClientApplication
是作为单例创建的,所以在实例化的时候会有一次性成本