调试 AzureFunction 以及部署 azure 函数时缺少 ProviderName
Missing ProviderName when debugging AzureFunction as well as deploying azure function
我在获取 DbContext
以正确从 local.settings.json
中提取连接字符串时遇到问题
上下文:
- 这是一个 Azure 函数项目
- 主要问题代码在
System.Data.Entity.Internal.AppConfig
- 虽然我有一个
local.settings.json
文件,但这不是 dotnet 核心。这是.net 4.6.1
错误信息:
'The connection string 'ShipBob_DevEntities' in the application's configuration file does not contain the required providerName attribute."'
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
令牌值中:出现相同的错误
- 将
ConnectionString
属性中的 provider
属性设置为 EntityClient
:这没有任何作用
将 ShipBob_DevEntities
的字符串值设为 ConnectionString
的值:这会引发新的错误,例如
keyword metadata is not supported
我尝试使用 ADO 连接字符串,它会引发 code first
异常,当您的连接字符串在 database first
方法中不正确时,似乎会发生这种情况。
我冒昧地使用 dotPeek 反编译了 EntityFramework.dll
,并将问题追溯到 System.Data.Entity.Internal.LazyInternalConnection.TryInitializeFromAppConfig
。在这个方法中有一个对 LazyInternalConnection.FindConnectionInConfig
的调用,它吐出一个 ConnectionStringSettings
对象,该对象的 ProviderName
值设置为 null。不幸的是,我无法调试它似乎用来生成此值的 AppConfig.cs
class,所以我被卡住了。
目前我已经查阅了这两篇文章。其中一个声明将提供者名称作为它自己的标记;但是,这不起作用。
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
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=brucchstorage;AccountKey=<AccountKey>",
"AzureWebJobsDashboard": "DefaultEndpointsProtocol=https;AccountName=brucchstorage;AccountKey=<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:
}
此外,您可以参考 Local settings file Azure Functions。
所以解决方案最终变得微不足道。在 local.settings.json
中指定的 ProviderName
属性必须 是驼峰式。
来自最初的 git 中心讨论:
https://github.com/Azure/azure-functions-cli/issues/46
将提供商名称显示为 pascal 大小写
https://github.com/Azure/azure-functions-cli/issues/193
在伪代码中显示提供者名称为驼峰式
很容易错过,但是你的配置部分必须完全如下
"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 配置复制字符串,请确保取消转义撇号
- 确保
ProviderName
属性是 驼峰式大小写
- 确保提供商名称是
System.Data.EntityClient
修复了部署中缺少提供者名称的问题
请注意,此答案假定您正在尝试使用 DbContext 的无参数构造函数。如果您正在创建新代码,您可以轻松地遵循第二个赞成的答案
我找到了一种方法来规避提供商名称问题,同时仍然保留对门户配置的使用,从而保留部署槽。它涉及使用静态属性设置数据库上下文的默认连接字符串
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 语法中的 link:https://docs.microsoft.com/en-us/visualstudio/modeling/writing-a-t4-text-template
下面是对 EF T4 模板的解释:https://msdn.microsoft.com/en-us/library/jj613116(v=vs.113).aspx#1159a805-1bcf-4700-9e99-86d182f143fe
我在这里经历了几个类似的问题和答案。他们中的许多人要么具有误导性,要么假设每个人都处于同一水平并且理解 azure 函数是如何工作的。像我这样的新手没有答案。我想在这里逐步总结我的解决方案。我不认为提供的答案是最好的选择,因为它会强制您更改自动生成的 edmx 文件,这些文件可能会被错误覆盖或下次从数据库更新您的 edmx。我认为这里最好的选择是使用连接字符串而不是应用程序设置。
最重要的是我们了解local.settings.json文件
不适合 AZURE。它是 运行 您在本地的应用程序的名称
明明说所以解决方案与这个文件无关。
App.Config 或 Web.Config 不适用于 Azure 函数连接字符串。如果您有数据库层库,则无法像在 Asp.Net 应用程序中那样使用其中任何一个覆盖连接字符串。
为了使用,您需要在 Azure 函数的 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
- 设置完成后,您需要在应用程序中读取 url 并提供 DBContext。 DbContext 实现了一个带有连接字符串参数的构造函数。默认情况下,构造函数没有任何参数,但您可以扩展它。如果你使用的是 POCO class,你可以简单地修改 DbContext class。如果你像我一样使用数据库生成的 Edmx classes,你不想触摸自动生成的 edmx class 而不是你想在同一个命名空间中创建部分 class 并扩展这个 class 如下。
这是自动生成的 DbContext
namespace myApp.Data.Models
{
public partial class myDBEntities : DbContext
{
public myDBEntities()
: base("name=myDBEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
}
这是新的部分class,您创建
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());
}
}
}
- 毕竟你可以从 Azure 设置中获取连接字符串,在你的 Azure Function 项目中使用下面的代码并提供给你的 DbContext
myDBEntities 是您在 Azure 门户中为连接字符串提供的名称。
var connString = ConfigurationManager.ConnectionStrings["myDBEntities"].ConnectionString;
using (var dbContext = new myDBEntities(connString))
{
//TODO:
}
这里有两种适合我的方法:
方法一
- 按照以下格式将连接字符串添加到应用程序设置(分别为local.settings.json):
metadata=res:///xxx.csdl|res:///xxx.ssdl|res://*/xxx.msl;provider=System.Data.SqlClient;provider connection string='data source=xxx.database.windows.net;initial catalog=xxx;user id=xxx;password=xxx;MultipleActiveResultSets=True;App=EntityFramework'`
- 转到扩展 DbContext ("TestEntities") 的 class 并扩展构造函数以将连接字符串作为参数
public partial class TestEntities: DbContext
{
public TestEntities(string connectionString)
: base(connectionString)
{
}
- 如果您想与数据库交互,您需要从应用程序设置中检索连接字符串,然后在初始化 DbContext 时将其传递过来
string connectionString = Environment.GetEnvironmentVariable("connectionStringAppSettings");
using (var dbContext = new TestEntities(connectionString))
{
// Do Something
}
- 这种方法的问题是每次更新数据库时都需要更新 class "TestEntities" 因为它被覆盖了
方法二
这里的目标是保持 class "TestEntities" 不变,以避免方法 1
中的问题
将连接字符串添加到应用程序设置(分别为 local.settings.json),如方法 1
保持 TestEntities 不变
public partial class TestEntities : DbContext
{
public TestEntities ()
: base("name=TestEntities")
{
}
- 由于 TestEntities 是部分的,您可以通过在同一命名空间中创建另一个同名的部分来扩展 class。 class 的目标是提供将连接字符串作为参数的构造函数
public partial class TestEntities
{
public TestEntities(string connectionString)
: base(connectionString)
{
}
}
- 然后你可以像方法 1 一样继续
我在获取 DbContext
以正确从 local.settings.json
上下文:
- 这是一个 Azure 函数项目
- 主要问题代码在
System.Data.Entity.Internal.AppConfig
- 虽然我有一个
local.settings.json
文件,但这不是 dotnet 核心。这是.net 4.6.1
错误信息:
'The connection string 'ShipBob_DevEntities' in the application's configuration file does not contain the required providerName attribute."'
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
令牌值中:出现相同的错误 - 将
ConnectionString
属性中的provider
属性设置为EntityClient
:这没有任何作用 将
ShipBob_DevEntities
的字符串值设为ConnectionString
的值:这会引发新的错误,例如keyword metadata is not supported
我尝试使用 ADO 连接字符串,它会引发
code first
异常,当您的连接字符串在database first
方法中不正确时,似乎会发生这种情况。
我冒昧地使用 dotPeek 反编译了 EntityFramework.dll
,并将问题追溯到 System.Data.Entity.Internal.LazyInternalConnection.TryInitializeFromAppConfig
。在这个方法中有一个对 LazyInternalConnection.FindConnectionInConfig
的调用,它吐出一个 ConnectionStringSettings
对象,该对象的 ProviderName
值设置为 null。不幸的是,我无法调试它似乎用来生成此值的 AppConfig.cs
class,所以我被卡住了。
目前我已经查阅了这两篇文章。其中一个声明将提供者名称作为它自己的标记;但是,这不起作用。
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
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=brucchstorage;AccountKey=<AccountKey>",
"AzureWebJobsDashboard": "DefaultEndpointsProtocol=https;AccountName=brucchstorage;AccountKey=<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:
}
此外,您可以参考 Local settings file Azure Functions。
所以解决方案最终变得微不足道。在 local.settings.json
中指定的 ProviderName
属性必须 是驼峰式。
来自最初的 git 中心讨论:
https://github.com/Azure/azure-functions-cli/issues/46
将提供商名称显示为 pascal 大小写
https://github.com/Azure/azure-functions-cli/issues/193
在伪代码中显示提供者名称为驼峰式
很容易错过,但是你的配置部分必须完全如下
"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 配置复制字符串,请确保取消转义撇号
- 确保
ProviderName
属性是 驼峰式大小写 - 确保提供商名称是
System.Data.EntityClient
修复了部署中缺少提供者名称的问题
请注意,此答案假定您正在尝试使用 DbContext 的无参数构造函数。如果您正在创建新代码,您可以轻松地遵循第二个赞成的答案
我找到了一种方法来规避提供商名称问题,同时仍然保留对门户配置的使用,从而保留部署槽。它涉及使用静态属性设置数据库上下文的默认连接字符串
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 语法中的 link:https://docs.microsoft.com/en-us/visualstudio/modeling/writing-a-t4-text-template
下面是对 EF T4 模板的解释:https://msdn.microsoft.com/en-us/library/jj613116(v=vs.113).aspx#1159a805-1bcf-4700-9e99-86d182f143fe
我在这里经历了几个类似的问题和答案。他们中的许多人要么具有误导性,要么假设每个人都处于同一水平并且理解 azure 函数是如何工作的。像我这样的新手没有答案。我想在这里逐步总结我的解决方案。我不认为提供的答案是最好的选择,因为它会强制您更改自动生成的 edmx 文件,这些文件可能会被错误覆盖或下次从数据库更新您的 edmx。我认为这里最好的选择是使用连接字符串而不是应用程序设置。
最重要的是我们了解local.settings.json文件 不适合 AZURE。它是 运行 您在本地的应用程序的名称 明明说所以解决方案与这个文件无关。
App.Config 或 Web.Config 不适用于 Azure 函数连接字符串。如果您有数据库层库,则无法像在 Asp.Net 应用程序中那样使用其中任何一个覆盖连接字符串。
为了使用,您需要在 Azure 函数的
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
- 设置完成后,您需要在应用程序中读取 url 并提供 DBContext。 DbContext 实现了一个带有连接字符串参数的构造函数。默认情况下,构造函数没有任何参数,但您可以扩展它。如果你使用的是 POCO class,你可以简单地修改 DbContext class。如果你像我一样使用数据库生成的 Edmx classes,你不想触摸自动生成的 edmx class 而不是你想在同一个命名空间中创建部分 class 并扩展这个 class 如下。
这是自动生成的 DbContext
namespace myApp.Data.Models
{
public partial class myDBEntities : DbContext
{
public myDBEntities()
: base("name=myDBEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
}
这是新的部分class,您创建
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());
}
}
}
- 毕竟你可以从 Azure 设置中获取连接字符串,在你的 Azure Function 项目中使用下面的代码并提供给你的 DbContext myDBEntities 是您在 Azure 门户中为连接字符串提供的名称。
var connString = ConfigurationManager.ConnectionStrings["myDBEntities"].ConnectionString; using (var dbContext = new myDBEntities(connString)) { //TODO: }
这里有两种适合我的方法:
方法一
- 按照以下格式将连接字符串添加到应用程序设置(分别为local.settings.json):
metadata=res:///xxx.csdl|res:///xxx.ssdl|res://*/xxx.msl;provider=System.Data.SqlClient;provider connection string='data source=xxx.database.windows.net;initial catalog=xxx;user id=xxx;password=xxx;MultipleActiveResultSets=True;App=EntityFramework'`
- 转到扩展 DbContext ("TestEntities") 的 class 并扩展构造函数以将连接字符串作为参数
public partial class TestEntities: DbContext
{
public TestEntities(string connectionString)
: base(connectionString)
{
}
- 如果您想与数据库交互,您需要从应用程序设置中检索连接字符串,然后在初始化 DbContext 时将其传递过来
string connectionString = Environment.GetEnvironmentVariable("connectionStringAppSettings");
using (var dbContext = new TestEntities(connectionString))
{
// Do Something
}
- 这种方法的问题是每次更新数据库时都需要更新 class "TestEntities" 因为它被覆盖了
方法二
这里的目标是保持 class "TestEntities" 不变,以避免方法 1
中的问题
将连接字符串添加到应用程序设置(分别为 local.settings.json),如方法 1
保持 TestEntities 不变
public partial class TestEntities : DbContext
{
public TestEntities ()
: base("name=TestEntities")
{
}
- 由于 TestEntities 是部分的,您可以通过在同一命名空间中创建另一个同名的部分来扩展 class。 class 的目标是提供将连接字符串作为参数的构造函数
public partial class TestEntities
{
public TestEntities(string connectionString)
: base(connectionString)
{
}
}
- 然后你可以像方法 1 一样继续