I am developing an MVC5 application and it is a requirements that all environmental configuration be stored outside of the application to enable simple deployment between environments. i.e. When a change is made, copy a bunch of files and don't worry about overwriting the web.config.
I have moved all the configurable settings to a SQL table which works fine, however, I am struggling to move the Connectionstrings outside of the web.config.
I am using EntityFramework within a C# MVC application. I am also using ELMAH for error handling.
I have tried executing the following from the Global.asax, but EntityFramework complains the connection string is not in the file:
private static void SetConnectionString(string connectionStringName, string connectionString)
{
var settings = ConfigurationManager.ConnectionStrings;
var element = typeof(ConfigurationElement).GetField("_bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
var collection = typeof(ConfigurationElementCollection).GetField("bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
element.SetValue(settings, false);
collection.SetValue(settings, false);
settings.Add(new ConnectionStringSettings(connectionStringName, connectionString));
collection.SetValue(settings, true);
element.SetValue(settings, true);
}
Is it possible to override the configurationmanager that Entity framework uses?
Edit: Solution.
So the solution was actually pretty simple. As i was using Code First EF I can actually pass in the connection string into the constructor:
public AdventureWorksDb(string connectionString) : base(connectionString) { }
So, with the combination of the two, loading the connectionstrings from XML and modifying the ConnectionStrings Collection at runtime and then passing the correct connection string into the DBContext constructor I have a working solution.
As Ewan mentioned, you can use the machine.config file for this purpose. You can also use the root web.config file. Both of these files are located in the %WinDir%\Microsoft.NET\Framework[64]\[FrameworkVersion]\Config\ directory.
The application web.config file will override any settings in the machine-level files. So, the solution is to exclude your connection strings from the application web.config file that you deploy in order for the application to pick them up from the machine-level file.
Related
I want to make a library that will be used by either an exe (app.config) or a website (web.config). If I create a custom configuration section, and load it using
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
CustomSection section = config.Sections.OfType<CustomSection>().FirstOrDefault();
it won't run on web site. It throws a exePath must be specified when not running inside a stand alone exe. exception, since it's not an exe. I can use something like:
CustomSection db = (CustomSection )ConfigurationSettings.GetConfig("CustomSection");
But that relies on the section being properly named. Is there a way to get all the sections and iterate through them?
I am using Entity Framework. The connection string is currently saved in the app.config file, which by default creates a new file called APPLICATION_NAME.exe.config and copies it to the output folder when building.
Now I don't want my connection string to be visible to the world when I am creating applications, so one way is to create another config file as a resource and reference it in the application.
EDIT:
I am developing using WPF, now is there any special configuration for it doing the below:
How to register an embedded config file in the application ?
Normally when you create your own DbContext, you pass the connection string like this
public MyDBContext() : base("name=Connection_String_Name_In_AppConfig")
so how to tell the DbContext classes to take the connection string from the newly created config file?
Q1. How to register an embedded config file in the Application
You do not. But nothing stops you do "program" code that takes your own configuration information from wherever you want and uses that.
Q2. How to tell DBContext classes to take the DB connection from the newly created config
file ?
You do not. But you can actually pass in an code created connection toe a DbCOntext, in case you did not see that in the documentation.
And seriously, NOTHING in here has ANY relationship to WPF, so that tag you added is just not a good smart move.
To embed a file into the assembly, select the file in the solution explorer and go to Property window then select Build Action as 'Embedded Resource' and Copy to Output Directory as 'Do not copy'. This way the XML config file will get embedded in the assembly.
Now in your Host application(here WPF), you can read the resource file content and then pass the connection string data to your DBContext.
Example to read embedded file from the assembly:
private const string strFileName = “XMLFile.config”;
var assembly = Assembly.GetExecutingAssembly();
var stream = assembly.GetManifestResourceStreamthis.GetType(), strFileName);
var doc = new XmlDocument();
try
{
if (stream == null)
{
throw new FileNotFoundException(“Couldnot find embedded mappings resource file.”, strFileName);
}
doc.Load(stream);
}
catch (Exception ex)
{
throw ex;
}
Hope this will help you
I have a sample application hosted on Github that exactly does what you want.
I want to adjust the machine keys dynamically in code during runtime, for an IIS hosted ASP.NET MVC 4 website.
The machine keys, encryption and validation keys as well as algorithms to use, are stored in a database. Instead of reading the values from the web.config file, I want to inject those values during application startup and let the system use those instead.
Is there any way to accomplish that without having to change web.config at all (to only change the in memory configuration)?
I have tried accessing the configuration section but it is marked as readonly and is also sealed, so I cannot override IsReadOnly(). However, there is a setter that is an indicator, to me, that there may be a way to potentially remove the readonly flag.
var configSection = (MachineKeySection)ConfigurationManager.GetSection("system.web/machineKey");
if (!configSection.IsReadOnly())
{
configSection.ValidationKey = _platformInfo.MachineKey.ValidationKey;
configSection.DecryptionKey = _platformInfo.MachineKey.EncryptionKey;
...
}
Is there any way to accomplish this?
The only alternative I can see is to use a custom method like AppHarbor, however I would rather stick with the built in approach if it is possible at all.
In case someone asks why I want to do that, the reason is,
this is for a large number of identical websites running in a webfarm. Hence, having non-auto-generated keys is a must (must be the same on each server). Also each website should be isolated and should not share the same keys. As all websites are identical in their physical representation, they share the same physical location. That is the reason the web.config file cannot contain application specific settings.
Edit: It would be very helpful to confirm, at least, if it is simply not possible. As said, one can use custom authentication and encryption methods which would avoid using the machine key settings altogether. Thanks.
It's ugly, but I was able to use reflection to temporarily remove the read-only bit from the config section, set the keys, then restore it:
var getter = typeof(MachineKeySection).GetMethod("GetApplicationConfig", BindingFlags.Static | BindingFlags.NonPublic);
var config = (MachineKeySection)getter.Invoke(null, Array.Empty<object>());
var readOnlyField = typeof(ConfigurationElement).GetField("_bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
readOnlyField.SetValue(config, false);
config.DecryptionKey = myKeys.EncryptionKey;
config.ValidationKey = myKeys.ValidationKey;
readOnlyField.SetValue(config, true);
There is no way to set this programmatically once the web application starts. However, it is still possible to accomplish your goal.
If each application is running in its own application pool, and if each application pool has its own identity, then check out the CLRConfigFile switch in applicationHost.config. You can use this per-application pool to inject a new level of configuration. See http://weblogs.asp.net/owscott/archive/2011/12/01/setting-an-aspnet-config-file-per-application-pool.aspx for an example of how to use this. You could set an explicit and unique <system.web/machineKey> element in each application pool's custom CLR config file.
This is the same mechanism used by Azure Web Sites, GoDaddy, and other hosters that need to set default explicit machine keys on a per-application basis. Remember to ACL each target .config file appropriately for the application pool which will be accessing it.
Inside running code, probably not.
But, in another application like a Console, I solved using this code:
private static void ChangeWebConfig(string validationKey, string decryptionKey, string webConfigPath)
{
ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap();
configFileMap.ExeConfigFilename = webConfigPath;
System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);
MachineKeySection section = (MachineKeySection)config.GetSection("system.web/machineKey");
section.ValidationKey = validationKey;
section.DecryptionKey = decryptionKey;
config.Save();
}
I have a desktop WPF application which uses Entity Framework 4.1 Code First approach to manage data.
EF adds a connection string to the App.config file and I wan't to be able to change this connection string at runtime.
Scenario is like this:
When I deploy the application it has a default connection string in the App.config file. The user runs the application and since the connection string will probably be invalid for the user (because of server name, user id and password) I will display a server configuration window.
Here user will enter the valid information about his connection and press OK. I will then be able to change the App.config file and save the user's new valid information to the file.
Problems:
If I change it using ConfigurationManager, the changes will be temporary meaning that the file is not saved, changes are made in memory.
If I read the App.config file into a stream, make required changes in the memory, delete physical file and save the in memory stream as App.config again, Windows will not let me make changes to files under ProgramFiles folder.
What is would be the best approach here?
EDIT: Problem Again!
After I modify the App.config file with this method:
private void ApplyChangesToConnectionString()
{
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var connectionStringsSection = (ConnectionStringsSection)config.GetSection("connectionStrings");
connectionStringsSection.ConnectionStrings["SomeUniqueName"].ConnectionString = GetChangesAppliedConnectionString(connectionStringsSection.ConnectionStrings["SomeUniqueName"].ConnectionString);
config.Save(); // This line throws an exception
ConfigurationManager.RefreshSection("connectionStrings");
}
config.Save(); method call throws an error saying
"Access to "C:\Program Files (x86)\MyCompany\MyApp\MyApp.exe.config"
is denied."
I know that files under "Program files" are immutable, so how can I handle this?
I couldn't modify ConfigurationManager.ConnectionStrings["key"] object, because it is readonly.
So I decided to add a new connection string to my App.config file so it looks like this:
<connectionStrings>
<add name="SomeUniqueName" connectionString="Data Source=(local)\SQLExpress;Initial Catalog=MyDb;User Id=sa;Password=password; MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
and then changed my DbContext constructor to take newly added connection string like this:
public MyContext()
: base("name=SomeUniqueName")
{
}
Here, the value of name attribute at connection string and constructor must match.
Then to change this newly added connection string at runtime I used a method like this:
private void ApplyChangesToConnectionString()
{
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var connectionStringsSection = (ConnectionStringsSection)config.GetSection("connectionStrings");
connectionStringsSection.ConnectionStrings["SomeUniqueName"].ConnectionString = GetChangesAppliedConnectionString(connectionStringsSection.ConnectionStrings["SomeUniqueName"].ConnectionString);
config.Save();
ConfigurationManager.RefreshSection("connectionStrings");
}
App.config is not the proper place to do this since it's a global configuration used by the application.
I recommend you to save the settings per user. See this related question : c# - approach for saving user settings in a WPF application?
App.config is the correct approach in my opinion, however I wouldn't rely on writing the file physically yourself. Instead, allow the framework to do the heavy lifting for you.
Check out the sample here.
EDIT: Glad Sandeep's comment above has helped you. Feel free to check out that link too if you want a bit more information!
I have DataClasses.dbml file in my C# winforms project. This automatically adds a setting of type ConnectionString to Settings file of the project. The connection string throughout the project is accessed using this setting.
Now when I work on my PC, it connects to the database and works fine. But how to set a new connection string depending on client's host and instance names in the settings file permanently and for once (during setup).
I tried doing:
Settings.Default.ConnectionString = "SqlConnectionString";
Settings.Default.Save();
But it gives a compile-time error that its Read-Only.
My only aim is to set the connectionstrings according the clients setting. I dont want to make it hard coded.
Add a partial class definition like the following
public partial class DataClasses
{
partial void OnCreated()
{
Connection.ConnectionString = SQLHelpers.GetConnectionStr();
}
}
where SQLHelpers.GetConnectionStr should lookup the settings from the users App.Config file.
Remember to put this in a separate file to your auto-generated dbml file.