调试AzureFunction以及部署azure函数时缺少ProviderName

我有一个问题是让DbContext正确地从我的local.settings.json提取连接字符串

语境:

  • 这是一个Azurefunction项目
  • 主要问题代码在System.Data.Entity.Internal.AppConfig
  • 虽然我有一个local.settings.json文件,但这不是dotnet核心。 这是.net 4.6.1

错误信息:

‘应用程序配置文件中的连接字符串’ShipBob_DevEntities’不包含必需的providerName属性。“’

Json配置:

 { "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "", "AzureWebJobsDashboard": "" }, "ConnectionStrings": { "ShipBob_DevEntities": { "ConnectionString": "metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider connection string='data source=***;initial catalog=***;persist security info=True;User Id=***;Password=***;;multipleactiveresultsets=True;application name=EntityFramework'", "providerName": "System.Data.EntityClient" } } } 

测试的配置版本:

  • 将提供程序名称移动到实际的ConnectionString标记值:相同的错误ocurrs
  • ConnectionString属性中的provider属性设置为EntityClient :这没有做任何事情
  • 使ShipBob_DevEntities成为一个字符串值= ConnectionString的值:这会引发新的错误,其中包括

    不支持关键字元数据

  • 我尝试使用ADO连接字符串,它抛出code firstexception,这似乎是在database first一种方法中连接字符串不正确时发生的。

我冒昧地使用dotPeek反编译EntityFramework.dll并将问题追溯到System.Data.Entity.Internal.LazyInternalConnection.TryInitializeFromAppConfig 。 在这个方法中,有一个对LazyInternalConnection.FindConnectionInConfig的调用,它调出一个ConnectionStringSettings对象,该对象的ProviderName值设置为null。 不幸的是我无法调试它似乎用来生成这个值的AppConfig.cs类,所以我被卡住了。

在此处输入图像描述

到目前为止,我已经查阅了这两篇文章。 其中一个规定将提供者名称作为自己的令牌; 但是,这不起作用。

https://github.com/Azure/azure-functions-cli/issues/193
https://github.com/Azure/azure-functions-cli/issues/46

有没有人知道在local.settings.json中使用的entity framework连接的正确格式?

所以解决方案最终变得微不足道。 local.settings.json指定的ProviderName属性必须是驼峰式的。

从原来的git hub讨论:
https://github.com/Azure/azure-functions-cli/issues/46
将提供程序名称显示为pascal大小写

https://github.com/Azure/azure-functions-cli/issues/193
在伪代码中显示提供者名称是camel case这很容易错过,但您的配置部分必须完全如下

 "ConnectionStrings": { "ShipBob_DevEntities": { "ConnectionString": "metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider connection string='data source=***;initial catalog=***;persist security info=True;User Id=***;Password=***;;multipleactiveresultsets=True;application name=EntityFramework'", "ProviderName": "System.Data.EntityClient" } } 

这些要点很重要:

  • 确保您的连接字符串包含元数据信息
  • 如果从xml配置中复制字符串,请确保使用unescape撇号
  • 确保ProviderName属性是驼峰式的
  • 确保提供程序名称为System.Data.EntityClient

修复部署中缺少的providername

注意,这个答案假设您正在尝试使用DbContext的无参数构造函数。 如果您要创建新代码,您可以轻松地按照第二个提出的答案

我想出了一种绕过提供程序名称问题的方法,同时仍保留使用门户网站配置和部署插槽的方法。 它涉及使用静态属性设置db context的默认连接字符串

 private static string _connectionString = "name=ShipBob_DevEntities"; static ShipBob_DevEntities() { if(!string.IsNullOrEmpty(System.Environment.GetEnvironmentVariable("AzureFunction"))) { var connectionString = System.Environment.GetEnvironmentVariable("EntityFrameworkConnectionString"); if (!string.IsNullOrEmpty(connectionString)) { _connectionString = connectionString; } } } public ShipBob_DevEntities() : base(_connectionString) { this.Configuration.LazyLoadingEnabled = false; } 

这涉及开发人员在azure门户中创建应用程序设置作为标志。 在我的例子中,它是AzureFunction 。 这确保我们的代码仅在azure函数中运行,并且此DbContext的所有其他客户端,无论是Web应用程序,Windows应用程序等,仍然可以继续按预期运行。 这还涉及将连接字符串作为AppSetting添加到azure门户,而不是实际的连接字符串。 请使用完整的连接字符串,包括元数据信息,但没有提供者名称!

编辑

您需要编辑自动生成的.tt文件t4模板,以确保在首先使用db时不会覆盖此代码。

以下是T4语法的链接: https : //docs.microsoft.com/en-us/visualstudio/modeling/writing-a-t4-text-template

以下是对EF T4模板的解释: https : //msdn.microsoft.com/en-us/library/jj613116(v = vs133).aspx#1159a805-1bcf- 4700-9e99-86d182f143fe

我在这里经历了几个类似的问题和答案。 他们中的许多人要么误导,要么假设每个人都处于同一水平,并了解azure色function是如何运作的。 像我这样的新手没有答案。 我想在此总结一下我的解决方案。 我不认为提供答案是最佳选择,因为它会强制您更改自动生成的edmx文件,这些文件可能被错误覆盖或从数据库下次更新edmx。 此外,最好的选择是在我看来使用连接字符串而不是应用程序设置。

  1. 最重要的是我们了解local.settings.json文件不是为了AZURE。 它是在本地运行您的应用程序,因为名称显然是在说。 所以解决方案与此文件无关。

  2. App.Config或Web.Config不适用于Azurefunction连接字符串。 如果您有数据库层库,则无法像使用Asp.Net应用程序那样使用其中任何一个覆盖连接字符串。

  3. 为了使用,您需要在Azurefunction中的“ Application Settings下的azure门户上定义连接字符串。 有连接字符串。 你应该复制你的DBContext的连接字符串。 如果它是edmx,它将如下所示。 有连接类型,我使用它SQlAzure但我测试与自定义(有人声称只适用于自定义)适用于两者。

metadata = res:// /Models.myDB.csdl|res:// /Models.myDB.ssdl|res://*/Models.myDB.msl;provider=System.Data.SqlClient;provider connection string =’data source = [yourdbURL]; initial catalog = myDB; persist security info = True; user id = xxxx; password = xxx; MultipleActiveResultSets = True; App = EntityFramework

  1. 设置完成后,您需要读取应用程序中的url并提供DBContext。 DbContext实现了一个带有连接字符串参数的构造函数。 默认情况下,构造函数没有任何参数,但您可以扩展它。 如果您使用的是POCO类,则可以简单地修改DbContext类。 如果您使用像我这样的数据库生成的Edmx类,您不想触摸自动生成的edmx类,而不想在同一名称空间中创建分部类并扩展此类,如下所示。

这是自动生成的DbContext

 namespace myApp.Data.Models { public partial class myDBEntities : DbContext { public myDBEntities() : base("name=myDBEntities") { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { throw new UnintentionalCodeFirstException(); } } 

这是你创建的新的部分类

 namespace myApp.Data.Models { [DbConfigurationType(typeof(myDBContextConfig))] partial class myDBEntities { public myDBEntities(string connectionString) : base(connectionString) { } } public class myDBContextConfig : DbConfiguration { public myDBContextConfig() { SetProviderServices("System.Data.EntityClient", SqlProviderServices.Instance); SetDefaultConnectionFactory(new SqlConnectionFactory()); } } } 
  1. 毕竟,您可以使用下面的代码在Azurefunction项目中从azure设置获取连接字符串,并提供给您的DbContext myDBEntities是您在连接字符串的azure门户中提供的名称。
 var connString = ConfigurationManager.ConnectionStrings["myDBEntities"].ConnectionString; using (var dbContext = new myDBEntities(connString)) { //TODO: } 

我之前遇到过类似的问题,我会用以下方法来实现我的目的,你可以参考它:

local.settings.json

 { "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=brucchstorage;AccountKey=", "AzureWebJobsDashboard": "DefaultEndpointsProtocol=https;AccountName=brucchstorage;AccountKey=", "sqldb-connectionstring": "Data Source=.\\sqlexpress;Initial Catalog=DefaultConnection;Integrated Security=True;Connect Timeout=15;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False" }, "ConnectionStrings": { "Bruce_SQLConnectionString": "Data Source=.\\sqlexpress;Initial Catalog=DefaultConnection;Integrated Security=True;Connect Timeout=15;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False" } } 

用于检索连接字符串:

 var connString = ConfigurationManager.AppSettings["sqldb-connectionstring"]; //or var connString = ConfigurationManager.ConnectionStrings["Bruce_SQLConnectionString"].ConnectionString; using (var dbContext = new BruceDbContext(connString)) { //TODO: } 

或者你可以为你的DbContext初始化你的无参数构造函数,如下所示:

 public class BruceDbContext:DbContext { public BruceDbContext() : base("Bruce_SQLConnectionString") { } public BruceDbContext(string connectionString) : base(connectionString) { } } 

然后,您可以为DbContext创建实例,如下所示:

 using (var dbContext = new BruceDbContext(connString)) { //TODO: } 

此外,您可以参考Azurefunction的本地设置文件 。