ProviderIncompatibleException when running first time. But, not in second time - c#

So, basically I'm using EF with Reverse POCO Code First Generator in my project.
My connection Strings look like this:
<connectionStrings>
<add name="HistoryDBContext" connectionString="" providerName="System.Data.SqlClient" />
<add name="ProjectMgtContext" connectionString="data source=xxx;initial catalog=xxx;user id=xx;password=xxx;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
</connectionStrings>
The idea is that I am going to read the connectionString for HistoryDBContext from ProjectMgtContext (which is working fine) and then rewrite the connectionString from HistoryDBContext using:
public static void WriteConnectionString(string connectionString)
{
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var connectionStringsSection = (ConnectionStringsSection) config.GetSection("connectionStrings");
connectionStringsSection.ConnectionStrings["HistoryDBContext"].ConnectionString = connectionString;
config.Save();
ConfigurationManager.RefreshSection("connectionStrings");
}
However, this only works the second time that I run my project because during the first run, it complains that the connectionString is empty:
So, obviously my approach is not working. How should I approach this?
Thank you
Edit
More code:
while (ProjectManager.HaveProjects())
{
var project = ProjectManager.GetNextProject();
if (project == null) continue;
/*Write the current project to the configuration file*/
Connection.WriteConnectionString(project.ConnectionString);
...
}

Well most likely it won't work this way. EF might not re-read your connection string from configuration every time it needs it. It might as well cache either whole configuration or just connection string in memory. App.config is for connection strings that are not changing often during application runtime (or better at all). Just pass connection string to your ProjectMgtContext directly, or do like this:
public partial class ProjectMgtContext : DbContext {
public static string DefaultConnectionString;
public TestEntities()
: base(DefaultConnectionString)
{
}
And then update DefaultConnectionString static property. If you generate your context from template - edit template to add default connection string as above.

Related

Why does my connection string not work that I added in my App.config?

I have my connection string set up in the app config but I can not get it to find that connectionstring or even see that the connection name currency is in the collection.
I tried to add a version.
I have tried to adding the provider name to the string.
I have moved it in and out and in again of the path where the database and solution are located.
I have carefully made sure that I was pointed to the right location on my computer.
<connectionStrings>
<!-- CHANGE THIS to match where your solution and sqlite database are -->
<add name="currency" connectionString="Data Source=F:\CurrencyExercise\currency.db" />
</connectionStrings>
And then the code that calls the above.
public BaseDataAccess(string connectionName = "currency")
{
_connectionName = connectionName;
Connection = new SQLiteConnection(ConfigurationManager.ConnectionStrings[_connectionName].ConnectionString);
Connection.Open();
}
I would expect the code to actually pull back a SQLiteConnection object rather than erroring out.

How do I get a list of all connection strings in app.config

In need to create an small console app, that takes two arguments:
File location for app.config file.
passkey
My problem is that the console app needs to read the connectionStrings and Encrypt it and then save the encrypted text to the config file. I have looked, but have not found any solution for it.
My app.config file could look like this:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="Conn1" connectionString="Data Source=Database1;Initial Catalog=DB12;User ID=User1234;Password=Qwerty123" providerName="System.Data.SqlClient" />
<add name="Conn2" connectionString="Data Source=Database2;Initial Catalog=DB12;User ID=User1234;Password=Qwerty123" providerName="System.Data.SqlClient" />
</connectionStrings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
</configuration>
I have the encryption part are done. I just need to in this example to read those two connectionstrings and encrypt them each. Please note that the name of the connectStrings are different every time, since the console app are triggered by TFS build server.
Basically you don't really need the name of the Connection String but the connection itself. I believe you just need to go thru them with foreach and for each iteration you can get the connectionString till the last one in the app.config.
foreach (System.Configuration.ConnectionStringSettings css in System.Configuration.ConfigurationManager.ConnectionStrings)
{
string connectionString = css.ConnectionString;
// encryption part
// rewriting the connectionString in the app.config or however you want ot be done
}
You mentioned you got the encryption part done and all you need is reading the strings.
Hope it helps!
If you donĀ“t know the name of the connection string, you could try it like this:
ConnectionStringSettingsCollection connections = ConfigurationManager.ConnectionStrings;
if (connections.Count != 0)
{
//go trough all available ConnectionStrings in app.config
foreach (ConnectionStringSettings connection in connections)
{
//reading the ConnectionString
string conString = ConfigurationManager.ConnectionStrings[connection.Name].ConnectionString;
//writing the ConnectionString
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
config.ConnectionStrings.ConnectionStrings[connection.Name].ConnectionString = EncryptConfig(conString); //just call your Encryption part here instead of "EncryptConfig()"
config.Save(ConfigurationSaveMode.Modified, true);
ConfigurationManager.RefreshSection("connectionStrings");
}
}
Use ConfigurationManager class like
ConfigurationManager.ConnectionStrings["Conn2"].ConnectionString
came up with this to work with both app and web, note i did also need to change the values and save them to disk so couldn't just loop through
var appConfig = System.Web.HttpContext.Current == null
? ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)
: System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~");
var needsToSave = false;
foreach (var appCon in appConfig.ConnectionStrings.ConnectionStrings.Cast<ConnectionStringSettings>())
{
//do stuff
}

How to keep the "Provider Connection String" separate from the EF (EDMX) metadata information?

I would like to be able to handle the actual Connection String and the EF metadata connection string separately in the config (app.config, web.config) - the goal is to make the actual data-source more apparent.
I have complete control over creating the EF Context (yay IoC!), but don't want to hard-code any of the values. Preferably something like:
<add name="EfEntities"
connectionString="metadata=res://*/..;provider connection string=NO;.." />
<add name="EfEntities_Provider"
connectionString="data source=SERVER;initial catalog=DB;user id=ME" />
It would also be acceptable, and possibly better, is the deployed configs had only the conglomerated EF connection string - as long as it was built from separate properties in the source configs. (But this level of config transformation escapes me.)
I'm fine living with the conglomerated EF "connection string" for the Designer / Updater - the EDMX is in a separate project from the usage in the target assemblies that are deployed.
In my projects, I build the EF connection string myself in code and pass that to the Context object's constructor, like so:
void InitContext() {
String actualConnectionString = ConfigurationManager.ConnectionStrings["server123"].ConnectionString;
String efConnectionString = String.Format(CultureInfo.InvariantCulture, "metadata=res://*/..;provider connection string={0};.", actualConnectionString);
_context = new MyEFContext( efConnectionString );
}
I've decided to "go" with the idea behind Dai's answer. There may be problems with it (eg. the password better not contain a quote); but it works here on tests and feels more "visible" in config transformations.
The code looks about like:
var edmxMetadata = GetConnectionStringSetting("EfEntities_Metadata");
var providerConnection = GetConnectionStringSetting("EfEntities_Provider");
var edmxCS = string.Format(#"{0};provider={1};provider connection string=""{2}""",
edmxMetadata.ConnectionString,
providerConnection.ProviderName,
providerConnection.ConnectionString);
// (and by and by)
var context = new EfEntities(edmxCS);
Where:
<!-- just the metadata -->
<add name="EfEntities_Metadata"
connectionString="metadata=res://*/EfEntities.csdl|res://*/EfEntities.ssdl|res://*/EfEntities.msl"
providerName="System.Data.EntityClient" />
<!-- just the provider connection string -->
<add name="EfEntities_Provider"
connectionString="data source=SERVER;initial catalog=DB;user id=ME"
providerName="System.Data.SqlClient" />

Customize DbContext using connection string from ServiceConfiguration (Azure)

I am using the entity-framework with Code First approach and I have a custom DbContext.
My connectionstring is defined in the ServiceConfiguration.Cloud.cscfg and ServiceConfiguration.Local.cscfg files.
public CustomContext()
: base("dbActivity")
{
...
When the above code executes, it tries to check my web.config and not the ServiceConfiguration file from an azure solution.
UPDATE:
Here is my connectionString in the ServiceConfiguration file:
<ConfigurationSettings>
<Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="UseDevelopmentStorage=true" />
<Setting name="dbActivity" value="Server=tcp:xxx.database.windows.net,1433;Database=ra;User ID=user;Password=password;Trusted_Connection=False;Encrypt=True;Connection Timeout=30;" />
</ConfigurationSettings>
Exception:
No connection string named 'Server=tcp:xxx.database.windows.net,1433;Database=ra;User ID=user;Password=password;Trusted_Connection=False;Encrypt=True;Connection Timeout=30;' could be found in the application config file."
Is it possible to make it check the ServiceConfiguration.Local.cscfg instead?
Finally I found the solution. It's necessary to use the SqlConnectionStringBuilder class.
public RAActivityContext()
: base((new SqlConnectionStringBuilder(
CloudConfigurationManager.GetSetting("dbRAActivity")).ToString()))
{
}
There is an overloaded constructor that takes an actual connection string.
This should do the trick:
public CustomContext()
: base(CloudConfigurationManager.GetSetting("dbActivity"))
{
// stuff here
}
Of course, the setting contains the (full) connection string.
I don't know it you can directly load it from a different config but there is a constructor for dbcontext that takes a connection string as input.
string connectionString = LoadFromConfig();
var context = new CustomContext(connectionString );
I needed to do this, but wasn't able to change the actual DbContext constructor as it was in a shared library that was also used by projects that used an app.config. I ended out coming up with an alternative solution which was to programatically modify the app.config connection string inside the worker role itself.
See this for a way to do that: Change connection string & reload app.config at run time
The code I used was
string databaseConnectionString = CloudConfigurationManager.GetSetting("dbActivity");
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConnectionStringsSection connectionStringsSection = (ConnectionStringsSection) config.GetSection("connectionStrings");
connectionStringsSection.ConnectionStrings["dbActivity"].ConnectionString = databaseConnectionString;
config.Save();
ConfigurationManager.RefreshSection("connectionStrings");
For this to work you need to have an empty connection string inside the app.config for which it will overwrite
<connectionStrings>
<add name="dbActivity" connectionString="" providerName="System.Data.SqlClient" />
</connectionStrings>

%APPDATA% in connection string is not substituted for the actual folder?

When using WPF and entity-framework I have an APP.CONFIG that looks like the following:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="DatabaseEntities" connectionString="metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl;provider=System.Data.SqlServerCe.4.0;provider connection string="Data Source=%APPDATA%\Folder\Database.sdf"" providerName="System.Data.EntityClient" />
</connectionStrings>
</configuration>
When using this code it always throws the following error:
System.Data.EntityException: The underlying provider failed on Open. ---> System.Data.SqlServerCe.SqlCeException: The path is not valid. Check the directory for the database. [ Path = %APPDATA%\Folder\Database.sdf ]
When I run the path "%APPDATA%\Folder\Database.sdf" from the command prompt it works fine, and if I remove "%APPDATA% and hardcode the path it works fine - so it looks simply like the %APPDATA% is just not being substituted for the actual folder...
Thanks,
As you already reallized, %APPDATA% or any other environtment variables are not replaced with their respective value in connection strings. Environment varialbes are something related to the operating system shell. They work in command prompt because the command prompt explicitly parses the values entered and substitutes environment variables. That's not something that .NET Framwork usually performs.
To achive this, you have to manually provide the value for %APPDATA% (using Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) or Environment.GetEnvironmentVariable("APPDATA")). There are two options:
Change your connection string and use |DataDirectory|:
<connectionStrings>
<add name="DatabaseEntities" connectionString="metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl;provider=System.Data.SqlServerCe.4.0;provider connection string="Data Source=|DataDirectory|\Database.sdf"" providerName="System.Data.EntityClient" />
</connectionStrings>
(Notice the use of |DataDirectory| in the path to the database file.)
Then provide the value for |DataDirectory| in your application's Main method:
AppDomain.CurrentDomain.SetData("DataDirectory",
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData));
Refer to this MSDN page for more information.
Manually provide the connection string for your ObjectContext class. This way you can parse and change the connection string:
public static string GetConnectionString()
{
var conStr = System.Configuration.ConfigurationManager.ConnectionStrings["DatabaseEntities"].ConnectionString;
return conStr.Replace("%APPDATA%",
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData));
}
And later:
var db = new DatabaseEntities(GetConnectionString());
Or subclass you ObjectContext class and always use the new connection string:
public class MyDatabaseEntities : DatabaseEntities
{
public MyDatabaseEntities()
: base(GetConnectionString())
{
}
public static string GetConnectionString()
{
var conStr = System.Configuration.ConfigurationManager.ConnectionStrings["DatabaseEntities"].ConnectionString;
return conStr.Replace("%APPDATA%",
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData));
}
}
and use the new class anywhere.
I have another option. We don't need to replace anything. I am using below connection string without any replace and it's working fine.
<connectionStrings>
<add name="ProjectManagementDBEntities" connectionString="metadata=res://*/Models.ProjectManagementModels.csdl|res://*/Models.ProjectManagementModels.ssdl|res://*/Models.ProjectManagementModels.msl;provider=System.Data.SqlClient;provider connection string="data source=(LocalDB)\MSSQLLocalDB;attachdbfilename=|DataDirectory|\ProjectManagementDB.mdf;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient"/>
</connectionStrings>
Main change is data source=(LocalDB)\MSSQLLocalDB;attachdbfilename=|DataDirectory|\ProjectManagementDB.mdf;integrated security=True;
I hope this will save someone.
You have to replace the %APPDATA% in the code with the relative path -
var connectionString = ConfigurationManager.ConnectionStrings["DatabaseEntities"]
.ConnectionString;
connectionString.Replace("%APPDATA%",
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);

Categories

Resources