J'ai un problème à obtenir une DbContext
pour extraire correctement ma chaîne de connexion de mon local.settings.json
Le contexte:
System.Data.Entity.Internal.AppConfig
local.settings.json
, ce n’est pas du noyau dotnet. C'est .net 4.6.1Message d'erreur:
'La chaîne de connexion' ShipBob_DevEntities 'dans le fichier de configuration de l'application ne contient pas l'attribut providerName requis. "'
Configuration 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"
}
}
}
Versions de configuration testées:
ConnectionString
réelle: la même erreur se produitprovider
à l'intérieur de l'attribut ConnectionString
sur EntityClient
: cela n'a rien faitFaire de ShipBob_DevEntities
une valeur de chaîne = à la valeur de ConnectionString
: cela génère de nouvelles erreurs similaires
les métadonnées de mots clés ne sont pas prises en charge
J'ai essayé d'utiliser une chaîne de connexion ADO qui lève une exception code first
qui semble se produire lorsque votre chaîne de connexion est incorrecte dans une approche database first
.
J'ai pris la liberté de décompiler EntityFramework.dll
en utilisant dotPeek et j'ai retracé le problème jusqu'à System.Data.Entity.Internal.LazyInternalConnection.TryInitializeFromAppConfig
. Dans cette méthode, il existe un appel à LazyInternalConnection.FindConnectionInConfig
qui crache un objet ConnectionStringSettings
dont la valeur ProviderName
est définie sur null. Malheureusement, je suis incapable de déboguer la classe AppConfig.cs
qu'il semble utiliser pour générer cette valeur, donc je suis bloqué.
Jusqu'à présent, j'ai consulté ces deux articles. L'une d'elles indique de mettre le nom du fournisseur comme son propre jeton; Cependant, cela ne fonctionne pas.
https://github.com/Azure/azure-functions-cli/issues/193
https://github.com/Azure/azure-functions-cli/issues/46
Est-ce que quelqu'un connaît le format correct à utiliser dans local.settings.json pour une connexion Entity Framework?
Donc, la solution a fini par être triviale. L'attribut ProviderName
spécifié dans local.settings.json
DOIT être un cas de chameau.
D'après les discussions sur le hub git d'origine:
https://github.com/Azure/azure-functions-cli/issues/46
Affiche le nom du fournisseur comme étant la casse Pascal
https://github.com/Azure/azure-functions-cli/issues/193
indique le pseudo-code du nom du fournisseur en tant qu'affaire camel Il était très facile de rater mais votre section de configuration doit être exactement comme suit
"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"
}
}
Ces points sont importants:
ProviderName
est camel caseSystem.Data.EntityClient
Remarque, cette réponse suppose que vous essayez d'utiliser le constructeur sans paramètre d'un DbContext. Si vous créez un nouveau code, vous pouvez facilement suivre la deuxième réponse votée.
J'ai trouvé un moyen de contourner le problème du nom du fournisseur tout en conservant l'utilisation de la configuration du portail et donc des emplacements de déploiement. Cela implique de définir la chaîne de connexion par défaut du contexte de base de données à l'aide de propriétés statiques
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;
}
Cela implique que le développeur crée un paramètre d'application dans le portail Azure en tant qu'indicateur. Dans mon cas, il s'agit de AzureFunction. Cela garantit que notre code est uniquement exécuté dans une fonction Azure et que tous les autres clients de ce DbContext, qu'il s'agisse d'applications Web, d'applications Windows, etc., peuvent continuer à se comporter comme prévu. Cela implique également l'ajout de votre chaîne de connexion au portail Azure en tant que AppSetting et non en tant que chaîne de connexion réelle. Veuillez utiliser la chaîne de connexion complète, y compris les informations metadata, mais sans le nom du fournisseur!
Vous devrez modifier votre modèle t4 de fichier .tt généré automatiquement pour vous assurer que ce code ne sera pas remplacé si vous utilisez db en premier.
Voici un lien sur la syntaxe T4: https://docs.Microsoft.com/en-us/visualstudio/modeling/writing-a-t4-text-template
Et voici une explication sur les modèles EF T4: https://msdn.Microsoft.com/en-us/library/jj613116(v=vs.113).aspx#1159a805-1bcf-4700-9e99-86d182f143fe
J'ai parcouru plusieurs questions et réponses similaires ici. Nombre d'entre eux sont trompeurs ou supposent que tout le monde est au même niveau et comprend le fonctionnement des fonctions Azure. il n'y a pas de réponse pour les débutants comme moi. Je voudrais résumer ici ma solution étape par étape. Je ne pense pas que la réponse fournie soit la meilleure option car elle vous oblige à modifier les fichiers edmx générés automatiquement qui peuvent être écrasés par erreur ou lors de la prochaine mise à jour de votre edmx à partir de la base de données. La meilleure option consiste également à utiliser des chaînes de connexion au lieu des paramètres de l'application, à mon avis.
le plus important, c’est que nous comprenions le fichier local.settings.jsonIS PAS POUR Azure. il s'agit d'exécuter votre application dans la section locale comme son nom l'indique clairement. La solution n’a donc rien à voir avec ce fichier.
App.Config ou Web.Config ne fonctionne pas pour les chaînes de connexion de fonction Azure. Si vous disposez d'une bibliothèque de couches de base de données, vous ne pouvez pas écraser la chaîne de connexion à l'aide de l'une de ces méthodes, comme vous le feriez dans les applications Asp.Net.
Pour pouvoir travailler avec, vous devez définir votre chaîne de connexion sur le portail Azure sous le Application Settings
dans votre fonction Azure. Il y a des chaînes de connexion Là, vous devriez copier votre chaîne de connexion de votre DBContext. si c'est edmx, cela ressemblera à celui ci-dessous. Il y a un type de connexion, je l'utilise SQlAzure mais j'ai testé avec Custom (quelqu'un a prétendu ne fonctionne qu'avec custom) fonctionne avec les deux.
metadata = res: // /Models.myDB.csdl|res:// / Models.myDB.ssdl | res: //*/Models.myDB.msl; provider = System.Data.SqlClient; provider chaîne de connexion = 'source de données = [yourdbURL]; initiale catalog = myDB; informations de sécurité persistantes = True; utilisateur id = xxxx; mot de passe = xxx; MultipleActiveResultSets = True; App = EntityFramework
Ceci est généré automatiquement DbContext
namespace myApp.Data.Models
{
public partial class myDBEntities : DbContext
{
public myDBEntities()
: base("name=myDBEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
}
c'est la nouvelle classe partielle, vous créez
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());
}
}
}
var connString = ConfigurationManager.ConnectionStrings["myDBEntities"].ConnectionString; using (var dbContext = new myDBEntities(connString)) { //TODO: }
J'ai déjà rencontré un problème similaire auparavant, j'utiliserais l'approche suivante pour atteindre mon objectif, vous pouvez vous y référer:
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"
}
}
Pour récupérer la chaîne de connexion:
var connString = ConfigurationManager.AppSettings["sqldb-connectionstring"];
//or var connString = ConfigurationManager.ConnectionStrings["Bruce_SQLConnectionString"].ConnectionString;
using (var dbContext = new BruceDbContext(connString))
{
//TODO:
}
Ou vous pouvez initier votre constructeur sans argument pour votre DbContext
comme suit:
public class BruceDbContext:DbContext
{
public BruceDbContext()
: base("Bruce_SQLConnectionString")
{
}
public BruceDbContext(string connectionString) : base(connectionString)
{
}
}
Ensuite, vous pouvez créer l'instance pour votre DbContext
comme suit:
using (var dbContext = new BruceDbContext(connString))
{
//TODO:
}
De plus, vous pouvez vous référer à Fichier de paramètres locaux pour Azure Functions.