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

Adr*_*ian 12 c# entity-framework azure azure-functions

我有一个问题是DbContext从我的正确拉出我的连接字符串local.settings.json

语境:

  • 这是一个Azure功能项目
  • 主要问题代码是 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"
    }
  }
}  
Run Code Online (Sandbox Code Playgroud)

配置版本测试:

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

    不支持关键字元数据

  • 我尝试使用ADO连接字符串,该字符串抛出code first异常,当您的连接字符串在database first方法中不正确时,似乎会发生异常.

我已经冒昧地EntityFramework.dll使用dotPeek进行反编译,并将问题追溯到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中使用正确的格式进行实体框架连接?

Adr*_*ian 11

所以解决方案最终变得微不足道.必须是camel case中ProviderName指定的属性.local.settings.json

从最初的git hub讨论:
https://github.com/Azure/azure-functions-cli/issues/46
将提供者名称显示为pascal case

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"
  }
}  
Run Code Online (Sandbox Code Playgroud)

这些要点很重要:

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

修复部署中缺少的提供程序名称

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

我找到了一种绕过提供程序名称问题的方法,同时仍保留使用门户网站配置和部署插槽的方法.它涉及使用静态属性设置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;
    }  
Run Code Online (Sandbox Code Playgroud)

这涉及开发人员在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

  • 您还可以使用ConfigurationManager从Azure功能设置中获取连接字符串. (2认同)

bat*_*aci 9

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

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

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

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

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();
        }

}
Run Code Online (Sandbox Code Playgroud)

这是你创建的新的部分类

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());
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)
  1. 毕竟,您可以使用以下代码从Azure功能项目中获取连接字符串,并提供给您的DbContext myDBEntities是您在连接字符串的azure门户中提供的名称.
var connString = ConfigurationManager.ConnectionStrings["myDBEntities"].ConnectionString;


 using (var dbContext = new myDBEntities(connString))
{
        //TODO:
}
Run Code Online (Sandbox Code Playgroud)