How is location of |Data Directory| in connection strings resolved? - c#

If I create an asp.net project and use entity framework to create a database, something like this is automatically added to the connection strings in the web.config:
<add name="DefaultConnection" connectionString="data source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\WebAppName.mdf;initial catalog=WebAppName;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
Note that instead of a fully qualified file path, it uses |Data Directory| which, in this case, points to the 'App_Data` folder. Here's how some of the documentation explains it:
The presence of User Instance=true and AttachDBFilename=|DataDirectory| cause SqlConnectionHelper to conclude that the connection string targets SQL Server Express and triggers the database's creation. (The presence of data source=.\SQLEXPRESS in the connection string does not factor into the decision, because SqlConnectionHelper supports non-default as well as default instances of SQL Server Express.) The |DataDirectory| portion of the connection string specifies that the MDF file is located inthe App_Data directory. SqlConnectionHelper derives the database name from the MDF file name. It also creates an App_Data folder to hold the MDF if the folder doesn't already exist.
Except, if I use Entity Framework in, say, a console application, none of that is true--you'll just get an exception saying there's no file at the specified path, and it will both ignore any App_Data folder you created and fail to create one if there is none. If you remove the AttachDBFilename section altogether, it will work, but will create the database in the local output bin where the .exe file is located. Google tells me you can manually set |Data Directory| using AppDomain.SetData but apparently that's still not true for a console application (get compile error saying "An object reference is required").
So my question is, how exactly does the location of |Data Directory| get resolved? As far as I know, the fact that it differs between console apps and Asp.net apps means the resolution can't be happening solely in SQL Server Express as both are using the same installation. So is it happening in the asp.net server? Or is there a hidden settings file that gets created in asp.net projects?

Here is the code which specifies where is |DataDirectory|
GetDataDirectory
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
internal static string GetDataDirectory() {
if (HostingEnvironment.IsHosted)
return Path.Combine(HttpRuntime.AppDomainAppPath, HttpRuntime.DataDirectoryName);
string dataDir = AppDomain.CurrentDomain.GetData(s_strDataDir) as string;
if (string.IsNullOrEmpty(dataDir)) {
string appPath = null;
#if !FEATURE_PAL // FEATURE_PAL does not support ProcessModule
Process p = Process.GetCurrentProcess();
ProcessModule pm = (p != null ? p.MainModule : null);
string exeName = (pm != null ? pm.FileName : null);
if (!string.IsNullOrEmpty(exeName))
appPath = Path.GetDirectoryName(exeName);
#endif // !FEATURE_PAL
if (string.IsNullOrEmpty(appPath))
appPath = Environment.CurrentDirectory;
dataDir = Path.Combine(appPath, HttpRuntime.DataDirectoryName);
AppDomain.CurrentDomain.SetData(s_strDataDir, dataDir, new FileIOPermission(FileIOPermissionAccess.PathDiscovery, dataDir));
}
return dataDir;
}

Related

Entity Framework 6 connection string questions

I have a C# ASP.NET MVC code first project that works as intended to connect to my database. It has the connection string set in the web.config file and all seems to work great.
<connectionStrings>
<add name="HorstMFGContext" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|HorstMFG.mdf;Initial Catalog=aspnet-HorstMFG;Integrated Security=True" providerName="System.Data.SqlClient" />
</connectionStrings>
Now I need to connect to the same database from a different application that needs to be a WinForms based class library. I don't have access to the source code of the program that uses my dll because my application is just a plug-in for Autodesk Vault.
There are numerous examples such as this one that show how to set the connection string in the application that calls the dll, but that obviously won't work in my case.
This link here seems to very close to what I need, but I haven't been able to get it to work. Here is my version of the 'Create' function.
public static HorstMFGEntities Create(string nameOrConnectionString)
{
var entityBuilder = new EntityConnectionStringBuilder();
// use your ADO.NET connection string
entityBuilder.ProviderConnectionString = nameOrConnectionString;
// Set the Metadata location.
entityBuilder.Metadata = #"res://*/HorstMFG.ssdl|res://*/HorstMFG.msl";
return new HorstMFGEntities(entityBuilder.ConnectionString);
}
Here is the line that calls the function:
using (var db = HorstMFGEntities.Create(#"data source=(localdb)\MSSQLLocalDB;attachdbfilename=C:\Users\lorne\source\repos\HorstMFG\HorstMFG\App_Data\HorstMFG.mdf;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"))
{
foreach (string l in lineList3)
{
....
....
Here is the actual line that throws the exception.
// calculate material
Material mat = db.Materials.Where(m => m.StructuralCode == l.Split('\t')[6]).FirstOrDefault();
The exception message "Error: Keyword not supported: 'metadata'.
Any help to point me in the right direction is appreciated. Thanks.
I updated my code as per the first comment, and also added the 'provider' that I had missed as well.
public static HorstMFGEntities Create(string nameOrConnectionString)
{
var entityBuilder = new EntityConnectionStringBuilder();
// use your ADO.NET connection string
entityBuilder.ProviderConnectionString = nameOrConnectionString;
entityBuilder.Provider = "System.Data.SqlClient";
// Set the Metadata location.
entityBuilder.Metadata = #"res://*/HorstMFG.csdl|res://*/HorstMFG.ssdl|res://*/HorstMFG.msl";
return new HorstMFGEntities(entityBuilder.ConnectionString);
}
Now I get the error "Error: Unable to load the specified resource."
I now followed the instructions in the link you provided. I think I must be getting close, but I'm a little foggy on the metadata format yet. There seems to be a portion commented out in the example. I replaced it with this:
entityBuilder.Metadata = "res://*/HorstMFG.csdl|res://*/HorstMFG.ssdl|res://*/HorstMFG.msl";
Now I get the error: "Unable to load the specified metadata resource"
I confirmed that the files I am referencing do exist, they are in the ...\obj\Debug\edmxResourcesToEmbed\ folder in my project. I also changed the 'build action' for the 'HorstMFG.edmx' object from 'None' to 'Embedded Resource'. That didn't help anything.

What is the command to connect to a database(MS Access) saved anywhere on the PC without specifying the full path name

public void DatabaseConnection()
{
connection = new OleDbConnection(#"Provider=Microsoft.ACE.OLEDB.12.0;Data source =..\\..\\TaxApp.accdb;Persist Security Info=False");
connection.Open();
}
If you're in a language that allows you to construct the path, such as C#:
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)
will, by default, give you the path to c:\ProgramData folder, which is shared among all users.
So:
string AccessDbPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
"YourAppFolder\\TaxApp.accdb");
Usually you don't store fixed paths in your code. This will make your application very difficult to install on your customers PCs.
Knowing this, the NET framework provides an infrastructure to store this kind of informations in external XML based file (named yourexename.exe.config when you release the application and simply app.config inside your project).
You can store there this kind of information that need to be changed on a customer by customer base.
So you app.config could have a section made in this way
<appSettings>
<add key="DatabasePath" value="C:\programdata\MyApp\Database.accdb" />
</appSettings>
Inside your program you could read and use this value using this code
string dbFile = ConfigurationManager.AppSettings["DatabasePath"].ToString();
connection = new OleDbConnection(#"Provider=Microsoft.ACE.OLEDB.12.0;
Data source =" + dbFile +
";Persist Security Info=False");
You could even store the whole connection string using the project properties menu and adding a new entry of type ConnectionString in the Settings page.
Again, what you type there will be stored in the app.config file in a specific section
<connectionStrings>
<add name="MyDb" connectionString="Provider=Microsoft.ACE.OLEDB.12.0;
Data source=C:\programdata\MyApp\Database.accdb;
Persist Security Info=False";/>
</connectionStrings>
and you can read it with
string myConnectionString = ConfigurationManager.ConnectionStrings["MyDb"].ConnectionString;
See ConfigurationManager class on MSDN

Set the database location in the connection string thought AppDomain.CurrentDomain.SetData

I've found out how to use the |DataDirectory| in the connection string to set the directory where my wpf app's database file resides, but I wish to go a little further.
As stated in the msdn SqlConnection.ConnectionString Property,
The path may be absolute or relative by using the DataDirectory substitution string. If DataDirectory is used, the database file must exist within a subdirectory of the directory pointed to by the substitution string.
Now, i want to set the full location, not just the DataDirectory, with a custom DatabaseLocation attribute which is controlled by the application and thus I won't need to handle the connection string.
In other words, my connection string will go from this:
connectionString="Data Source='|DataDirectory|\database.sdf'" providerName="System.Data.SqlServerCE.4.0"
into this:
connectionString="Data Source='|DatabaseLocation|" providerName="System.Data.SqlServerCE.4.0"
and in the code, I'll read the user configuration and set the convenient data to the proper location:
AppDomain.CurrentDomain.SetData("DatabaseLocation", someVarWithDatabaseLocation);
This way I'll not need the content of the someVarWithDatabaseLocation from the user interface to the business down to the dataaccess and create a connection string.
Is this possible, or only the |DataDirectory| magic tag is treated by the connection string builder?
Thank you
Well. There isn't.
After decompiling System.Data (sorry) and analysing the available ConnectionString constructors I saw that there is only ExpandDataDirectory and it has hardcoded to the
const string DataDirectory = "|datadirectory|";
So... only datadirectoy is handled...

How to change DbEntry settings at runtime

I want to change the DB string connection I have stored in Web.config file depending on where is run the application.
I know how to read but, do anybody have an idea how to to change it?
http://geekswithblogs.net/AzamSharp/archive/2005/10/09/56457.aspx
Third link to come up on a BING search.
private void GetConfigSettings()
{
string path = Server.MapPath("Web.config");
string newConnectionString = #"(Your connection string here)";
XmlDocument xDoc = new XmlDocument();
xDoc.Load(path);
XmlNodeList nodeList = xDoc.GetElementsByTagName("appSettings");
XmlNodeList nodeAppSettings = nodeList[0].ChildNodes;
XmlAttributeCollection xmlAttCollection = nodeAppSettings[0].Attributes;
xmlAttCollection[0].InnerXml = txtKey.Text; // for key attribute
xmlAttCollection[1].InnerXml = newConnectionString; // for value attribute
xDoc.Save(path); // saves the web.config file
}
New answer based on your comment on my original answer
You can use the String.Replace function. The ConnectionString is just a string.
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["YourConnectionString"].ConnectionString;
connectionString = connectionString.Replace("TextToReplace", "ReplacementText");
so assuming your connection string is
"Data Source=LiveDbServer;Initial Catalog=MyDataBase;Persist Security Info=True;User ID=someUserId;Password=yourPassword"
You could use the following logic
if(System.Environment.ServerName == "MyTestServer")
{
connectionString = connectionString.Replace("LiveDbServer", "TestDbServer");
}
The following is just based on a guess as to why you might want to change connectionstrings at run-time based on which server the app is running on
ALTHOUGH (added bonus idea) if your goal is to always have your test web server point to your test DB server and your live web server point to your live DB server, an easier method is to edit the HOSTS file. (I'm sure there are other options, but that was a simple enough hack for us, and it works with database connections, web service references, etc.)
Simply set up the hosts file on the test machine so that it maps "MyLiveDbServer" to the ipaddress of "MyTestDbServer", and viola - your apps will point to the test server without any extra code or configuration, automatically, and for all time.
I used to load the connection string into the Application object (for better or worse) in the Application_Start event of global.asax.
Then, I get the option of checking which server I'm running on and loading that connection string into the Application object, rhather than relying on just a single entry in web.config.
Example:
<connectionStrings>
<add name="testservername" connectionString="..." providerName="..."/>
<add name="prodservername" connectionString="..." providerName="..."/>
<add name="localhost" connectionString="..." providerName="..."/>
</connectionStrings>
...in global.asax:
<%# Import Namespace="System.Configuration.ConfigurationManager" %>
...
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
Application("cn") = ConnectionStrings(System.Web.HttpContext.Current.Request.ServerVariables("HTTP_HOST"))
End Sub
So then, every time I would have used ConnectionStrings("cn"), I will instead use Application("cn").
Check to see what HTTP_HOST gives you on each server and name your connection strings accordingly.
Alternately, you could just use ConnectionStrings(System.Web.HttpContext.Current.Request.ServerVariables("HTTP_HOST")) all over instead of storing the whole string in Application.
Another option would be to just store the host name in the Application variable to pass to ConnectionStrings any time you need it.

How to connect published Visual C# solution to a different database

So here's the what's up. I just created and "published" a staff management tool in Visual C#. During development, I used a string saved in Properties.Settings.Default to connect to the database I was using for development. Now since the solution is published and ready to go, the boss wants to connect to the real staff database. I was under the impression that connection to the new database would be as simple as changing the connection string in some properties file somewhere. Unfortunately I can't seem to find the proper file/string to connect to the database I want to. Any ideas?
Thanks!
JB
Look here:
Connection Strings and Configuration Files
By using a config file you just have to change the config file connection string once your application has been deployed.
Here's a way of doing what you want:
From http://www.dreamincode.net/forums/topic/70745-connection-string-in-appconfig/
Your config file content:
<connectionStrings >
<add name="YourName"
connectionString="Provider=msdaora;Data Source=MyOracleDB;Persist Security Info=False;Integrated Security=Yes;"
providerName="System.Data.OracleClient" />
</connectionStrings>
Method to get the connection string at runtime:
public static string GetConnectionString(string strConnection)
{
//Declare a string to hold the connection string
string sReturn = new string("");
//Check to see if they provided a connection string name
if (!string.IsNullOrEmpty(strConnection))
{
//Retrieve the connection string fromt he app.config
sReturn = ConfigurationManager.ConnectionStrings(strConnection).ConnectionString;
}
else
{
//Since they didnt provide the name of the connection string
//just grab the default on from app.config
sReturn = ConfigurationManager.ConnectionStrings("YourConnectionString").ConnectionString;
}
//Return the connection string to the calling method
return sReturn;
}
Using the method:
string connectionString = GetConnectionString("YourName");
I've had to change the entry in the Properties.Settings text file, recompile and redeploy to get the new connectionstring to take. In the future consider reading your connection string from your .config file under either the ConnectionStrings or AppSettings node. When you store it there you simply need to change a production text file to switch your database...

Categories

Resources