We have a code-first entity framework project and an associated app.config file.
The connection string points to a SQL Azure instance. When we deploy the app into a Cloud Service and ask it to auto-create the database it does so, but does not appear to run the Configuration code as we are seeing empty tables.
When I run "update-database" from a Dev VM hosted in Azure, I get the following error:
An error occurred while getting provider information from the
database. This can be caused by Entity Framework using an incorrect
connection string. Check the inner exceptions for details and ensure
that the connection string is correct.
With further details of
System.Data.Entity.Core.ProviderIncompatibleException: An error occurred while getting provider information from the database.
This can be caused by Entity Framework using an incorrect connection string.
Check the inner exceptions for details and ensure that the connection string is correct.
---> System.Data.Entity.Core.ProviderIncompatibleException: The provider did not return a ProviderManifestToken string.
---> System.MissingMethodException: Method not found: 'System.Data.Entity.Infrastructure.Interception.DbConnectionDispatcher
Yet it is the same connection string we have used in the deployed instance to create the database.
Any suggestions as to why update-database is failing?
Azure SQL Database uses SQL Logins so the user id and password are in the connection string. If you don't have Persist Security Info=True then the credentials get lost inside EF. This is something that was broken I think in EF 6.0.
I wanted to be helpful so here is the workaround we use. We implement Dispose on our DbContext and restore the connection string after the context is disposed so the migration doesn't later choke:
protected override void Dispose(bool disposing)
{
if (!_disposed)
{
_disposed = true;
var connection = Database.Connection;
var connectionString = Database.Connection.ConnectionString;
base.Dispose(disposing);
connection.ConnectionString = connectionString;
}
}
Related
I'm migrating a servicebus client application from Microsoft.Azure.ServiceBus to use the current library Azure.Messaging.ServiceBus.
The application is a Worker Process running on a virtual machine in windows azure.
The VM has a system assigned managed identity which grants it access to service bus and we have been using it successfully with the old library for over a year.
On the old library we created a client using this connection string
Endpoint=sb://MyNamespace.servicebus.windows.net/;Authentication=Managed Identity
When I put that connection string into the constructor of Azure.Messaging.ServiceBus.ServiceBusClient I get the following error
The connection string used for an Service Bus client must specify the Service Bus namespace host and either a Shared Access Key (both the name and value) OR a Shared Access Signature to be valid. (Parameter 'connectionString')
I've been trawling through documents for some time now with no progress.
Is there anyway to make this work?
Ideally I would continue to use the connection string - developer machines do not have system assigned ID's so we develop with key based connection strings and let devops swap in the correct prod connection string.
UPDATE
Following on from Jesse's answer managed identity has to go trough a separate constructor which requires a namespace instead of an endpoint and an instance of ManagedIdentityCredential.
As I mentioned not all environments where we deploy have managed aged identities, some require a SharedAccessKey based connection string.
Instead introducing new "identity type" configuration parameters into our build process I've used a factory method to parse the connection string and call the correct constructor overload. Where its a managed identity It extracts the namespace from the endpoint setting.
I Hope its useful for others
private static ServiceBusClient CreateServiceBusClient(string connectionString)
{
var cs = new DbConnectionStringBuilder();
cs.ConnectionString = connectionString;
if (cs.ContainsKey("Authentication") &&
"Managed Identity".Equals(cs["Authentication"].ToString(), StringComparison.OrdinalIgnoreCase))
{
string endpoint = cs["Endpoint"].ToString() ?? String.Empty;
if (endpoint.StartsWith(#"sb://", StringComparison.OrdinalIgnoreCase)) endpoint = endpoint.Substring(5);
if (endpoint.EndsWith(#"/")) endpoint = endpoint.Substring(0, endpoint.Length - 1);
return new ServiceBusClient(endpoint, new ManagedIdentityCredential());
}
return new ServiceBusClient(connectionString);
}
it needs the Azure.Identity package and the namespace System.Data.Common for the connection string builder.
The clients in the Azure.Messaging.ServiceBus package support connection strings only in the format that the Azure portal returns them.
The ;Authentication=Managed Identity token that you've included in your connection string is not a known token and is ignored, so the client does not have the information needed to perform authorization. A managed identity cannot be specified via connection string.
To use a managed identity, you'll use one of the constructor overloads that accepts a fully qualified namespace and a TokenCredential. An example can be found in the package Overview docs. Any of the Azure.Identity credentials can be used; you may want to take take a look at the managed identity section of the Azure.Identity overview.
Hi I posted this to the log4net user group but thought I'd post it here as well.
I'm working on a project that requires Azure MSI to connection from Azure PaaS to Azure SQL. Wondering if log4net’s ADOAppender supports this already connection mechanism already. From what I can tell it doesn't but thought I'd ask the community before extending log4net.
To support MSI apps can’t connect to a database with a connectionstring alone, they need to get an access token from Azure and then set the AccessToken property on the SqlConnection object. Like the code below is doing:
private static SqlConnection GetSqlConnection()
{
var sqlConnection = new SqlConnection(GetConnectionString());
if (sqlConnection.DataSource != "(localdb)\\MSSQLLocalDB")
sqlConnection.AccessToken = new AzureServiceTokenProvider()
.GetAccessTokenAsync("https://database.windows.net/").Result;
return sqlConnection;
}
This code is using two Microsoft nuget packages to get the access token.
Thanks!
I am writing a function to log information from every file uploaded to a blob storage account using entity framework core. When I try to connect to the azure sql db, I get the following error:
System.Private.CoreLib: Exception while executing function: BlobStorageLogging. System.Data.SqlClient: Keyword not supported: 'authentication'
I copied my connection string from the azure portal:
"DefaultConnection": {
"ConnectionString": "Server=tcp:dbserver.database.windows.net,1433;Initial Catalog=loggingdb;Persist Security Info=False;User ID={*****};
MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Authentication=\"Active Directory Integrated\";",
"ProviderName": "System.Data.SqlClient"
}
The code that is causing the error is:
var optionsBuilder = new DbContextOptionsBuilder<LoggingDBContext>();
var options = optionsBuilder.UseSqlServer(connectionString, providerOptions => providerOptions.CommandTimeout(60)).Options;
using (var context = new LoggingDBContext(options))
{
context.Database.ExecuteSqlCommand("TRUNCATE TABLE [dbo].[BlobInfo]");
I have tried removing the authentication but that resulted in an unauthorized error. Any help would be appreciated.
The exception is expected for now, check the thread.
SqlClient for .NET Core still does not support using the 'authentication' keyword in the connection string. That will happen when this issue is actually fixed.
In order to use AAD in the new version of SqlClient in .NET Core 2.2, customers need to obtain the access token themselves using ADAL.NET and then set the AccessToken property on the SqlConnection
It means in .NET Core, Active Directory Integrated authentication is unavailable, to use Active Directory password authentication, we have to obtain access token which seems not ideal in EF configuration.
The workaround is to use Connection string for SQL authentication.
I moved my API from free plan app service to a basic plan app service with custom domain and SSL certificate.
1) I see that my API app's status is "running", Authentication (AAD) is working properly
2) if I open it's api definition (i.e. */swagger/docs/v1) it IS working
3) If i try a request that does not try to access backend Azure sql db, then it is working correctly
4) If I use a request that call backend Azure SQL db (it worked before moving API to custom domain) it fails with this error:
{"The underlying provider failed on Open., StackTrace: at
System.Data.Entity.Core.EntityClient.EntityConnection.Open()\r\n at
System.Data.Entity.Core.Objects.ObjectContext.EnsureConnection(Boolean
shouldMonitorTransactions)\r\n at
System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func1
func, IDbExecutionStrategy executionStrategy, Boolean
startLocalTransaction, Boolean releaseConnectionOnSuccess)\r\n at
System.Data.Entity.Core.Objects.ObjectContext.<>c__DisplayClass651.b__63()\r\n
at
System.Data.Entity.Infrastructure.DbExecutionStrategy.Execute[TResult](Func1
operation)\r\n at
System.Data.Entity.Core.Objects.ObjectContext.ExecuteStoreQueryReliably[TElement](String
commandText, String entitySetName, ExecutionOptions executionOptions,
Object[] parameters)\r\n at
System.Data.Entity.Core.Objects.ObjectContext.ExecuteStoreQuery[TElement](String
commandText, ExecutionOptions executionOptions, Object[]
parameters)\r\n at
System.Data.Entity.Internal.InternalContext.<>c__DisplayClass141.b__13()\r\n
at System.Data.Entity.Internal.LazyEnumerator1.MoveNext()\r\n at
System.Collections.Generic.List1..ctor(IEnumerable1 collection)\r\n
at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source)\r\n
at
P..Repositories.DataAccessLayer.DbContexts.P..DbContext.GetP..Dtos()\r\n
at
P..Repositories.Repositories.P..Repository.P..Repository..ctor()\r\n
at P..API.Controllers.A..Controller.Get()"}]}
Only thing I've found so far suggests that my API can not access Azure SQL because of firewall rules, but that doesn't sound as an option, since I just assigned a custom domain to my app, I believe it is in the same "place" in Azure... and I did not manage so far to find any suggestions regarding Azure SQL db connections when migrating API to custom domains...
Any ideas?
It turns out, that by some strange reason, after moving to custom domain - publishing settings, AzureDatabase connection string had a non-existent (seams like "generated" name -> Api name with added postfix _db) database name. When I changed it to the real db name, everything started to work again...
P.S. I guess unchecking "use this connection string at runtime (update destination web.config)" would give the same result, since web.config holds the right connection string.
I have two Azure Mobile Services (.NET backend) which share the same Azure Database. Let say Service "X" and "Y". The database is created by service "X" (when it ran for the first time) and created tables "TA" with schema name "X". Then I ran service "Y" which created the same tables "TA" and "TB" in the same database but with schema name "Y".
Now I want to make service "Y" to use schema "X" to make sure both services use the same data. Inside the "TADataController" I changed the code to:
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
// MyContext context = new MyContext(Services.Settings.Name.Replace('-', '_'));
YContext context = new YContext("X");
DomainManager = new EntityDomainManager<ADataController>(context, Request, Services);
}
Also in the "YContext" class
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//string schema = ServiceSettingsDictionary.GetSchemaName();
//if (!string.IsNullOrEmpty(schema))
// modelBuilder.HasDefaultSchema(schema);
modelBuilder.HasDefaultSchema("X");
}
but when I try to access the data inside "TADataController" using Query(), I get a SqlException with message "The specified schema name "X" either does not exist or you do not have permission to use it." I doubt that permission would be the issue as both services use the same Azure Database account and also clearly the schema exists as my other service uses it! so I cannot figure out what the problem is.
I also tried:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var entityConfig = modelBuilder.Entity<UserProfileData>();
entityConfig.ToTable("UserProfileDatas", "X");
}
but the same exception was raised. There are some information on the web for transferring data to another schema which could be helpful if I wanted to transfer my data to service "Y". But I want to use both services "X" and "Y" on a shared database.
UPDATE:
I realized that ServiceSettingsDictionary.GetSchemaName() used in the OnModelCreating returns the name of the schema based on the configuration, so I uploaded the original codes:
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
MyContext context = new MyContext(Services.Settings.Name.Replace('-', '_'));
DomainManager = new EntityDomainManager<ADataController>(context, Request, Services);
}
and
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
string schema = ServiceSettingsDictionary.GetSchemaName();
if (!string.IsNullOrEmpty(schema))
modelBuilder.HasDefaultSchema(schema);
}
then in the management portal, I added one item to Configuration\App Settings with Key=MS_TableSchema and Value=X. Then debugged the code again, I realized that ServiceSettingsDictionary.GetSchemaName() returns "X" as I wanted, but again the controllers throw a SqlException with message "The specified schema name "X" either does not exist or you do not have permission to use it." when I use the Query() method.
When a mobile service named 'MSName' is created, a schema with the name 'MSName' is defined in the database, and a user is created with permission to access that schema only. That is the user in the default connection string which the mobile service will use when connecting to the database in the runtime. So if you have a service 'X' and try from it to access the service 'Y', the user it's using will not have permissions to do so.
There are a few options you can do to solve that. One is to find the user for service 'Y' (using one of the SQL server management tools) and grant it access to the schema for 'X'. Another option is to, when creating the context in the mobile service Y not to use the default connection string ("Name=MS_TableConnectionString"), but a connection string which uses the user for 'X'.
Just so that I understand -- you want your two services to access the same set of tables in the same database? Not just using the same database but the same tables?
You can easily reuse the same database but we set each mobile service using it up with a separate schema and permissions to only access that schema so that two services won't inadvertently interfere.
However, it sounds like you want them to access the same tables, right? This means that in addition to changing the schema in your service you also need to set the permissions for the mobile user in the DB to access that schema.
You can get the user using the kudu site under the Environment tab (look for the MS_TableConnectionString connection string):
https://<your service>.scm.azure-mobile.net/Env
And you can set the permissions for that user using the grant command -- you can see an example her:
https://weblogs.asp.net/fredriknormen/database-migration-and-azure-mobile-service-adventure
Hope this helps!
Henrik