Runtime Web.config / App.config Editing

文章介绍了Web.config和app.config文件的appSettings部分,可存储元素名值对,运行时读取使用。但默认无法在运行时修改、添加和保存appSettings项,需手动操作。作者给出一个方便的类,可在运行时对Executable、Console或ASP.NET应用的appSettings元素进行修改、添加或删除,还提供了示例项目。
 

Web.config configuration files and app.config project item files, which get converted to "ExecutableName.exe.config" at build time, both support the convenient appSettings section with its own read method in the System.Configuration.ConfigurationSettngs class. The appSettings section stores element name / value pairs in the format:

<add key="elementName" value="elementValue" />

You can store as many of these <add> elements as you want, read them out at runtime, and use the values in the application. If you have an item that contains multiple values and you would like to keep them together, you can store them as a single string, delimited with a pipe | or other symbol, read them out at runtime, and call the String.Split() method to parse them into a useable string array.

I often read out my appSetting values into a NameValueCollection at runtime, which provides one-shot acess to the entire collection in memory:

NameValueCollection mySettings = System.Configuration.ConmfigurationSettings.AppSettings;
string connStr = mySettings["connString"];

But what about being able to change, add, and save appSettings items while that app is running in response to user input or other actions, instead of just reading them out? Nada, Zippo, Efes!  You have to open the config file manually and add them by "hand". Well that kinda stinks, don't you think? So here's my take on a convenient little class that allows you to either modify, add or delete any appSettings element, in either your Executable, Console or ASP.NET web application at runtime, on the fly. Bear in mind of course, that if you modify a web.config on a running ASP.NET app, the ASP.NET worker process will recycle. Users currently using your app aren't exactly guaranteed to have a fun experience when this happens...


New Articles & Tips
SQL Server Reporting Services - Lessons Learned
Dr. Dotnetsky's Cool .net Tips and Tricks # 18
.net Compact Framework Save Signature To File
Compressed Ink for Tablet PC and Windows XP
WebService Enabling SQL Server 2005 Methods
HOWTO: Register an Assembly in the GA
.net Framework 1.1 SP1 - Issues
Circular References / Memory Leaks /other baddies
Rsources Section Now Open for Testing!
.net Compact Framework App.Config Workaround
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type=text/javascript> </script>

using System;
using System.Xml;  
using System.Configuration;
using System.Collections;
using System.Reflection;  
using System.Diagnostics ;

public enum   ConfigFileType
{
 WebConfig ,
 AppConfig
}

public class AppConfig : System.Configuration.AppSettingsReader
{ 
 public string  docName = String.Empty;
 private  XmlNode node=null;

 private int _configType;
public int ConfigType { get { return _configType; } set { _configType=value; } } public bool SetValue(string key, string value) { XmlDocument cfgDoc = new XmlDocument(); loadConfigDoc(cfgDoc); // retrieve the appSettings node node = cfgDoc.SelectSingleNode("//appSettings"); if( node == null ) { throw new System.InvalidOperationException( "appSettings section not found"); } try { // XPath select setting "add" element that contains this key XmlElement addElem= (XmlElement)node.SelectSingleNode("//add[@key='" +key +"']") ; if (addElem!=null) { addElem.SetAttribute("value",value); } // not found, so we need to add the element, key and value else { XmlElement entry = cfgDoc.CreateElement("add"); entry.SetAttribute("key",key); entry.SetAttribute("value",value); node.AppendChild(entry); } //save it saveConfigDoc(cfgDoc,docName); return true; } catch { return false; } } private void saveConfigDoc(XmlDocument cfgDoc,string cfgDocPath) { try { XmlTextWriter writer = new XmlTextWriter( cfgDocPath , null ); writer.Formatting = Formatting.Indented; cfgDoc.WriteTo( writer ); writer.Flush(); writer.Close(); return; } catch { throw; } } public bool removeElement ( string elementKey) { try { XmlDocument cfgDoc = new XmlDocument(); loadConfigDoc(cfgDoc); // retrieve the appSettings node node = cfgDoc.SelectSingleNode("//appSettings"); if( node == null ) { throw new System.InvalidOperationException( "appSettings section not found"); } // XPath select setting "add" element that contains this key to remove node.RemoveChild( node.SelectSingleNode("//add[@key='" +elementKey +"']") ); saveConfigDoc(cfgDoc,docName); return true; } catch { return false; } } private XmlDocument loadConfigDoc( XmlDocument cfgDoc ) { // load the config file if( Convert.ToInt32(ConfigType)==Convert.ToInt32(ConfigFileType.AppConfig)) { docName= ((Assembly.GetEntryAssembly()).GetName()).Name; docName += ".exe.config"; } else { docName=System.Web.HttpContext.Current.Server.MapPath("web.config"); } cfgDoc.Load( docName ); return cfgDoc; } }


I'm sure the above can be improved, but it works just fine for me. Notice that if you attempt to modify an element that doesn't exist, we assume that we need to create it for you. In the downloadable solution below you'll find sample projects for a Web application and a Winforms executable, both of which contain sample code to use this class. Note that there are three subfolders under the solution when you unzip this, and the one named "appConfigWeb" needs to be made an IIS virtual directory / application. Enjoy!

Download the code that accompanies this article

 

Peter Bromberg began programming at Merrill Lynch, developing computerized trading programs, later becoming a Development Manager and Senior Programmer at medical and financial services firms. In 2001, he was lead developer on a jointly-funded project with Microsoft to convert COM banking services middleware to the new .net platform. The technology tested at Microsoft Testing Lab at 10 times faster than any previous implementation. Peter has architected numerous enterprise - level business solutions with .net, and is the co-founder of eggheadcafe.com. His samples at gotdotnet.com have been downloaded over 26,000 times. He is a Microsoft MVP, and is currently a Senior Developer at AspSoft, based in Orlando.
Do you have a question or comment about this article? Have a programming problem you need to solve? Post it at eggheadcafe.com forums and receive immediate email notification of responses.
'use strict' const path = require('path') const utils = require('./utils') const webpack = require('webpack') const config = require('../config') const merge = require('webpack-merge') const baseWebpackConfig = require('./webpack.base.conf') const CopyWebpackPlugin = require('copy-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const ExtractTextPlugin = require('extract-text-webpack-plugin') const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') const UglifyJsPlugin = require('uglifyjs-webpack-plugin') const env = process.env.NODE_ENV === 'testing' ? require('../config/test.env') : require('../config/prod.env') const webpackConfig = merge(baseWebpackConfig, { module: { rules: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true, usePostCSS: true }) }, devtool: config.build.productionSourceMap ? config.build.devtool : false, output: { path: config.build.assetsRoot, filename: utils.assetsPath('js/[name].[chunkhash].js'), chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') }, plugins: [ // http://vuejs.github.io/vue-loader/en/workflow/production.html new webpack.DefinePlugin({ 'process.env': env }), new UglifyJsPlugin({ uglifyOptions: { compress: { warnings: false } }, sourceMap: config.build.productionSourceMap, parallel: true }), // extract css into its own file new ExtractTextPlugin({ filename: utils.assetsPath('css/[name].[contenthash].css'), // Setting the following option to `false` will not extract CSS from codesplit chunks. // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 allChunks: true, }), // Compress extracted CSS. We are using this plugin so that possible // duplicated CSS from different components can be deduped. new OptimizeCSSPlugin({ cssProcessorOptions: config.build.productionSourceMap ? { safe: true, map: { inline: false } } : { safe: true } }), // generate dist index.html with correct asset hash for caching. // you can customize output by editing /index.html // see https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ filename: process.env.NODE_ENV === 'testing' ? 'index.html' : config.build.index, template: 'index.html', inject: true, minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: true // more options: // https://github.com/kangax/html-minifier#options-quick-reference }, // necessary to consistently work with multiple chunks via CommonsChunkPlugin chunksSortMode: 'dependency' }), // keep module.id stable when vendor modules does not change new webpack.HashedModuleIdsPlugin(), // enable scope hoisting new webpack.optimize.ModuleConcatenationPlugin(), // split vendor js into its own file new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks (module) { // any required modules inside node_modules are extracted to vendor return ( module.resource && /\.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, '../node_modules') ) === 0 ) } }), // extract webpack runtime and module manifest to its own file in order to // prevent vendor hash from being updated whenever app bundle is updated new webpack.optimize.CommonsChunkPlugin({ name: 'manifest', minChunks: Infinity }), // This instance extracts shared chunks from code splitted chunks and bundles them // in a separate chunk, similar to the vendor chunk // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk new webpack.optimize.CommonsChunkPlugin({ name: 'app', async: 'vendor-async', children: true, minChunks: 3 }), // copy custom static assets new CopyWebpackPlugin([ { from: path.resolve(__dirname, '../static'), to: config.build.assetsSubDirectory, ignore: ['.*'] } ]) ] }) if (config.build.productionGzip) { const CompressionWebpackPlugin = require('compression-webpack-plugin') webpackConfig.plugins.push( new CompressionWebpackPlugin({ asset: '[path].gz[query]', algorithm: 'gzip', test: new RegExp( '\\.(' + config.build.productionGzipExtensions.join('|') + ')$' ), threshold: 10240, minRatio: 0.8 }) ) } if (config.build.bundleAnalyzerReport) { const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin webpackConfig.plugins.push(new BundleAnalyzerPlugin()) } module.exports = webpackConfig 打包后的文件会是zip文件吗
最新发布
12-02
// Jitsi Meet configuration. var config = {}; config.hosts = {}; config.hosts.domain = 'meet.jitsi'; var subdir = '<!--# echo var="subdir" default="" -->'; var subdomain = '<!--# echo var="subdomain" default="" -->'; if (subdir.startsWith('<!--')) { subdir = ''; } if (subdomain) { subdomain = subdomain.substring(0,subdomain.length-1).split('.').join('_').toLowerCase() + '.'; } config.hosts.muc = 'muc.' + subdomain + 'meet.jitsi'; config.bosh = 'https://mt.zy5158.com/' + subdir + 'http-bind'; config.bridgeChannel = { preferSctp: true }; // Video configuration. // config.resolution = 720; config.constraints = { video: { height: { ideal: 720, max: 720, min: 180 }, width: { ideal: 1280, max: 1280, min: 320}, } }; config.startVideoMuted = 10; config.startWithVideoMuted = false; config.flags = { sourceNameSignaling: true, sendMultipleVideoStreams: true, receiveMultipleVideoStreams: true }; // ScreenShare Configuration. // // Audio configuration. // config.enableNoAudioDetection = true; config.enableTalkWhileMuted = false; config.disableAP = false; config.disableAGC = false; config.audioQuality = { stereo: false }; config.startAudioOnly = false; config.startAudioMuted = 10; config.startWithAudioMuted = false; config.startSilent = false; config.enableOpusRed = false; config.disableAudioLevels = false; config.enableNoisyMicDetection = true; // Peer-to-Peer options. // config.p2p = { enabled: true, codecPreferenceOrder: ["AV1", "VP9", "VP8", "H264"], mobileCodecPreferenceOrder: ["VP8", "VP9", "H264", "AV1"] }; // Breakout Rooms // config.hideAddRoomButton = false; // Etherpad // // Recording. // // Local recording configuration. config.localRecording = { disable: true, notifyAllParticipants: false, disableSelfRecording: false }; // Analytics. // config.analytics = {}; // Dial in/out services. // // Calendar service integration. // config.enableCalendarIntegration = false; // Invitation service. // // Miscellaneous. // // Prejoin page. config.prejoinConfig = { enabled: true, // Hides the participant name editing field in the prejoin screen. hideDisplayName: false }; // List of buttons to hide from the extra join options dropdown on prejoin screen. // Welcome page. config.welcomePage = { disabled: false }; // Close page. config.enableClosePage = false; // Default language. // Require users to always specify a display name. config.requireDisplayName = false; // Chrome extension banner. // Disables profile and the edit of all fields from the profile settings (display name and email) config.disableProfile = false; // Room password (false for anything, number for max digits) config.roomPasswordNumberOfDigits = false; // Advanced. // // Transcriptions (subtitles and buttons can be configured in interface_config) config.transcription = { enabled: false, disableClosedCaptions: true, translationLanguages: [], translationLanguagesHead: ['en'], useAppLanguage: true, preferredLanguage: 'en-US', disableStartForAll: false, autoCaptionOnRecord: false, }; // Dynamic branding // Deployment information. // config.deploymentInfo = {}; // Deep Linking config.disableDeepLinking = false; // P2P preferred codec // Video quality settings. // config.videoQuality = {}; config.videoQuality.codecPreferenceOrder = ["AV1", "VP9", "VP8", "H264"]; config.videoQuality.mobileCodecPreferenceOrder = ["VP8", "VP9", "H264", "AV1"]; config.videoQuality.enableAdaptiveMode = true; config.videoQuality.av1 = {}; config.videoQuality.h264 = {}; config.videoQuality.vp8 = {}; config.videoQuality.vp9 = {}; // Reactions config.disableReactions = false; // Polls config.disablePolls = false; // Configure toolbar buttons // Hides the buttons at pre-join screen // Configure remote participant video menu config.remoteVideoMenu = { disabled: false, disableKick: false, disableGrantModerator: false, disablePrivateChat: false }; // Configure e2eping config.e2eping = { enabled: false }; // Settings for the Excalidraw whiteboard integration. config.whiteboard = { enabled: false, }; // JaaS support: pre-configure image if JAAS_APP_ID was set. // Testing config.testing = { enableCodecSelectionAPI: true }; 解答下
09-18
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值