Declaring per-user (roaming user) configuration settings for a plugin DLL? - c#

How can I setup support for per (roaming) user configuration settings for a particular plugin .DLL loaded from another application?
I have a .DLL that is loaded as add-in/plugin from another application, and want to persist configuration settings particular for this one independently of the main application that loads it, based on machine, .dll (=executable), roaming user or user profile.
I have found the System.Configuration.ExeConfigurationFileMap class that looks likely to provide what I need, but I can't figure out how to setup the right paths specific for my (plugin) application.
What code I have so far is:
public class MyConfigurationSettings : ConfigurationSection
{
public static MyConfigurationSettings GetSection (ConfigurationUserLevel ConfigLevel)
{
string configFile = Assembly.GetAssembly(typeof(MyConfigurationSettings)).Location + ".config";
ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap();
configFileMap.ExeConfigFilename = configFile;
configFileMap.LocalUserConfigFilename = <localUserConfigFile>; // ??? What filename to place here and how to get it based on the current environment ???
configFileMap.RoamingUserConfigFilename = <roamingUserConfigFile>; // ???;
System.Configuration.Configuration Config = ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigLevel);
// ...
}
}
Can anyone point me into the right direction? The available documentation and search results are too confusing or insufficient for me to get this right. Sorry , if this seems to be a silly question, but my C# (.NET) skills are going to get rusty after 4+ years not using it for earning daily bread.
I also believe it's not primarily an issue about configuration settings management, but how to get paths for installation specific application instance configurations.

You set RoamingUserConfigFilename = RoamingName.config and put it under Roaming Profile:
%AppData%\[AppName]\[Vendor]\[CodedPath]\[Version]\RoamingName.config
Also you set LocalUserConfigFilename = LocalName.config and put it under Local Profile:
%LocalAppData%\[AppName]\[Vendor]\[CodedPath]\[Version]\LocalName.config
Now calling
ConfigurationManager.OpenMappedExeConfiguration(
exeMap,
ConfigurationUserLevel.PerUserRoamingAndLocal);
config will be read in the following order:
Source on MSDN blogs.
For samples search for User.config under c:\Users\[User]\AppData. Also see CP article.
Code Sample:
public static MyConfigurationSettings GetSection (ConfigurationUserLevel ConfigLevel)
{
try
{
string appDataPath = System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string localDataPath = System.Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
System.Configuration.ExeConfigurationFileMap exeMap = new ExeConfigurationFileMap();
exeMap.ExeConfigFilename = System.IO.Path.Combine(appDataPath, #"MyCompany\MyPlugin\Default.config");
exeMap.RoamingUserConfigFilename = System.IO.Path.Combine(appDataPath, #"MyCompany\MyPlugin\Roaming.config");
exeMap.LocalUserConfigFilename = System.IO.Path.Combine(localDataPath, #"MyCompany\MyPlugin\Local.config");
System.Configuration.Configuration Config = ConfigurationManager.OpenMappedExeConfiguration(exeMap,ConfigLevel);
return (MyConfigurationSettings)Config.GetSection("MyConfigurationSettings");
}
catch (Exception ex) {
// ...
}
return null; // or throw an appropriate exception
}

Generally load the configuration from the special folder where you put it in and put it into one of the roaming (i.e. non local) locations. The OS handles the rest as per roaming specifications.
The usage of ConfigSections is totally irrelevant unless there is a very very special need to use the config file for that. In any .NET project I have seen in the last 10 years this file was never used for user specific settings.

Related

How can i save files in folder within the IIS of outside of the Application folder in asp.net

In my web application, i have some files those are saving within application it's creating a folder for saving files but i need to save those file outside of the application and inside of IIS.how can i do this?
With in application Folder we are using below code
Server.MapPath(Path)
For Saving in IIS How can i Write?
Thank you
you need to create a virtual directory that points ti the folder outside.
Go to IIS right click on your website. click on Add Virtual directry from the menu.Give an alias for the directory select your desired folder and you are done. it will consider this outside folder as an internal folder and work the same way. check this link How to: Create and Configure Virtual Directories in IIS 7.0
Disclaimer: but you will have to do this after hosting to iis i.e publishing. while using visual studio in dev environment i.e debugging it will store in internal directories only
Edit: for creating virtual directories this is the code. I have not tested its validity.
static void CreateVDir(string metabasePath, string vDirName, string physicalPath)
{
// metabasePath is of the form "IIS://<servername>/<service>/<siteID>/Root[/<vdir>]"
// for example "IIS://localhost/W3SVC/1/Root"
// vDirName is of the form "<name>", for example, "MyNewVDir"
// physicalPath is of the form "<drive>:\<path>", for example,"C:\Inetpub\Wwwroot"
try
{
DirectoryEntry site = new DirectoryEntry(metabasePath);
string className = site.SchemaClassName.ToString();
if ((className.EndsWith("Server")) || (className.EndsWith("VirtualDir")))
{
DirectoryEntries vdirs = site.Children;
DirectoryEntry newVDir = vdirs.Add(vDirName, (className.Replace("Service", "VirtualDir")));
newVDir.Properties["Path"][0] = physicalPath;
newVDir.Properties["AccessScript"][0] = true;
// These properties are necessary for an application to be created.
newVDir.Properties["AppFriendlyName"][0] = vDirName;
newVDir.Properties["AppIsolated"][0] = "1";
newVDir.Properties["AppRoot"][0] = "/LM" + metabasePath.Substring(metabasePath.IndexOf("/", ("IIS://".Length)));
newVDir.CommitChanges();
}
else
}
catch (Exception ex)
{
}
}
Normally you can not create a folder outside the root path i.e. if you have your application in say C:\inetpub\testapp you can only create a folder inside testapp. This restriction is for security reason where a web server is not supposed to allow access to anything above root folder.
Moreover it's not recommended to write any folders/files in the root folder as writing to root folder cause appdomain to recycle after certain number of writes (default is 15) causing session loss. See my answer here.
However there is a workaround
Add a path of your server to web.config and then fetch it in your code.Use something like below in the appsettings section of web.config
<add key="logfilesPath" value="C:\inetpub\MyAppLogs" />
Create a folder of above path and add Users group to your folder and give that group full permission (read/write). (Adding permission is very important)
In your code you can fetch as below
string loggerPath = (ConfigurationManager.AppSettings["logfilesPath"]);
Hope this helps

C# app.config read and write

Can we save data or some text in app.config file
if yes then it is persistence or temporary ?
for example if i store last access date time in app.config file and then close/Exit the application after the some time/days/years when i start my application is it possible that the last access date time I can retrieve. If yes then how please explain with code ....
Thanks,
Raj
here is my code ..
Trying to retrieve date time from config file..
But show error object not set to be an object like...
System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
if (email.To.Contains(Email) && DateTime.Compare(email.UtcDateTime.Date, Convert.ToDateTime(config.AppSettings.Settings["ModificationDate"].Value)) > 0)
Here i store /save the date time in app.config file.
System.Configuration.Configuration config =
ConfigurationManager.OpenExeConfiguration
(ConfigurationUserLevel.None);
// Add an Application Setting.
config.AppSettings.Settings.Add("ModificationDate", DateTime.Now + " ");
// Save the changes in App.config file.
config.Save(ConfigurationSaveMode.Modified);
// Force a reload of a changed section.
ConfigurationManager.RefreshSection("appSettings");
Console.WriteLine("Last Update :"+config.AppSettings.Settings["ModificationDate"].Value);
Console.ReadLine();
Please suggest me why it show me an error that object not set am done any mistake please ans...
You can create a settings xml file to do this.
In VS go to your project properties -> Settings, then write a value in.
In code, you can get/set that value using
Properties.Settings.Default.YourVariable = 0;
If you are setting the value make sure to save it
Properties.Settings.Default.Save();
See here for a good tutorial.
I would use a custom configuration section to create your configuration type - last access date time. you can create more complex hierarchy configuration and strongly-typed.
Again, it depend on your requirement and design if you need to do that. otherwise, standard file storage(txt/xml) would also allow you to do that. I personally normally using app.config for application specific level(appearance/fonts/servername etc.) configuration rather than transactional configuration.
for e.g
public class LastAccessConfigurationSection : System.Configuration.ConfigurationSection {
[ConfigurationProperty("LastAccess")]
public string LastAccess{
get { return (string)this["LaunchTime"]; }
set { this["LaunchTime"] = value; }
}
}
you can have a static class to manage the life-cycle that would allow you to persist the change.
public static LastAccessConfigurationSection Config { get; internal set; }
public static void Initialize() {
Config = ConfigurationManager.GetSection("LastAccess") as LastAccessConfigurationSection;
}
You could use App.config for storage like any other file and it will persist and be available the next time you run your program. That is the nature of file storage in general. I would suggest that you store that data in a separate file or database. I will not, however, write the code for you.

How to build an installer to update an ASP.NET's web.config, DLL, files, etc

I have an add-on for a commercial ASP.NET website. My add-on requires people to merge entries into their web.config, add/overwrite existing files, and add some DLL files to the bin folder.
Is there a good and safe way to create an installer than can do this with a wizard type of installation? It would really help non-technical people install the add-on easily. Maybe even a web-based installer would be good?
Any help or suggestions would be greatly appreciated.
Had a similar problem...
Web.Config
Created a .NET command line program that you can call from your installer passing it the web.config path and other args to match what I'm trying to do
In the command line program you can then modify the web.config to your needs... Below is an example of setting a connection string & the stmp from address in a web.config
public static void SetConnectionString(string name, string connString, string webConfigPath)
{
string directory = System.IO.Path.GetDirectoryName(webConfigPath);
VirtualDirectoryMapping vdm = new VirtualDirectoryMapping(directory, true);
WebConfigurationFileMap wcfm = new WebConfigurationFileMap();
wcfm.VirtualDirectories.Add("/", vdm);
System.Configuration.Configuration webConfig = System.Web.Configuration.WebConfigurationManager.OpenMappedWebConfiguration(wcfm, "/");
webConfig.ConnectionStrings.ConnectionStrings[name].ConnectionString = connString;
webConfig.Save();
}
public static void SetFromAddress(string email, string webConfigPath)
{
string directory = System.IO.Path.GetDirectoryName(webConfigPath);
VirtualDirectoryMapping vdm = new VirtualDirectoryMapping(directory, true);
WebConfigurationFileMap wcfm = new WebConfigurationFileMap();
wcfm.VirtualDirectories.Add("/", vdm);
System.Configuration.Configuration webConfig = System.Web.Configuration.WebConfigurationManager.OpenMappedWebConfiguration(wcfm, "/");
System.Net.Configuration.MailSettingsSectionGroup mailSettings = (System.Net.Configuration.MailSettingsSectionGroup)webConfig.GetSectionGroup("system.net/mailSettings");
mailSettings.Smtp.From = email;
webConfig.Save();
}
Installer
I used NSIS (http://nsis.sourceforge.net/Main_Page). Use HM NIS Edit as a good starting point as it has a wizard that will generate scripts for you. From there you can modify up the scripts to your needs. In my case I called my command line program after the files where installed. Example NSIS script below.
Section "My Config Wizard" SecWizard
ExecWait '"$INSTDIR\Bin\My.Config.Wizard.exe" "$INSTDIR"'
Return
SectionEnd
Good luck! Need more examples just hit me up. :P
The web.config is the tricky part. Your first installer will deploy an XML file and then a user will change something in it. Meanwhile you have another build where the developer makes changes to the XML and now the installer has to try to figure out how that should merge all back together.
Out of the box, it can't.
2 strategies that I've used over the years:
1) Have the installer smart enough to pick out key pieces of information from the xml before replacing the xml. Then apply the information back.
2) Design your software to have 2 XML files. One that the installer can safely always overwrite and the other to act as an override that the user can modify safely.

Is it possible to modify configuration ConnectionStrings at runtime?

Is it possible to modify the connectionstrings defined in the app.config/web.config at runtime? I want to use different configfiles depending on the machine the app/site is run on (only for debugging purposes, of course. We'll use the regular config files when deployed).
I can write AppSettings, but not ConnectionStrings (AFAIK). Or can I?
Yes it's possible, but AFAIK only via Reflection. The following code should do what you need (read below for usage):
public static string SetConnectionString(Type assemblyMember,
Type settingsClass,
string newConnectionString,
string connectionStringKey)
{
Type typSettings = Type.GetType(Assembly.CreateQualifiedName(assemblyMember.Assembly.FullName, settingsClass.FullName));
if (typSettings == null)
{
return null;
}
PropertyInfo prpDefault = typSettings.GetProperty("Default", BindingFlags.Static | BindingFlags.Public);
if (prpDefault == null)
{
return null;
}
object objSettings = prpDefault.GetValue(null, null);
if (objSettings == null)
{
return null;
}
// the default property, this[], is actually named Item
PropertyInfo prpItem = objSettings.GetType().GetProperty("Item", BindingFlags.Instance | BindingFlags.Public);
if (prpItem == null)
{
return null;
}
object[] indexerName = { connectionStringKey };
string oldConnectionString = (string)prpItem.GetValue(objSettings, indexerName);
prpItem.SetValue(objSettings, newConnectionString, indexerName);
return oldConnectionString;
}
assemblyMember is the calling type
settingsClass is the type of your settings class
newConnectionString is the full string to set
connectionStringKey is the name of the connection string that you defined in your app's settings
You should call this method as soon as possible after your app has started, preferably in the Main() method.
I tried this once in my project for debugging purpose but could not do it, the problem (I guess, correct me if I am wrong) is on app start up the app.config gets loaded into the memory, any changes to app.config while the app is running do not get reflected.
To overcome this here's what I did, define all the connectionstrings in the app.config and then call the ones that you want when your program is running like this.
for example lets assume you defined your connectionstrings in the app.config as follows.
<connectionStrings>
<add name="YourNameSpace.Properties.Settings.ConnectionString_1"
connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data
Source=|DataDirectory|\file.mdb"
providerName="System.Data.OleDb"/>
<add name="YourNameSpace.Properties.Settings.ConnectionString_2"
connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data
Source=|DataDirectory|\file.mdb"
providerName="System.Data.OleDb"/>
</connectionStrings>
define as many as you want (you are debugging right :-) )
then to call those connection settings in your code do something like this:
YourNameSpace.Properties.Settings foo = new YourNameSapce.Properties.Settings();
foo.ConnectionString_1;
HTH
Best Regards
#nand
P.S: This reply is specific to C#.
You can't really edit the config file of the running process.
One option (with pros and cons) is to use config data in the machine.config or the master web.config (for "site", you use the "location" nodes) - not an option to rush into, though.
A better way to handle this is to swap the config file as part of your build/deploy process, ideally automated. That way, everything is self-contained, and you can "robocopy" to a vanilla server and have it work.
Re your "per developer" point; I found that the easiest way to do this was to standardise the config, and tweak the machines. For example, we run a local web-server on a VM; rather than code against each machine, we standardise on "localserver" (to mirror "localhost"), and add a local DNS record to each machine that the developer can control. Note that this requires fixed IP addresses (or maybe a DHCP reservation) to prevent it changing over time!
Ditto databases; local servers can use "."; remote servers can be aliased on the machine, so "devserver" points to whatever the user wants.
Just a thought...
You could run xmlpoke in a NAnt script when installing the website.
E.g.
Deploy the NAnt with the website.
Create a go.bat that calls NAnt which installs the site and does xmlpoke to modify the web.config with settings based on the server name or some other parameter.

Changing values in Web.config with a Batch file or in .NET code

I have a web.config file on my computer.
There are alot of things i need to change and add in the file.
(I am actually working with my SharePoint web.config file)
Can i do this with a Batch file, if so how would i do it.
Or how would i do it using VB.NET or C# code?
Any ideas guys?
Edit: i need to create a program to alter a web.config of lets say i web.config laying on my deskop and not the actual web.config of my project
Regards
Etienne
You can modify it from C# code, for example:
Configuration configuration = WebConfigurationManager.OpenWebConfiguration("~");
AppSettingsSection appSettingsSection = (AppSettingsSection)configuration.GetSection("appSettings");
if (appSettingsSection != null)
{
appSettingsSection.Settings["foo"].Value = "bar";
config.Save();
}
where foo is the key and bar the value of the key to set, obviously. To remove a value, use Settings.Remove(key);
See the msdn documentation for more information about the OpenWebConfiguration method and more.
The context in which you want to change the file really affects how you should do it. If you're looking at performing changes relatively frequently, but in an administrative domain, then some sort of command-line tool makes sense, and in this case I'd agree with JaredPar that PowerShell would be a valuable tool.
If, on the other hand, you find yourself in a situation where you need to modify the web.config in a more programmatic environment (e.g., as part of a setup program), then using programmatic technologies might make more sense. I recently had to do such a thing and Linq to Xml proved very convenient.
For example, to open a document "C:\foo\bar.xml" you could do something like (untested, no convenient build environment at the moment):
XDocument config = XDocument.Load(#"C:\foo\bar.xml");
You could then carry on in the usual fashion with the API. Note that this may be overkill if you're doing an administrative task as opposed to a programmatic task-- there are big, long-term advantages to learning a tool like PowerShell.
Finally, if you're modifying the web.config from within the program that the web.config is being used for, and you aren't doing anything too fancy or dynamic, then using the built-in Settings or ConfigurationManager may be the way to go.
Your best bet might to change it using a MSBuild Script and the MsBuild Community Tasks XML Mass update task
I would personally recommend using PowerShell. This is the next gen command line from Microsoft and it's sits right on top of .Net. It was built to do items like batch edits across large sets of files.
For a change in web.config in a SharePoint environment you have classes specially developed for this task. You only have to search for SPWebConfigModification class.
To load an arbitrary .NET config file
string configLocation = #"C:\myconfigFile.Config";
ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap();
configFileName = configLocation;
configFileMap.ExeConfigFilename = configFileName;
Configuration configuration= ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);
Then use Razzie's code to alter the actual config setting
AppSettingsSection appSettingsSection = (AppSettingsSection)configuration.GetSection("appSettings");
if (appSettingsSection != null)
{
appSettingsSection.Settings["foo"].Value = "bar";
configuration.Save();
}
This is what i needed to do.......thanks for all the help!!!
// Read in Xml-file
XmlDocument doc = new XmlDocument();
doc.Load("C:/Web.config");
//SaveControl tag..........................................................
XmlNode n = doc.SelectSingleNode("/configuration/SharePoint/SafeControls");
XmlElement elemWeb = doc.CreateElement("SafeControl");
elemWeb.SetAttribute("Assembly", "SamrasWebOption4");
elemWeb.SetAttribute("Namespace", "SamrasWebOption4");
elemWeb.SetAttribute("TypeName", "*");
elemWeb.SetAttribute("Safe", "True");
XmlElement elemSmartPart = doc.CreateElement("SafeControl");
elemSmartPart.SetAttribute("Assembly", "Machine_Totals");
elemSmartPart.SetAttribute("Namespace", "Machine_Totals");
elemSmartPart.SetAttribute("TypeName", "*");
elemSmartPart.SetAttribute("Safe", "True");
//Appending the Nodes......................................................
n.AppendChild(elemWeb);
n.AppendChild(elemSmartPart);
//Saving the document......................................................
doc.Save("C:/Web.config");

Categories