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

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);

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.

Lost Session variable when use ConfigurationManager.RefreshSection()

I have a website and have multi domain point to this.
In my website code when use access to web, I use ConfigurationManager to change Connect string base every domain. Code bellow
Session["Key"] = "KeyID";
var connectionStringsSection = (ConnectionStringsSection)configuration.GetSection("connectionStrings");
string fullConnect = "New String Connnect";
connectionStringsSection.ConnectionStrings["Name"].ConnectionString
= fullConnect;
configuration.Save();
ConfigurationManager.RefreshSection("connectionStrings");
But I lost every Session variable initialization before Connect string change (like Session["Key"]).
How can i do that but kip my Session variable?.
If you update connection string in Web.config file then session will surely destroy. To prevent this, set multiple connection strings in Web.config file and whichever connection string you want, you can manage it in .cs file.
<connectionStrings>
<add name="String1"
connectionString="CONNECTION STRING1"
providerName="System.Data.SqlClient" />
<add name="String2"
connectionString="CONNECTION STRING2"
providerName="System.Data.SqlClient" />
</connectionStrings>
You can get connection in code using "ForEach" loop.
foreach (ConnectionStringSettings cstring in System.Configuration.ConfigurationManager.ConnectionStrings)
{
//use cstring.name
}

ProviderIncompatibleException when running first time. But, not in second time

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.

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>

How to store connection string in WinForms application?

I have an application that consists of two forms. One form displays data returned from the database in fields and the other form opens a windows that allows the user to select which database to get the data from.
Currently, the application doesn't store the user's choice of database. I want to store what the currently selected connection string is each time the user selects the database they want to use in form2.
What is the best way to do this? If I made an instance of an object of a static class to store this information, would that persist the data for use on each form?
You should have an app.config configuration file, and in there, define a <connectionStrings> section:
<configuration>
<connectionStrings>
<add name="YourNameHere"
connectionString="server=.;database=AdventureWorks;Integrated Security=SSPI"/>
</connectionStrings>
</configuration>
You then add a reference to System.Configuration to your project, and then you can easily retrieve those connection strings:
string connStr = ConfigurationManager.ConnectionStrings["YourNameHere"].ConnectionString;
using(SqlConnection connection = new SqlConnection(connStr))
{
// do something here....
}
You could store the connection string in App.config and retrieve it like this:
string connStr = ConfigurationSettings.AppSettings["ConnectionString"];
public SqlConnection conn = new SqlConnection(connStr);
Example App.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
</configSections>
<connectionStrings>
<add name="ConnectionString" connectionString="Data Source=./SQLEXPRESS;Initial Catalog=DB;Integrated Security=SSPI;" providerName="Microsoft.SqlClient" />
</connectionStrings>
</configuration>
I would be tempted to use Application Settings for this purpose.
http://msdn.microsoft.com/en-us/library/aa730869(v=vs.80).aspx
This is a recommended place to keep your connection strings :-)
Though there are built-in .NET capabilites to store user related information (via Registry, config files, settings etc.) they seem to be too heavy.
I would recommend to use plain text file and keep it in user folder:
var userPath = Environment.GetFolderPath(Environment
.SpecialFolder.ApplicationData);
var filename = Path.Combine(userPath, "mysettings");
// Read connection string
var connectionString = File.ReadAllText(filename);
// Write connection string
File.WriteAllText(filename, connectionString);
Also note that hardly users will have fun working with connection strings. They would prefer to specify database name, server, username etc. using separate form fields. To map those fields to connection string you may use SqlConnectionStringBuilder class (if you are working with MSSQL Server):
// to connection string
var connectionStringBuilder1 = new SqlConnectionStringBuilder();
connectionStringBuilder1.DataSource = "server";
connectionStringBuilder1.InitialCatalog = "database";
var connectionString = connectionStringBuilder1.ConnectionString;
// from connection string
var connectionStringBuilder2 = new SqlConnectionStringBuilder(connectionString);
var serverName = connectionStringBuilder2.DataSource;
var databaseName = connectionStringBuilder2.InitialCatalog;

Categories

Resources