webentwicklung-frage-antwort-db.com.de

Fehlender ProviderName beim Debuggen von AzureFunction sowie bei der Bereitstellung der Azure-Funktion

Ich habe ein Problem mit der DbContext, um meine Verbindungszeichenfolge korrekt aus meinem local.settings.json zu ziehen.

Kontext:

  • Dies ist ein Azure-Funktionsprojekt
  • Der Hauptproblemcode ist in System.Data.Entity.Internal.AppConfig
  • Obwohl ich eine local.settings.json-Datei habe, ist dies kein Dotnet-Kern. Es ist .net 4.6.1

Fehlermeldung: 

'Die Verbindungszeichenfolge' ShipBob_DevEntities 'in der Konfigurationsdatei der Anwendung enthält nicht das erforderliche providerName-Attribut. " 

Json Konfiguration:

{
  "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"
    }
  }
}  

Getestete Konfigurationsversionen:

  • Verschieben des Anbieternamens in den tatsächlichen ConnectionString-Tokenwert: Es wird derselbe Fehler angezeigt
  • Das provider-Attribut im ConnectionString-Attribut auf EntityClient setzen: Dies hat nichts getan
  • ShipBob_DevEntities einen String-Wert = auf den Wert von ConnectionString setzen: Dies wirft neue Fehler auf, wie zB 

    keyword-Metadaten werden nicht unterstützt

  • Ich habe versucht, eine Verbindungszeichenfolge ADO zu verwenden, die eine code first-Ausnahme auslöst, die auftritt, wenn Ihre Verbindungszeichenfolge in einem database first-Ansatz falsch ist. 

Ich habe mir die Freiheit genommen, EntityFramework.dll mit dotPeek zu dekompilieren und das Problem auf System.Data.Entity.Internal.LazyInternalConnection.TryInitializeFromAppConfig zurückzuführen. Innerhalb dieser Methode gibt es einen Aufruf an LazyInternalConnection.FindConnectionInConfig, der ein ConnectionStringSettings-Objekt ausspuckt, dessen ProviderName-Wert auf null gesetzt ist. Leider kann ich die AppConfig.cs-Klasse nicht debuggen, mit der sie diesen Wert zu generieren scheint. 

 enter image description here

Bisher habe ich diese beiden Artikel konsultiert. Eine davon gibt an, den Anbieternamen als sein eigenes Token zu verwenden. dies funktioniert jedoch nicht.

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

Kennt jemand das richtige Format, das in local.settings.json für eine Entity Framework-Verbindung verwendet werden soll?

11
Adrian

Die Lösung war also trivial. Das ProviderName-Attribut, das in local.settings.jsonANGEGEBEN IST, MUSS Kamelfall sein. 

Aus den ursprünglichen Diskussionen über Git Hub:
https://github.com/Azure/azure-functions-cli/issues/46
Zeigt den Anbieternamen als Pascal-Fall an. 

https://github.com/Azure/azure-functions-cli/issues/193
Zeigt den Anbieternamen als Kamelfall in Pseudo-Code .__ an. Es war sehr leicht zu übersehen, aber Ihr Konfigurationsabschnitt muss genau wie folgt aussehen 

"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"
  }
}  

Diese Punkte sind wichtig: 

  • Stellen Sie sicher, dass Ihre Verbindungszeichenfolge Metadateninformationen enthält
  • Wenn Sie Ihren String aus einer XML-Konfiguration kopieren, müssen Sie die Aufhebung der Apostrophe aufheben
  • Stellen Sie sicher, dass das ProviderName-Attribut camel case ist.
  • Stellen Sie sicher, dass der Anbietername System.Data.EntityClient ist. 

Korrektur für fehlenden Providernamen in der Bereitstellung

Beachten Sie, diese Antwort setzt voraus, dass Sie versuchen, den parameterlosen Konstruktor eines DbContext zu verwenden. Wenn Sie neuen Code erstellen, können Sie einfach der zweiten Antwort folgen.

Ich habe einen Weg gefunden, das Problem mit dem Providernamen zu umgehen und dabei die Portalkonfiguration und damit die Bereitstellungsslots beizubehalten. Dazu müssen Sie die Standardverbindungszeichenfolge des Datenbankkontextes mit statischen Eigenschaften festlegen 

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

Dazu muss der Entwickler eine App-Einstellung im Azure-Portal als Flag erstellen. In meinem Fall ist es AzureFunction. Dadurch wird sichergestellt, dass unser Code nur in einer Azure-Funktion ausgeführt wird und alle anderen Clients dieses DbContext-Objekts, ob Web-Apps, Windows-Apps usw., sich weiterhin wie erwartet verhalten. Dazu gehört auch das Hinzufügen Ihrer Verbindungszeichenfolge zum Azure-Portal als AppSetting und nicht als tatsächliche Verbindungszeichenfolge. Bitte verwenden Sie die vollständige Verbindungszeichenfolge einschließlich der Informationen zu metadata, jedoch ohne den Anbieternamen!

BEARBEITEN

Sie müssen Ihre automatisch erstellte .tt-Datei t4-Vorlage bearbeiten, um sicherzustellen, dass dieser Code nicht überschrieben wird, wenn Sie db zuerst verwenden. 

Hier ist ein Link zur T4-Syntax: https://docs.Microsoft.com/en-us/visualstudio/modeling/writing-a-t4-text-template

Und hier ist eine Erklärung zu EF T4-Vorlagen: https://msdn.Microsoft.com/de-de/library/jj613116(v=vs.113).aspx#1159a805-1bcf-4700-9e99-86d182f143fe

10
Adrian

Ich habe hier einige ähnliche Fragen und Antworten durchlaufen. Viele von ihnen sind entweder irreführend oder gehen davon aus, dass sich alle auf derselben Ebene befinden und wissen, wie die Azure-Funktionen funktionieren. Für Neulinge wie mich gibt es keine Antwort. Ich möchte hier meine Lösung Schritt für Schritt zusammenfassen. Ich denke nicht, dass die bereitgestellte Antwort die beste Option ist, weil Sie dazu gezwungen werden, die automatisch generierten edmx-Dateien zu ändern, die versehentlich überschrieben werden können, oder das nächste Update Ihres edmx aus der Datenbank. Auch die beste Option ist hier die Verwendung von Verbindungszeichenfolgen anstelle von App-Einstellungen.

  1. das Wichtigste ist, dass wir die Datei local.settings.json verstehenIS NICHT FÜR Azure. Es ist Ihre App im lokalen auszuführen, da der Name klar ist. Die Lösung hat also nichts mit dieser Datei zu tun.

  2. App.Config oder Web.Config funktioniert nicht für Verbindungszeichenfolgen der Azure-Funktion. Wenn Sie über eine Datenbankschichtbibliothek verfügen, können Sie die Verbindungszeichenfolge nicht wie in Asp.Net-Anwendungen mit einer dieser Verbindungen überschreiben.

  3. Damit Sie damit arbeiten können, müssen Sie Ihre Verbindungszeichenfolge im Azure-Portal unter Application Settings in Ihrer Azure-Funktion definieren. Es gibt Verbindungszeichenfolgen. Dort sollten Sie Ihre Verbindungszeichenfolge Ihres DBContext kopieren. Wenn es sich um edmx handelt, sieht es wie folgt aus. Es gibt einen Verbindungstyp, ich verwende es SQlAzure, aber ich habe es mit Custom getestet (jemand behauptet, dass es nur mit Custom funktioniert) funktioniert mit beiden. 

metadata = res: // /Models.myDB.csdl | res:// / Models.myDB.ssdl | res: //*/Models.myDB.msl; provider = System.Data.SqlClient; provider Verbindungszeichenfolge = 'Datenquelle = [yourdbURL]; initial catalog = myDB; persistente Sicherheitsinformationen = True; Benutzer id = xxxx; Kennwort = xxx; MultipleActiveResultSets = True; App = EntityFramework

  1. Nachdem Sie dies eingerichtet haben, müssen Sie die URL in Ihrer Anwendung lesen und den DBContext angeben. DbContext implementiert einen Konstruktor mit dem Parameter für die Verbindungszeichenfolge. Standardmäßig enthält der Konstruktor keine Parameter, Sie können dies jedoch erweitern. Wenn Sie die POCO-Klasse verwenden, können Sie die DbContext-Klasse einfach ändern. Wenn Sie mit der Datenbank generierte Edmx-Klassen wie mich verwenden, möchten Sie nicht die automatisch generierte Edmx-Klasse berühren, anstatt Teilklassen in demselben Namespace zu erstellen und diese Klasse wie folgt zu erweitern.

Dies ist automatisch DbContext generiert

namespace myApp.Data.Models
{   

    public partial class myDBEntities : DbContext
    {
        public myDBEntities()
           : base("name=myDBEntities")
        {
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            throw new UnintentionalCodeFirstException();
        }

}

dies ist die neue Teilklasse, die Sie erstellen

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. Schließlich können Sie die Verbindungszeichenfolge aus den Azure-Einstellungen in Ihrem Azure Function-Projekt mit dem folgenden Code abrufen und Ihrem DbContext MyDBEntities den Namen geben, den Sie im Azure-Portal für Ihre Verbindungszeichenfolge angegeben haben.
var connString = ConfigurationManager.ConnectionStrings["myDBEntities"].ConnectionString;


 using (var dbContext = new myDBEntities(connString))
{
        //TODO:
}
9
batmaci

Hier sind zwei Ansätze, die für mich funktionieren:

Ansatz 1

  • Fügen Sie den App-Einstellungen (bzw. local.settings.json) die Verbindungszeichenfolge im folgenden Format hinzu:

metadata = res: // /xxx.csdl|res:// /xxx.ssdl|res://*/xxx.msl;provider= System.Data.SqlClient; Provider-Verbindungszeichenfolge = "Datenquelle = xxx.database.windows.net; Anfangskatalog = xxx; Benutzer-ID = xxx; Kennwort = xxx; MultipleActiveResultSets = True; App = EntityFramework "`

  • Wechseln Sie zu der Klasse, die DbContext erweitert ("TestEntities"), und erweitern Sie den Konstruktor, um die Verbindungszeichenfolge als Argument zu verwenden
public partial class TestEntities: DbContext
{
    public TestEntities(string connectionString)
        : base(connectionString)
    {
    }
  • Wenn Sie dann mit der Datenbank interagieren möchten, müssen Sie die Verbindungszeichenfolge aus den App-Einstellungen abrufen und sie dann beim Initialisieren von DbContext übergeben
string connectionString = Environment.GetEnvironmentVariable("connectionStringAppSettings");

using (var dbContext = new TestEntities(connectionString))
{
// Do Something
}
  • Das Problem bei diesem Ansatz ist, dass Sie jedes Mal, wenn Sie die Datenbank aktualisieren, die Klasse "TestEntities" aktualisieren müssen, wenn sie überschrieben wird

Ansatz 2

  • Das Ziel hierbei ist, die Klasse "TestEntities" zu verlassen und das Problem von Ansatz 1 zu vermeiden

  • Fügen Sie die Verbindungszeichenfolge wie in Ansatz 1 zu den App-Einstellungen (bzw. local.settings.json) hinzu

  • Lassen Sie TestEntities unverändert

public partial class TestEntities : DbContext
    {
        public TestEntities ()
            : base("name=TestEntities")
        {
        }
  • Da TestEntities partiell ist, können Sie diese Klasse erweitern, indem Sie eine andere Klasse erstellen, die ebenfalls partiell mit demselben Namen im selben Namespace ist. Ziel dieser Klasse ist es, den Konstruktor bereitzustellen, der die Verbindungszeichenfolge als Argument verwendet

public partial class TestEntities
{
    public TestEntities(string connectionString)
        : base(connectionString)
    {
    }
}
  • Dann können Sie wie bei Ansatz 1 weitermachen
0
quervernetzt

Ich bin schon früher auf ein ähnliches Problem gestoßen, ich würde den folgenden Ansatz verwenden, um meinen Zweck zu erreichen. Sie könnten sich darauf beziehen:

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"
  }
} 

So rufen Sie die Verbindungszeichenfolge ab:

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

Oder Sie können Ihren No-Argument-Konstruktor für Ihre DbContext wie folgt initialisieren:

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

    public BruceDbContext(string connectionString) : base(connectionString)
    {
    }
}

Dann können Sie die Instanz für Ihre DbContext wie folgt erstellen:

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

Darüber hinaus können Sie auf Lokale Einstellungsdatei für Azure-Funktionen zugreifen.

0
Bruce Chen