EF 5 code first - using a method to determine the connection - entity-framework

Is it possible that the connection string is redirected to a method (instead using App.config)?
(App.config is not an option because the location for a sdf can be changed)
My connection string (sdf) can change via an OpenFileDialog for instance.
The hint concerning SQLExpress was seen.
When debugging, the migration goes towards the IMigrationMetadata.Id.
Then, DataException - An exception occurred while initializing the database.
at
System.Data.Common.DbProviderServices.GetProviderManifestToken(DbConnection connection)
ProviderIncompatibleException - The provider did not return a ProviderManifestToken string.
{"An error occurred while getting provider information from the database. This can be caused by Entity Framework using an incorrect connection string. "}
at System.Data.Entity.ModelConfiguration.Utilities.DbProviderServicesExtensions.GetProviderManifestTokenChecked(DbProviderServices providerServices, DbConnection connection)
The default ctor applies a connection string that cannot be used:
Data Source=.\SQLEXPRESS;Initial Catalog=Namespace.AbacusContext;Integrated Security=True;MultipleActiveResultSets=True;Application Name=EntityFrameworkMUE
But when invoking
this.Database.Initialize( false ); //good connStr applied
within a ctor (incl. arguments), then the default ctor is automatically invoked (but not by me) and that uses an impaired connection string. See overview:
public AbacusContext( DbConnection connection, bool contextOwnsConnection ) :
base( connection, contextOwnsConnection )
{
Database.SetInitializer( new MigrateDatabaseToLatestVersion<AbacusContext, Configuration>() );
string goodConnStr = this.Database.Connection.ConnectionString;//was used for a breakpoint
this.Database.Initialize( false );
}
public AbacusContext()
{
string badConnStr = this.Database.Connection.ConnectionString;
//SQLExpress and some other stuff can be seen now
}

Related

Issue with Entity Framework 6 while connecting to SQL Server from Azure function V2

I am trying to use an existing library which is a .net library which uses EF 6.0 to connect to a database. Since Azure Functions does not have an app.config file, I am trying to set the connection string using C# code. But I am getting the following exception while connecting to the DB using my DB context:
System.ArgumentException: The ADO.NET provider with invariant name 'System.Data.SqlClient' is either not registered in the machine or application config file, or could not be loaded. See the inner exception for details.
System.ArgumentException: The specified invariant name 'System.Data.SqlClient' wasn't found in the list of registered .NET Data Providers
MyDBContext.partial.cs:
[DbConfigurationType(typeof(MyDbConfiguration))]
public partial class MyDBContext : DbContext
{
public MyDBContext (string ConnectionString)
: base(ConnectionString)
{
}
}
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetProviderServices("System.Data.SqlClient", SqlProviderServices.Instance);
SetDefaultConnectionFactory(new SqlConnectionFactory());
}
}
I have a method as following to get the DBContext. This method will be used by the library methods to get the DB context instance.
public MyDBContext GetDB( string metadata, string connectionString )
{
EntityConnectionStringBuilder b = new EntityConnectionStringBuilder();
b.Metadata = metadata;
b.ProviderConnectionString = connectionString;
b.Provider = "System.Data.SqlClient";
return new MyDBContext (b.ConnectionString);
}
When I execute a library method to load data from db from an Azure function v2, which internally calls the above method to get DB Context and then connects to actual DB. Here MyDBContext object is getting created, but when it connects to db the following exception occurs.
System.ArgumentException: The ADO.NET provider with invariant name 'System.Data.SqlClient' is either not registered in the machine or application config file, or could not be loaded. See the inner exception for details.
System.ArgumentException: The specified invariant name 'System.Data.SqlClient' wasn't found in the list of registered .NET Data Providers
I just worked on this issue but for Azure function V1.
When using EF with Azure function, you can specify connection string in 'local.settings.json' file like this:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"AzureWebJobsDashboard": ""
},
"ConnectionStrings": {
"YourEntities": {
"ConnectionString": "metadata=res://*/EF.yourModel.csdl|res://*/EF.yourModel.ssdl|res://*/EF.yourModel.msl;provider=System.Data.SqlClient;provider connection string='data source=yourServer;initial catalog=yourDB;persist security info=True;user id=yourUserID;password=yourPwd;MultipleActiveResultSets=True;App=EntityFramework'",
"ProviderName": "System.Data.EntityClient"
}
}
}
Please pay attention to 'ProviderName' attribute. Case should be exact as shown above and provider should be 'EntityClient'
Plus 'Provider Connection String' attribute of actual connection string should be in single quote (I am not sure why Microsoft did this but this is how it is supposed to be).
This will help you run your function app locally with EF without any more changes
Now for deployment in Azure.
local.settings.json does not get deployed to cloud. As its name suggests it acts as configuration file for local run.
So you need to set connection string in 'Configuration' of Azure function app on portal.
There you can specify following parameters:
Name - 'YourEntities'
value - Just Connection string part from above json file
Type - 'Custom'
Slot Settings - according to your requirement
Now if you notice there is no way to specify ProviderName here. If you try to run function now you will get error for 'missing provider name'
Here your extended DBConfiguration class comes in handy.
Create your DB configuration class as below and specify provider as EntityType
public class YourDBContextConfig : DbConfiguration
{
public YourDBContextConfig()
{
SetProviderServices("System.Data.EntityClient",
SqlProviderServices.Instance);
SetDefaultConnectionFactory(new SqlConnectionFactory());
}
}
You can create this class in same file where you have created partial class for your DBContext
Add following attribute to your Context class:
[DbConfigurationType(typeof(YourDBContextConfig))]
Also make sure your partial context class has constructor that takes connection string as parameter and supply it while initializing context:
string connString =
ConfigurationManager.ConnectionStrings["YourEntities"].ConnectionString;
using (YourEntities db = new YourEntities(connString))
{
}
This will work for deployment.

Debugging Code Called by EF Core Add-Migrations

I have an Entity Framework Core database defined in a separate assembly, using the IDesignTimeDbContextFactory<> pattern (i.e., I define a class, derived from IDesignTimeDbContextFactory, which has a method called CreateDbContext that returns an instance of the database context).
Because the application of which the EF Core database is a part utilizes AutoFac dependency injection, the IDesignTimeDbContextFactory<> factory class creates an AutoFac container in its constructor, and then resolves the DbContextOptionsBuilder<>-derived class which is fed into the constructor for the database context (I do this so I can control whether a local or an Azure-based SqlServer database is targeted, based on a config file setting, with passwords stored in an Azure KeyVault):
public class TemporaryDbContextFactory : IDesignTimeDbContextFactory<FitchTrustContext>
{
private readonly FitchTrustDBOptions _dbOptions;
public TemporaryDbContextFactory()
{
// OMG, I would >>never<< have thought to do this to eliminate the default logging by this
// deeply-buried package. Thanx to Bruce Chen via
// https://stackoverflow.com/questions/47982194/suppressing-console-logging-by-azure-keyvault/48016958#48016958
LoggerCallbackHandler.UseDefaultLogging = false;
var builder = new ContainerBuilder();
builder.RegisterModule<SerilogModule>();
builder.RegisterModule<KeyVaultModule>();
builder.RegisterModule<ConfigurationModule>();
builder.RegisterModule<FitchTrustDbModule>();
var container = builder.Build();
_dbOptions = container.Resolve<FitchTrustDBOptions>() ??
throw new NullReferenceException(
$"Could not resolve {typeof(FitchTrustDBOptions).Name}");
}
public FitchTrustContext CreateDbContext( string[] args )
{
return new FitchTrustContext( _dbOptions );
}
}
public class FitchTrustDBOptions : DbContextOptionsBuilder<FitchTrustContext>
{
public FitchTrustDBOptions(IFitchTrustNGConfigurationFactory configFactory, IKeyVaultManager kvMgr)
{
if (configFactory == null)
throw new NullReferenceException(nameof(configFactory));
if (kvMgr == null)
throw new NullReferenceException(nameof(kvMgr));
var scannerConfig = configFactory.GetFromDisk()
?? throw new NullReferenceException(
"Could not retrieve ScannerConfiguration from disk");
var dbConnection = scannerConfig.Database.Connections
.SingleOrDefault(c =>
c.Location.Equals(scannerConfig.Database.Location,
StringComparison.OrdinalIgnoreCase))
?? throw new ArgumentOutOfRangeException(
$"Cannot find database connection information for location '{scannerConfig.Database.Location}'");
var temp = kvMgr.GetSecret($"DatabaseCredentials--{dbConnection.Location}--Password");
var connString = String.IsNullOrEmpty(dbConnection.UserID) || String.IsNullOrEmpty(temp)
? dbConnection.ConnectionString
: $"{dbConnection.ConnectionString}; User ID={dbConnection.UserID}; Password={temp}";
this.UseSqlServer(connString,
optionsBuilder =>
optionsBuilder.MigrationsAssembly(typeof(FitchTrustContext).GetTypeInfo().Assembly.GetName()
.Name));
}
}
Needless to say, while this provides me with a lot of flexibility (I can switch from local to cloud database just by changing a single config parameter, and any required passwords are reasonably securely stored in the cloud), it can trip up the add-migration commandlet if there's a bug in the code (e.g., the wrong name of a configuration file).
To debug those kinds of problems, I've often had to resort to outputting messages to the Visual Studio output window via diagnostic WriteLine calls. That strikes me as pretty primitive (not to mention time-consuming).
Is there a way to attach a debugger to my code that's called by add-migration so I can step thru it, check values, etc? I tried inserting a Launch() debugger line in my code, but it doesn't work. It seems to throw me into add-manager codebase, for which I have no symbols loaded, and breakpoints that I try to set in my code show up as the empty red circle: they'll never be hit.
Thoughts and suggestions would be most welcome!
Add Debugger.Launch() to the beginning of the constructor to launch the just-in-time debugger. This lets you attach VS to the process and debug it like normal.

Is it possible to read EF Code First Connection String from Azure Role Environment

I have an Azure Worker Role which is using Entity Framework Code First (5.0) to talk to a SQL Azure database. Currently I have the connection string in the app.config of the worker role however I would like to move the connection string into the Woker Role's Role Environment settings in order to make connection string changes easier for my live services colleagues without requiring redeployment of the Azure package.
Currently I am initializing the context in the form:
protected BaseContext()
: base("name=DataStore")
{
try
{
((IObjectContextAdapter)this).ObjectContext.Connection.Open();
var storeConnection = (SqlConnection)currentDbConn;
new SqlCommand("declare #i int", storeConnection).ExecuteNonQuery();
}
catch (Exception ex)
{
currentDbConn.Close();
Trace.TraceError("Error occured while getting connection to context", ex.Message);
throw;
}
}
I haven't been able to override DbContext(string nameOrConnectionString) to be able to pull the connection string from RoleEnvironment and I've also tried creating a new SqlConnection and assigning that the Database.Connection property but there's no setter :/.
Has anyone any ideas or guidance on how this could be achieved?
Thanks in advance for taking time to look at this question.
I would suggest either create a ContextFactory class or a static Factory method on your context like this
public class BaseContext : DbContext
{
public BaseContext(string connectionString)
: base(connectionString)
{
}
public static BaseContext Create()
{
return new BaseContext(
RoleEnvironment.GetConfigurationSettingValue("connectionString"));
}
}

Entity Framework Object not allowing Connection String to be passed as parameter

I am trying to initialize an Entity object (ADO.NET EF Object), but it does not allow me to choose what connection string I want to use. I need to change connection string in order to give different access levels to users.
There are no overrides in the Entities Object, just a parameterless constructor.
If anyone can give me any pointers, it is appreciated.
If you have used the designer to generate an .edmx file for you, you will have something like below:
public MyEntities() : base("name=MyEntities", "MyEntities")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
This will by default, get the connection string from your configuration file.
What you could do in this case is set the connection string
public partial class MyEntities
{
partial void OnContextCreated()
{
//Dynamically Building a Connection String
this.Connection.ConnectionString = "myconnectionstring";
}
}
Bear in mind though that this will first use the base constructor to pull the connection string from config, then set it with your custom version, basically overriding the connection string. This is typically good when you always want a default connection string.
Another option if you want a bit more control, is pass the connection string in via the constructor as shown below:
public partial class MyEntities
{
public MyEntities(string connectionString) :
base(connectionString,"MyEntities")
{
this.OnContextCreated();
}
}
Now you are passing in the connection string down to the base class and this is the only one it will use. This does mean however that you will most often need to supply this each time.
Hope this helps...

Entity Framework - Database First without config

I'm developing a class library that deals with an exiting db using EF. I want to avoid the consumer of the class library (and .exe or a web site) to have in the *.config file the Entity connection string. I want the connection string set a run-time.
How do I set the connection string with Database First approach? There is no constructor overload that takes a connection string and when I created one (in a separate partial class) I got an "UnintentionalCodeFirstException".
I have reviewed already the following links:
Is there a way to change connection string in database first?. Its about modifying the connection string in the config file, which I want to avoid, also because it would recycle the process (in the case of a web app)
How can l use Entity Framework without App.config. Not good because it uses ObjectContext and I need the context generated when I imported the database.
There is a constructor on DbContext that takes a DbConnection, and you need to use an EntityConnection object for it:
SqlConnectionStringBuilder sqlBuilder = new SqlConnectionStringBuilder();
// Set the properties for the data source.
sqlBuilder.DataSource = "server name";
sqlBuilder.InitialCatalog = "database name";
sqlBuilder.IntegratedSecurity = true;
// Build the SqlConnection connection string.
string providerString = sqlBuilder.ToString();
var entityBuilder = new EntityConnectionStringBuilder();
// Initialize the EntityConnectionStringBuilder.
//Set the provider name.
entityBuilder.Provider = "System.Data.SqlClient";
// Set the provider-specific connection string.
entityBuilder.ProviderConnectionString = providerString;
// Set the Metadata location.
entityBuilder.Metadata = #"res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl";
using(var context = new YourDbContext(entityBuilder.ToString())){
//do stuff here
}
The important thing to note is the metadata part - "Model1" obviously needs to be replaced for your model name.
Ref: http://msdn.microsoft.com/en-us/library/bb738533.aspx
EDIT 20/02/2013 22:25
So as an addition you'll need to extend the created DbContext class with a partial class that adds a constructor to support the above code, like this:
public partial class YourDbContext
{
public YourDbContext(string connection) : base(connection) {}
}
This class needs to be in the same namespace as the DbContext that is generated by the entity framework wizard.