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

时间:2017-10-11 21:50:29

标签: c# entity-framework azure azure-functions

我在uiidDbContext

正确提取连接字符串时遇到问题

上下文:

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

错误讯息:

  

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

Json配置:

local.settings.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" } } } 标记值:相同的错误ocurrs
  • ConnectionString属性中的provider属性设置为ConnectionString:这没有做任何事情
  • EntityClient字符串值= ShipBob_DevEntities的值:这会引发新的错误

      

    不支持关键字元数据

  • 我尝试使用ADO连接字符串,该字符串会引发ConnectionString异常,这种异常似乎是在code first方法中连接字符串不正确时发生的。

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

enter image description here

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

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

有没有人知道在local.settings.json中使用正确的格式来进行实体框架连接?

4 个答案:

答案 0 :(得分:11)

所以解决方案最终变得微不足道。 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
以伪代码显示提供程序名称为驼峰式大小写 这很容易错过,但您的配置部分必须完全如下

"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

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

注意,这个答案假设您正在尝试使用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;
    }  

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

修改

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

以下是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=vs.113).aspx#1159a805-1bcf-4700-9e99-86d182f143fe

答案 1 :(得分:9)

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

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

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

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

  4.   

    元数据= RES:// /Models.myDB.csdl|res:// /Models.myDB.ssdl|res://*/Models.myDB.msl;provider=System。 Data.SqlClient提供方   连接字符串='数据源= [yourdbURL];初始   catalog = myDB; persist security info = True;用户   ID = XXXX;密码= XXX; MultipleActiveResultSets = TRUE;应用=的EntityFramework

    1. 设置完成后,您需要读取应用程序中的url并提供DBContext。 DbContext实现了一个带有连接字符串参数的构造函数。默认情况下,构造函数没有任何参数,但您可以扩展它。如果您使用的是POCO类,则可以简单地修改DbContext类。如果您使用像我这样的数据库生成的Edmx类,您不想触摸自动生成的edmx类,而不想在同一名称空间中创建部分类,并按如下所示扩展此类。
    2. 这是自动生成的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. 毕竟你可以使用下面的代码从Azure功能项目中获取azure设置的连接字符串并提供给你的DbContext myDBEntities是您在连接字符串的azure门户中提供的名称。
      2. var connString = ConfigurationManager.ConnectionStrings["myDBEntities"].ConnectionString;
        
        
         using (var dbContext = new myDBEntities(connString))
        {
                //TODO:
        }
        

答案 2 :(得分:0)

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

<强> 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功能。

答案 3 :(得分:0)

以下两种对我有用的方法:

方法1

  • 以以下格式将连接字符串添加到“应用程序设置”(分别为local.settings.json):
  

metadata = res:// /xxx.csdl|res:// /xxx.ssdl|res://*/xxx.msl;provider=System.Data.SqlClient;提供商连接字符串='数据源= xxx.database.windows.net;初始目录= xxx;用户ID = xxx;密码= xxx; MultipleActiveResultSets = True; App = EntityFramework'

  • 转到扩展DbContext(“ TestEntities”)的类,并扩展构造函数以将连接字符串作为参数
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
}
  • 这种方法的问题在于,每次更新数据库时,都需要更新“ TestEntities”类,因为该类将被覆盖

方法2

  • 此处的目标是使类“ TestEntities”保持原样,以避免方法1出现此问题

  • 将连接字符串添加到“方法1”中的“应用程序设置”(分别为local.settings.json)中

  • 按原样保留TestEntities

public partial class TestEntities : DbContext
    {
        public TestEntities ()
            : base("name=TestEntities")
        {
        }
  • 由于TestEntities是局部的,因此您可以通过创建另一个在相同名称空间中具有相同名称的局部扩展该类。此类的目标是提供将连接字符串作为参数的构造函数

public partial class TestEntities
{
    public TestEntities(string connectionString)
        : base(connectionString)
    {
    }
}
  • 然后,您可以继续使用方法1