I have a asp.net core application leveraging Azure SQL via the User Assigned ManagedIdentity created using Azure portal.
In this implementation I am using Microsoft.EntityFrameworkCore version 2.2.6
I have the following code :
IDBAuthTokenService.cs
public interface IDBAuthTokenService
{
Task<string> GetTokenAsync();
}
AzureSqlAuthTokenService.cs
public class AzureSqlAuthTokenService : IDBAuthTokenService
{
public readonly IConfiguration _configuration;
public AzureSqlAuthTokenService(IConfiguration configuration)
{
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
}
public async Task<string> GetTokenAsync()
{
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions{ManagedIdentityClientId = _configuration[C.AppKeys.UserAssignedClientId]});
var tokenRequestContext = new TokenRequestContext(new[]{_configuration[C.AppKeys.AzureSQLResourceId]});
var token = await credential.GetTokenAsync(tokenRequestContext, default);
return token.Token;
}
}
TestDbContext.cs:
public partial class TestDbContext : DbContext
{
public TestDbContext()
{
}
public TestDbContext(IDBAuthTokenService tokenService, DbContextOptions<TestDbContext> options) : base(options)
{
var connection = this.Database.GetDbConnection() as SqlConnection;
connection.AccessToken = tokenService.GetTokenAsync().Result;
}
public virtual DbSet<HealthCheckData> HealthCheckData { get; set; }
}
Startup.cs
public class Startup
{
private readonly IConfiguration configuration;
private readonly IWebHostEnvironment webHostEnvironment;
/// <summary>
/// Initializes a new instance of the <see cref = "Startup"/> class.
/// </summary>
/// <param name = "configuration">The application configuration, where key value pair settings are stored. See
/// http://docs.asp.net/en/latest/fundamentals/configuration.html</param>
/// <param name = "webHostEnvironment">The environment the application is running under. This can be Development,
/// Staging or Production by default. See http://docs.asp.net/en/latest/fundamentals/environments.html</param>
public Startup(IConfiguration configuration, IWebHostEnvironment webHostEnvironment)
{
this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
this.webHostEnvironment = webHostEnvironment ?? throw new ArgumentNullException(nameof(webHostEnvironment));
}
/// <summary>
/// Configures the services to add to the ASP.NET Core Injection of Control (IoC) container. This method gets
/// called by the ASP.NET runtime. See
/// http://blogs.msdn.com/b/webdev/archive/2014/06/17/dependency-injection-in-asp-net-vnext.aspx
/// </summary>
/// <param name = "services">The services.</param>
public virtual void ConfigureServices(IServiceCollection services) => services.AddAppDBConfiguration(configuration, webHostEnvironment)
/// <summary>
/// Configures the application and HTTP request pipeline. Configure is called after ConfigureServices is
/// called by the ASP.NET runtime.
/// </summary>
/// <param name = "app">The application builder.</param>
/// <param name = "l10nIniService">l10nIniService service</param>
public void Configure(IApplicationBuilder app, L10NCacheInitializationService l10nIniService)
{
string loglevel = this.configuration["Logging:LogLevel:Default"] ?? "None";
_ = Enum.TryParse(loglevel, out LogLevel loglevelEnum);
app.UseIf(this.webHostEnvironment.IsDevelopment(), x => x.UseServerTiming());
}
}
CustomServiceCollectionExtensions.cs
public static IServiceCollection AddAppDBConfiguration(this IServiceCollection services, IConfiguration configuration, IWebHostEnvironment webHostEnvironment) => services.AddEntityFrameworkSqlServer().AddDbContextPool<TestDbContext>(appDbContext =>
{
var appDataDBconn = configuration[VaultKeys.DataDBConnString];
if (webHostEnvironment.IsEnvironment(Constants.Local))
{
appDataDBconn = configuration.GetSection(nameof(ApplicationOptions.Local)).Get<Local>().LocalDataDBConn;
}
appDbContext.UseSqlServer(appDataDBconn, sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
});
});
On running the above code I see the below error :
The DbContext of type 'TestDbContext' cannot be pooled because it does not have a single public constructor accepting a single parameter of type DbContextOptions.
Can anyone help me to resolve this issue?