Transform web.config won't work well for Entity Framework CodeFirst because the connection string is added dynamically after transform when publishing. We have a dynamic connection settings being set in code for CodeFirst when releasing to different environments.
I have tried the correct removal method but the published results ends up with the connection string in the web.config.
Are there any work arounds for this? Putting connection info in our web.configs isn't an option. Adding the connection info dynamically at runtime works well.
The problem is when publish adds the connection info in the web.config the app gets a 500 error because codefirst is attempting to use an invalid connection string for the environment that is not sandbox where it was created.
We are changing that at runtime here and that is working.
public MyAppDataContext()
{
this.Database.Connection.ConnectionString = OurDynamicConfigSettings.GetSetting("MyAppConnectionString");
}
The codefirst though is attempting to use what is in web.config before we set it dynamically. The fix is to remove the connection info from the web.config.
It all works except when building on build server or publishing, codefirst inserts the connection info back into the web.config every time we publish. Having to remember to remove this each time is not good practice and prone to errors if you forget.
Code for removing connection string in our transform file should work but doesn’t.
<connectionStrings>
<add xdt:Transform="Remove" xdt:Locator="XPath(configuration/connectionStrings[#name='MyAppConnectionString'])" />
</connectionStrings>
Your transform should be:
<connectionStrings>
<add xdt:Transform="Remove" xdt:Locator="Match(name)" name="MyAppConnectionString" />
</connectionStrings>
But, I have to ask.. are connection strings stored in your database or something? Why do you need to do them in code?
You could just do this:
public class MyDataContext : DbContext {
public MyDataContext(string connectionString) : base(connectionString)
...
}
Then when you create you contexts, you do this:
using(var context =
new MyDataContext(OurDynamicConfigSettings.GetSetting("MyAppConnectionString"))) {
...
}
public partial class MyContextEntities : DbContext
{
public MyContextEntities(string ConnectionString="[Your Entity Connection String Here]")
: base(ConnectionString)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
}
Replace " with ' (single quote) in connection string
Related
I am trying to move my 2 database connection strings to environment variables for security reasons. Everything works fine when I include the 2 connection strings on web.config like so:
<connectionStrings>
<clear />
<add name="DefaultConnection" connectionString="Data Source=xxxxxx" providerName="System.Data.SqlClient" />
<add name="RDSContext" connectionString="Data Source=xxxxxx" providerName="System.Data.SqlClient" />
</connectionStrings>
I then removed the 2 connection strings from web.config and created 2 environment variables as follows:
setx CUSTOMCONNSTR_DefaultConnection "Data Source=xxxxx"
setx CUSTOMCONNSTR_RDSContext "Data Source=xxxxx"
Although I now get the following error when I startup IIS and visit the web app
Server Error in '/' Application.
Cannot attach the file 'C:\Users\xxx\xxx\App_Data\BookingSystem.Models.RDSContext.mdf' as database 'BookingSystem.Models.RDSContext'.
Can anyone tell me what I am doing incorrect?
Simply naming your environment variable in a certain way doesn't mean that MVC will pick them up. You may be assuming a little more magic is happening than there actually is.
When you do ConfigurationManager.ConnectionStrings["MyConnectionStringName"].ConnectionString you're only retrieving the connection string from your web.config file, or machine.config if you happened to have defined it there. There's nothing there to tell your app to grab it from anywhere else.
Instead, you might consider creating a class to represent your configuration:
public class MyApplicationDatabaseConfiguration
{
public string ConnectionString { get; set; }
}
Any class that needs to obtain this connection string can do so by depending on an instance of that MyApplicationDatabaseConfiguration.
public class MyDatabaseRepository
{
readonly MyApplicationDatabaseConfiguration _dbConfig;
public MyDatabaseRepository(MyApplicationDatabaseConfiguration dbConfig)
{
_dbConfig = dbConfig;
}
public void DoSomethingWithTheDatabase()
{
using(var connection = new SqlConnection(_dbConfig.Connectionstring))
{
//now you can use the connection
}
}
}
Then you can load your config however you like. Ex:
MyApplicationDatabaseConfiguration dbConfig = new MyApplicationDatabaseConfiguration();
dbConfig.ConnectionString = Environment.GetEnvironmentVariable("MyApplication_MyConnectionString");
Now the MyDatabaseRepository doesn't care how you load the database config, it simply says "I need the database config". This is extremely powerful, it allows you to change out the configuration by simply changing a line of code. Need to change back to using web.config? It's as simple as dbConfig.ConnectionString = ConfigurationManager.ConnectionStrings["MyConnectionStringName"].ConnectionString;
Microsoft has taken this one step further in Microsoft.Extensions.Configuration library, which is meant for .NET Core but targeted to .NET Standard, so you can use it in your .NET Framework library if you'd like.
When I use update-database -verbose and check the connection string it is wrong, no wonder the command returns with a crazy list of errors. In app.config my conn string is:
dd name="TESTDBConnString" connectionString="data source= .; initial catalog=TESTDB;
integrated security=True" providerName="System.Data.SqlClient"
And in my context class my constructor is:
public class MyContext : DbContext, IContext
{
public MyContext() : base("TESTDBConnString")
{
}
public DbSet<TestModel> TestModel { get; set; }
}
Yet when I run the verbose command I see .\SQLEXPRESS being used as the database source and I cannot even use that in SSMS so can someone show me how to get it to simply take '.'. Thanks!
it looks like your connection string format is invalid.
from the MSDN:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="BloggingDatabase"
connectionString="Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;" />
</connectionStrings>
</configuration>
it seems like you need to specify Server and Database for current Entity Framework apps connecting to SQL SERVER
I can't comment that's why i will make a suggestion. In .Net core app you need to have a specific structure in appsettings.json
Like
{
"ConnectionStrings": {
"TESTDBConnString": "server=localhost\\SQLEXPRESS;Initial Catalog=my-DB;Integrated Security=true;Trusted_Connection=True;connect timeout=100;"
}
}
For login-password connection string a little bit different.
In this case in the context for empty constructor you can use like. And all should work when you call Update-Database.
I am using Entity Framework model-first. After finishing my site, I did a Publish. I am using Entity Framework and a database connection setup using a connection string from settings.config:
<add key="thenna"
value="server=11.3.34.45;database=montage;user id=sample;password=Test;trusted_connection=false;"/>
I have config changed server database details.
My entity framework connection string in web.config:
<add name="tickandtieEntities"
connectionString="metadata=res://*/Entityframework.Tickmarks.csdl|res://*/Entityframework.Tickmarks.ssdl|res://*/Entityframework.Tickmarks.msl;provider=System.Data.SqlClient;provider connection string="data source=DESKTOP-QD6A981\SQLEXPRESS;initial catalog=tickandtie;user id=sa;password=tickmarks;MultipleActiveResultSets=True;App=EntityFramework""
providerName="System.Data.EntityClient" />
When I change web.config file with server details I get an error
Cannot open database "tickandtie" requested by the login
How can I configure Entity Framework in web.config when I move my app to the host server? Please help me anyone
You can do this by setting the connection string on your EF Db Context at creation time, passing your setting value to your EF context.
E.g.: adding a constructor on your context, which uses the base DbContext constructor to pass the connection string:
public class MyDbContext : DbContext
{
public MyDbContext(string connString) : base(connString)
{
}
}
Which then make your context used like:
var connectionString = "" // Get the value of of your custom config file here.
var ctx = new MyDbContext(connectionString);
As stated above, you need to read your connection string value first out of your settings.config file.
In my MVC projects with a web interface, I'm used to setting the Connection String in the Web.Config file.
However, now I'm making a bog standard console application - also with a database hook, but how do I set the connection string globally for the application?
At the moment, I am setting
var dbIndex = new DBContext();
dbIndex.Database.Connection.ConnectionString =
"Data Source=USER-PC;Initial Catalog=TextProject.DBContext;" +
"Integrated Security=True;MultipleActiveResultSets=True";
but I have to set this connectionstring property every time, in all function calls. Is there a way to set a global connection string when I don't have a web.config?
So I think what your saying is that Entity Framework (I assume that is what you are using) looks for defaultConnection connection string.
You can try putting it in the app.config file as suggested by others, but I'm not sure if this will be automagically picked up by EF.
What you could do, if it doesn't work, is to create new class which inherits DbContext -
public class MyDbContext : DbContext
{
public MyDbContext() : base()
{
var cs = ConfigurationManager.ConnectionStrings["defaultConnection"]
.ConnectionString;
this.Database.Connection.ConnectionString = cs;
}
}
App.config is the equivalent to a Web.config for console or .exe programs.
Add a reference to System.Configuration on any project that you want to use the ConnectionStrings of the app.config
Add a AppConfig to your entry point project. This is the project that is executed. (e.g. console .exe)
Add the connection string in the <connectionStrings></connectionStrings> section of the app.config
Now place the connection string in your app.config
string connStr =ConfigurationManager.ConnectionStrings["ConnName"]
.ConnectionString;
App.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="ConnName" connectionString="Data Source=USER-PC;Initial Catalog=TextProject.DBContext;Integrated Security=True;MultipleActiveResultSets=True" />
</connectionStrings>
</configuration>
As others have said put your connection string in the App.config file.
Regarding Entity Framework. If you are using Entity Framework (EF) version 6.2.0 perhaps earlier EF will automatically look for a connection with the same name as the class that inherits from DbContext.
If your class is
public class MyDatabaseContext : DbContext
{
// other code
}
EF will look for a connection string with a name like this
<add name="MyDatabaseContext" ...
You can also set the name in the constructor like so
public class MyDatabaseContext : DbContext
{
public MyDatabaseContext() : base ("name=defaultConnection")
{
// other code
}
// other code
}
In this case your connection string name can be <add name="defaultConnection" ...
You can put the same element in a App.config file for the console application and access it the same way you would a Web.config.
Related: What is App.config in C#.NET? How to use it?
The solution is very simple and easy. Just right click and set the project as start-up project. entity framework by default picks the start-up project and uses the connection string inside of App.config or web.config
Please refer to this:
I wish to pass a dynamic connection string to the entity framework context. I have over 150 schemas which are identical (one per account) and I would like to select the connection as such:
ApplicationDbContext db = new ApplicationDbContext("dbName");
In theory this would be fairly easy, as I can create a connectionString and pass it as the argument for the constructor, for example:
public ApplicationDbContext(string dbName) : base(GetConnectionString(dbName))
{
}
public static string GetConnectionString(string dbName)
{
// The connectionString passed is something like:
// Server=localhost;Database={0};Uid=username;Pwd=password
var connString = ConfigurationManager
.ConnectionStrings["MyDatabase"]
.ConnectionString
.ToString();
return String.Format(connString, dbName);
}
I can connect successfully when I just pass the connection string name, but not when I generate it dynamically as below. I realize now that it's because the connection string in web.config has the providerName="MySql.Data.MySqlClient" attribute in it.
When I pass the actual connection string dynamically to the connection though, it assumes that it needs to connect to SQL Server rather than MySQL and fails due to the connection string being invalid.
The question is, how do I pass the provider name to the connection string if I am creating it dynamically?
Entity Framework 6 offers some handy subtle changes which aid in both getting MySQL working and also creating dynamic database connections.
Getting MySQL working with Entity Framework 6
First, at the date of my answering this question, the only .Net connector drivers compatible with EF6 is the MySQL .Net Connectior 6.8.1 (Beta development version) which can be found at the official MySQL website here.
After installing, reference the following files from your Visual Studio solution:
Mysql.Data.dll
Mysql.Data.Entity.EF6.dll
You will also need to copy these files somewhere where they will be accessible to the project during build time, such as the bin directory.
Next, you need to add some items to your Web.config (or App.config if on desktop based) file.
A connection string:
<connectionStrings>
<add name="mysqlCon"
connectionString="Server=localhost;Database=dbName;Uid=username;Pwd=password"
providerName="MySql.Data.MySqlClient" />
</connectionStrings>
Also add the provider, inside the <entityFramework /> and <providers /> nodes, optionally (this is an absolute must in the second part of my answer, when dealing with dynamically defined databases) you may change the <defaultConnectionFactory /> node:
<entityFramework>
<defaultConnectionFactory type="MySql.Data.Entity.MySqlConnectionFactory, MySql.Data.Entity.EF6" />
<providers>
<provider invariantName="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.Entity.EF6" />
</providers>
</entityFramework>
If you change the defaultConnectionFactory from the default sql server connection, don't forget to remove the <parameter> nodes which are nested in the defaultConnectionFactory node. The MysqlConnectionFactory does not take any parameters for its constructor and will fail if the parameters are still there.
At this stage, it's quite easy to connect to MySQL with Entity, you can just refer to the connectionString above by name. Note that if connecting by name, this will work even if the defaultConnectionFactory node still points at SQL Server (which it does by default).
public class ApplicationDbContext: DbContext
{
public ApplicationDbContext() : base("mysqlCon")
{
}
}
The it is just a matter of connecting normally:
ApplicationDbContext db = ApplicationDbContext();
Connecting to a dynamically selected database name
At this point it's easy to connect to a database which we can pass as a parameter, but there's a few things we need to do.
Important Note
If you have not already, you MUST change the defaultConnectionFactory in Web.config if you wish to connect to MySQL
dynamically. Since we will be passing a connection string directly to
the context constructor, it will not know which provider to use and
will turn to its default connection factory unless specified in
web.config. See above on how to do that.
You could pass a connection string manually to the context like this:
public ApplicationDbContext() : base("Server:localhost;...")
{
}
But to make it a little bit easier, we can make a small change to the connection string we made above when setting up mySQL. Just add a placeholder as shown below:
<add name="mysqlCon" connectionString="Server=localhost;Database={0};Uid=username;Pwd=password" providerName="MySql.Data.MySqlClient" />
Now we can build a helper method and change the ApplicationDbContext class as shown below:
public class ApplicationDbContext: DbContext
{
public ApplicationDbContext(string dbName) : base(GetConnectionString(dbName))
{
}
public static string GetConnectionString(string dbName)
{
// Server=localhost;Database={0};Uid=username;Pwd=password
var connString =
ConfigurationManager.ConnectionStrings["mysqlCon"].ConnectionString.ToString();
return String.Format(connString, dbName);
}
}
If you are using database migrations, the following step is important
If you are using migrations, you will find that the ApplicationDbContext will be passed to your Seed method by the framework and it will fail because it will not be passing in the parameter we put in for the database name.
Add the following class to the bottom of your context class (or anywhere really) to solve that problem.
public class MigrationsContextFactory : IDbContextFactory<ApplicationDbContext>
{
public ApplicationDbContext Create()
{
return new ApplicationDbContext("developmentdb");
}
}
Your code-first migrations and seed methods will now target the developmentdb schema in your MySQL database.
Hope this helps someone :)
It's now 2019 of course things have changed a bit but Franciso's example really helped me on this. This is the simplest solution I could find and the only one that actually worked. I did change it a bit from what he has shown. Follow this to completion you should end up with a working solution.
I had to change a few things. I am going to be very explicit in what has to be done and I am going to use my actual file names etc so that you don't have to guess about substitutions. Many examples are also short on how to make it work at the end. This example has everything you need to know.
This was built on visual studio 2015 Entityframework 6 using MySql server 8.0.16.0.
Unfortunately the MySql connectors and libraries are a complete mess. The 8.0.xx.0 connector / net and MySql.Data.Entity.EF6 and MySql.Data are completely useless.
I have installed Connector Net 6.10.7.0, MySql.Data.Entity.EF6 6.10.7.0, and MySql.Data 6.10.7.0. That works for me and I will vigorously oppose changing this.
This is for MySql but I really don't know why it could not work for any db.
Scenario
I have a multi tenant situation where I have a common db and multiple tentant databases, one per customer The customer id is kept in the common db for login purposes and authorizaton and the customer id directs which database to use. The client db's are all called myclientdb_x where x is the client number. myclientdb_1, myclientdb_2, myclientdb_35 and so on.
I need to dynamically switch to whatever clientdb_x the code is currently serving. There is a initial database client called myclient_0 which is the template for all of the other myclient_x databases.
Step1
I created a specific connection string in my Web.config for this it looks like this. It allows connections to the clientdb_0
<add name="DefaultClientConnection" providerName="MySql.Data.MySqlClient"
connectionString="server=localhost;user id=xxx;
password=xxxx; persistsecurityinfo=True;database=clientdb_0" />
Step2
I created a new entity called ClientDbUserUpdater using the wizard. The data entity is called
ClientDbUserUpdater.edmx
I told it to use "DefaultClientConnection" as the DB connection
I told it to save this new connection string in the Web.config
This created new entity connection string in the Web.config file and it will look like
<add name="myclient_0Entities" connectionString="metadata=
res://*/Areas.Authorizations.Models.ClientDbUserUpdater.csdl|
res://*/Areas.Authorizations.Models.ClientDbUserUpdater.ssdl|
res://*/Areas.Authorizations.Models.ClientDbUserUpdater.msl;
provider=MySql.Data.MySqlClient;provider connection string="
server=localhost;user id=xxxx;password=yyyyy;
persistsecurityinfo=True;database=myclient_0"" providerName="System.Data.EntityClient" />
You might have to dig a bit because the wizard is not good about putting in \n in appropriate places.
Notice that this connection string is fundamentally the same as the initial connection string except for its name and the fact that it has
res://*/Areas.Authorizations.Models.ClientDbUserUpdater.csdl|
res://*/Areas.Authorizations.Models.ClientDbUserUpdater.ssdl|
res://*/Areas.Authorizations.Models.ClientDbUserUpdater.msl;
The res: strings are needed by the data entity and its why you can't just send a standard connection string into the data entity.
If you try to send in the initial connection string
<add name="DefaultClientConnection" providerName="MySql.Data.MySqlClient"
connectionString="server=localhost;user id=xxx;
password=xxxx; persistsecurityinfo=True;database=clientdb_0" />
you will get an exception from
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
Step3
This new connection string is the one you need to alter. I have not tested it but I am pretty sure if change the data entity model with the wizard you will need to make this change again.
Take string:
<add name="myclient_0Entities" connectionString="metadata=
res://*/Areas.Authorizations.Models.ClientDbUserUpdater.csdl|
res://*/Areas.Authorizations.Models.ClientDbUserUpdater.ssdl|
res://*/Areas.Authorizations.Models.ClientDbUserUpdater.msl;
provider=MySql.Data.MySqlClient;provider connection string="
server=localhost;user id=xxxx;password=yyyyy;
persistsecurityinfo=True;database=myclient_0"" providerName="System.Data.EntityClient" />
and change it to:
<add name="myclient_0Entities" connectionString="metadata=
res://*/Areas.Authorizations.Models.ClientDbUserUpdater.csdl|
res://*/Areas.Authorizations.Models.ClientDbUserUpdater.ssdl|
res://*/Areas.Authorizations.Models.ClientDbUserUpdater.msl;
provider=MySql.Data.MySqlClient;provider connection string="
server=localhost;user id=xxxx;password=yyyyy;
persistsecurityinfo=True;database={0}"" providerName="System.Data.EntityClient" />
Notice that the only part changed is database=myclient_0 to database={0}
Step 4
The data entity created some code behind ClientDbUserUpdater.edmx. The file is called ClientDbUserUpdater.Context.cs.
The code is ...
namespace what.ever.your.namespace.is
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
public partial class client_0Entities : DbContext
{
public client_0Entities()
: base("name=client_0Entities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<user> users { get; set; }
}
}
Notice that this a partial class. This means you can extend this class and add a new constructor.
Add the following class.
using System;
using System.Configuration ;
using System.Data.Entity ;
namespace what.ever.your.namespace.is
{
public partial class client_0Entities : DbContext
{
public client_0Entities(string dbName) : base(GetConnectionString(dbName))
{
}
public static string GetConnectionString(string dbName)
{
var connString = ConfigurationManager.ConnectionStrings["client_0Entities"].ConnectionString.ToString();
// obviously the next 2 lines could be done as one but creating and
// filling a string is better for debugging. You can see what happened
// by looking a conn
// return String.Format(connString, dbName);
string conn = String.Format(connString, dbName);
return conn ;
}
}
}
The class adds a new constructor which allows you to get the base connection string for the data entity model which from above looks like:
<add name="myclient_0Entities" connectionString="metadata=
res://*/Areas.Authorizations.Models.ClientDbUserUpdater.csdl|
res://*/Areas.Authorizations.Models.ClientDbUserUpdater.ssdl|
res://*/Areas.Authorizations.Models.ClientDbUserUpdater.msl;
provider=MySql.Data.MySqlClient;provider connection string="
server=localhost;user id=xxxx;password=yyyyy;
persistsecurityinfo=True;database={0}"" providerName="System.Data.EntityClient" />
and modfiy it at run time to change the schema.
The String.Format() call in the new partial class swaps out the database schema name in this connection string at run time.
At this point all configuration is done.
Step 5
Now you can make it go. For better understanding of this example it is nice to know what the model looks like for this entity. It is very simple because I was just testing and trying to make it go.
Drilling down through ClientDbUserUpdater.edmx and into into ClientDbUserUpdater.tt you will find your model in modelname.cs . My model is called "user" so my file name is called user.cs
namespace what.ever.your.namespace.is
{
using System;
using System.Collections.Generic;
public partial class user
{
public int UserId { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Nullable<bool> Active { get; set; }
}
}
Now you can generally access your model like this.
client_0Entities _client_0Entities = new client_0Entities("schemaName");
and this code can be anywhere in your solution that can see class client_0Entities.
which in practice is a line similar to any of the 3 below which are connection to databases client_19, client_47 and client_68 respectively.
client_0Entities _client_0Entities = new client_0Entities("client_19");
client_0Entities _client_0Entities = new client_0Entities("client_47");
client_0Entities _client_0Entities = new client_0Entities("client_68");
the following is an actual code example that works on my system. Obviously I am going to not hard code in "client_19" but its better for demo purposes.
here is actual code with real names that works and adds a new row to the user table on database client_19
string _newSchema = "client_19"
using(client_0Entities _client_0Entities = new client_0Entities(_newSchema))
{
user _user = new user();
_user.UserId = 201;
_user.Email = "someone#someplace.com"
_user.FirstName ' "Someone";
_user.LastName = "New";
_user.Active = true;
client_0Entities.users.Add ( _user ) ;
client_0Entities.SaveChangesAsync ( ) ;
}
Hopefully this helps some people. I spent about 20 hrs looking at different solutions which simply did not work or provide enough information to complete them. As I said, finding Franciso's example allowed me to get it working.
Regards,