【问题标题】:EF Core DB First, and how to avoid Constructor Overwrite on Model GenerationEF Core DB First,以及如何避免在模型生成时覆盖构造函数
【发布时间】:2020-06-12 07:19:09
【问题描述】:

我将不再对 Azure DB 使用 SQL 身份验证,而是改用 Active Directory 托管身份验证,如 article 中所述。

基本上,我正在做两件主要的事情来让它发挥作用。

1- 在 DBContext 构造函数中注入令牌:

public MyDBContext(DbContextOptions<MyDBContext> options)
    : base(options)
{
    var conn = (SqlConnection)Database.GetDbConnection();
    conn.AccessToken = (new Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider()).GetAccessTokenAsync("https://database.windows.net/").Result;

}

2- 在我的 Web 应用启动文件中,我正在注入 DBContext

    string SqlConnection = localConfig["SqlConnectionString"];
    services.AddDbContext<MyDBContext>(options => options.UseSqlServer(SqlConnection, sqlServerOptions => { sqlServerOptions.CommandTimeout(1000); }));

我现在的问题是,每次我需要使用Scaffold-DbContext 命令刷新模型时,我的MyDbContext 都会被覆盖,并且我会丢失对构造函数所做的更改。

有哪些解决方案可以避免这个问题?或者,有没有更好的方法将令牌注入其他地方有效

编辑: 请注意,我使用的是 EF 2.x

【问题讨论】:

    标签: c# azure entity-framework entity-framework-core azure-active-directory


    【解决方案1】:

    我使用拦截器注入访问令牌:

    public class ManagedIdentityConnectionInterceptor : DbConnectionInterceptor
    {
        private readonly bool _useManagedIdentity;
        private readonly AzureServiceTokenProvider _tokenProvider;
    
        public ManagedIdentityConnectionInterceptor(bool useManagedIdentity)
        {
            _useManagedIdentity = useManagedIdentity;
            _tokenProvider = new AzureServiceTokenProvider();
        }
    
        public override async Task<InterceptionResult> ConnectionOpeningAsync(
            DbConnection connection,
            ConnectionEventData eventData,
            InterceptionResult result,
            CancellationToken cancellationToken = default)
        {
            if (_useManagedIdentity)
            {
                // In Azure, get an access token for the connection
                var sqlConnection = (SqlConnection)connection;
                var accessToken = await GetAccessTokenAsync(cancellationToken);
                sqlConnection.AccessToken = accessToken;
            }
    
            return result;
        }
    
        private async Task<string> GetAccessTokenAsync(CancellationToken cancellationToken)
        {
            // Get access token for Azure SQL DB
            var resource = "https://database.windows.net/";
            return await _tokenProvider.GetAccessTokenAsync(resource, cancellationToken: cancellationToken);
        }
    }
    

    可以这样添加:

    // Detect environment somehow (locally you might not want to use tokens)
    var useManagedIdentity = true;
    var managedIdentityInterceptor = new ManagedIdentityConnectionInterceptor(useManagedIdentity);
    services.AddDbContext<Context>(options => options.UseSqlServer(connectionString).AddInterceptors(managedIdentityInterceptor));
    

    这种方式不需要在构造函数中进行任何更改。 在打开与 SQL DB 的连接之前,拦截器将获得一个令牌。 我们还避免在构造函数中执行同步异步。 请注意,这需要 EF Core 3.x。

    【讨论】:

    • 谢谢@juunas。这看起来是一个非常好的解决方案。唯一的问题是我使用的是 EF 2.x 。我应该在我的问题中提到这一点。
    • 对:如果我没记错的话,2.2 有一些版本的拦截器,但我从来没有在那里实现过。
    • @AbuShokry 所以最后你会得到我的解决方案 :) 顺便说一句,这与你当前的构造函数逻辑完全相同。
    • @IvanStoev 怎么样?在这里,您似乎正在创建 SQL 连接的新实例。虽然我的解决方案(如在 MS 文档中)将令牌注入到我不需要管理或处置的已建立连接中。我担心您的解决方案在某些时候会导致连接耗尽。我可以阅读对您的解决方案的任何引用吗?
    • @AbuShokry 摘自Documentation“如果连接处于关闭状态,则 EF 将根据需要打开和关闭连接。”。关闭是将物理连接返回到池
    【解决方案2】:

    您可以将UseSqlServer 重载与DbConnection 参数一起使用并传递配置的SqlConnection 对象:

    var sqlConnectionString = localConfig["SqlConnectionString"];
    services.AddDbContext<MyDBContext>(
        options => options.UseSqlServer(new SqlConnection(sqlConnectionString)
        {
            AccessToken = (new Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider()).GetAccessTokenAsync("https://database.windows.net/").Result
        },
        sqlServerOptions => { sqlServerOptions.CommandTimeout(1000); }));
    

    【讨论】:

    • 令牌将在几个小时后过期,因此需要每隔几个小时重新启动应用程序:\
    • @juunas 只要数据库上下文是短暂的(这对于 AspNet.Core 应用程序来说很常见),这应该不是问题。但我想你的解决方案总体上更好,如果有开箱即用的东西会很好。
    • 您的解决方案在启动时获取一次令牌。然后它会在某个时候停止工作。
    • @juunas 当然(我感到羞耻)!但它本来不是打算这样做的,现在也没有:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-03
    • 2012-04-26
    • 2014-04-11
    • 2018-10-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多