WSE 3.0 and SoapExtensions - c#

I'm trying to consume an Axis web service with a .NET client and I'm having some trouble setting up the WSE configuration to work with it. I know that WSE 3.0 is an outdated technology, but I'm stuck with a VS2005 and .NET 2.0 environment, so I'll have to work with this.
Currently I've created a new ASP.NET Website application and enabled WSE on it. It generages a bunch of sections like the WS:Addressing and WS:Timestamp sections from it that the server cannot handle and I'm looking to remove them. I couldn't find a config setting to do it, so after some searching on SO, I've started working with SoapExtensions to intercept the message and remove the sections manually.
My problem currently seems to be that the code in the SoapExtensions isn't getting executed at all. Here's my web.config -
<configuration>
<configSections>
<section name="microsoft.web.services3" type="Microsoft.Web.Services3.Configuration.WebServicesConfiguration, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</configSections>
<appSettings>
<add key="WebSvc.SecureService" value="<Trimmed>" />
</appSettings>
<connectionStrings />
<system.web>
<compilation debug="true">
<assemblies>
<add assembly="Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</assemblies>
</compilation>
<authentication mode="Windows" />
<webServices>
<soapExtensionImporterTypes>
<add type="Microsoft.Web.Services3.Description.WseExtensionImporter, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</soapExtensionImporterTypes>
<soapServerProtocolFactory type="Microsoft.Web.Services3.WseProtocolFactory, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</webServices>
</system.web>
<microsoft.web.services3>
<security>
<x509 verifyTrust="false" />
<defaultTtlInSeconds value="0" />
<timeToleranceInSeconds value="0" />
</security>
<diagnostics>
<trace enabled="true" input="InputTrace.webinfo" output="OutputTrace.webinfo" />
</diagnostics>
<policy fileName="wse3policyCache.config" />
</microsoft.web.services3>
</configuration>
And my web service method -
[WebMethod]
[TestApplication.TraceExtensionAttribute]
public void SendTestMessage()
{
WebSvc.B2BSecureServiceServiceWse svcClient = new WebSvc.B2BSecureServiceServiceWse();
svcClient.SetPolicy("Policy1");
WebSvc.B2BRequest request = new WebSvc.B2BRequest();
WebSvc.B2BRequestHeader WebSvc= new WebSvc.B2BRequestHeader();
// Set Request header properties
request.header = requestHeader;
request.msgPayload = ""; // Text removed
try
{
WebSvc.Response response = svcClient.processRequest(request);
}
catch (Exception ex)
{
}
}
And here's my SoapExtension Class -
public class Snoop : SoapExtension
{
Stream oldStream;
Stream newStream;
string filename;
// Save the Stream representing the SOAP request or SOAP response into
// a local memory buffer.
public override Stream ChainStream(Stream stream)
{
oldStream = stream;
newStream = new MemoryStream();
return newStream;
}
// When the SOAP extension is accessed for the first time, the XML Web
// service method it is applied to is accessed to store the file
// name passed in, using the corresponding SoapExtensionAttribute.
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
return ((TraceExtensionAttribute)attribute).Filename;
}
// The SOAP extension was configured to run using a configuration file
// instead of an attribute applied to a specific XML Web service
// method.
public override object GetInitializer(Type WebServiceType)
{
// Return a file name to log the trace information to, based on the
// type.
return "C:\\" + WebServiceType.FullName + ".log";
}
// Receive the file name stored by GetInitializer and store it in a
// member variable for this specific instance.
public override void Initialize(object initializer)
{
filename = (string)initializer;
}
// If the SoapMessageStage is such that the SoapRequest or
// SoapResponse is still in the SOAP format to be sent or received,
// save it out to a file.
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
WriteOutput(message);
break;
case SoapMessageStage.BeforeDeserialize:
WriteInput(message);
break;
case SoapMessageStage.AfterDeserialize:
break;
}
}
public void WriteOutput(SoapMessage message)
{
newStream.Position = 0;
FileStream fs = new FileStream(filename, FileMode.Append,
FileAccess.Write);
StreamWriter w = new StreamWriter(fs);
string soapString = (message is SoapServerMessage) ? "SoapResponse" : "SoapRequest";
w.WriteLine("-----" + soapString + " at " + DateTime.Now);
w.Flush();
Copy(newStream, fs);
w.Close();
newStream.Position = 0;
Copy(newStream, oldStream);
}
public void WriteInput(SoapMessage message)
{
Copy(oldStream, newStream);
FileStream fs = new FileStream(filename, FileMode.Append,
FileAccess.Write);
StreamWriter w = new StreamWriter(fs);
string soapString = (message is SoapServerMessage) ?
"SoapRequest" : "SoapResponse";
w.WriteLine("-----" + soapString +
" at " + DateTime.Now);
w.Flush();
newStream.Position = 0;
Copy(newStream, fs);
w.Close();
newStream.Position = 0;
}
void Copy(Stream from, Stream to)
{
TextReader reader = new StreamReader(from);
TextWriter writer = new StreamWriter(to);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}
}
// Create a SoapExtensionAttribute for the SOAP Extension that can be
// applied to an XML Web service method.
[AttributeUsage(AttributeTargets.Method)]
public class TraceExtensionAttribute : SoapExtensionAttribute
{
private string filename = "c:\\log.txt";
private int priority;
public override Type ExtensionType
{
get { return typeof(Snoop); }
}
public override int Priority
{
get { return priority; }
set { priority = value; }
}
public string Filename
{
get
{
return filename;
}
set
{
filename = value;
}
}
}
I'm guessing I'm doing something wrong while trying to assign the SoapExtension to that method call, so any help is much appreciated!

For anyone who comes across this problem, here's how I've solved it using SoapFilters. Here's the process from MSDN - http://msdn.microsoft.com/en-us/library/aa528788.aspx

Related

how to read encrypted app.config appSettings in win console application?

EDIT: This question made no sense. I mixed .vshost.config with exe.config. What to do with this?
Program.cs main:
databaseName = System.Configuration.ConfigurationManager.AppSettings["DatabaseName"];
databaseUser = System.Configuration.ConfigurationManager.AppSettings["DatabaseUser"];
databasePwd = System.Configuration.ConfigurationManager.AppSettings["DatabasePassword"];
port = System.Configuration.ConfigurationManager.AppSettings["Port"];
logDirectory = System.Configuration.ConfigurationManager.AppSettings["LogDirectory"];
strLogLevel = System.Configuration.ConfigurationManager.AppSettings["LogLevel"];
EncryptConfigSection("appSettings");
This is how I encrypt the file:
private static void EncryptConfigSection(string sectionKey)
{
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationSection section = config.GetSection(sectionKey);
if (section != null)
{
if (!section.SectionInformation.IsProtected)
{
if (!section.ElementInformation.IsLocked)
{
section.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
section.SectionInformation.ForceSave = true;
config.Save(ConfigurationSaveMode.Full);
}
}
}
}
The file gets duplicated and encrypted just like in the examples I found in web:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings configProtectionProvider="DataProtectionConfigurationProvider">
<EncryptedData>
<CipherData>
<CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAvsQ9Wtc58EC5EZCEq91EogQAAAACAAAAAAADZgAAwAAAABAAAClVHhpR5xAw4KFNyrANtavAAAAAASAAACgAAAAEAAAABHkhg2ztiY3bdWhTG9iy6twAAAAF5mAHt7oDQWCgc1iLL2hYUJZgmquU8XsojjqXVQdV1CaW3XEBXBDhN30DEZizP3F5rGGMCjL9CVjHfsPAfvVYyRHCcup22BoByb5y/MDujaASpaWZYcdxSxLijT/Zq3zB8hiWyWPruY0G7emYEOq/xQAAADkgStCMABwo3oZx/VXHD41wrsjXg==</CipherValue>
</CipherData>
</EncryptedData>
</appSettings>
</configuration>
But next time I start it, I can't read it. All read values are null. I naturally removed the original, unencrypted file from the folder.
You can use the KeyValueConfigurationCollection for the appSettings key and the ConnectionStringSettingsCollection for the connectionStrings key.
This encrypts when not encrypted and decrypts and prints out values when encrypted:
private static void CryptConfig (string[] sectionKeys)
{
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
foreach (string sectionKey in sectionKeys)
{
ConfigurationSection section = config.GetSection(sectionKey);
if (section != null)
{
if (section.ElementInformation.IsLocked)
{
Console.WriteLine("Section: {0} is locked", sectionKey);
}
else
{
if (!section.SectionInformation.IsProtected)
{
//%windir%\system32\Microsoft\Protect\S-1-5-18
section.SectionInformation.ProtectSection(DPCP);
section.SectionInformation.ForceSave = true;
Console.WriteLine("Encrypting: {0} {1}", section.SectionInformation.Name, section.SectionInformation.SectionName);
}
else
{ // display values for current config application name value pairs
foreach (KeyValueConfigurationElement x in config.AppSettings.Settings)
{
Console.WriteLine("Key: {0} Value:{1}", x.Key, x.Value);
}
foreach (ConnectionStringSettings x in config.ConnectionStrings.ConnectionStrings)
{
Console.WriteLine("Name: {0} Provider:{1} Cs:{2}", x.Name, x.ProviderName, x.ConnectionString);
}
//
section.SectionInformation.UnprotectSection();
section.SectionInformation.ForceSave = true;
Console.WriteLine("Decrypting: {0} {1}", section.SectionInformation.Name, section.SectionInformation.SectionName);
}
}
}
else
{
Console.WriteLine("Section: {0} is null", sectionKey);
}
}
//
config.Save(ConfigurationSaveMode.Full);
Console.WriteLine("Saving file: {0}", config.FilePath);
}
App.config used:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="DatabaseName" value="databaseName"/>
<add key="DatabaseUser" value="databaseUser"/>
<add key="DatabasePassword" value="databasePwd"/>
<add key="Port" value="port"/>
<add key="LogDirectory" value="logDirectory"/>
<add key="LogLevel" value="strLogLevel"/>
</appSettings>
<connectionStrings>
<add name="SecurePassDataBase" connectionString="Data Source=D-xxxx;Initial Catalog=DEMO;User ID=sa;Password=******" />
</connectionStrings>
</configuration>
here is a very simple code you can use
RsaProtectedConfigurationProvider Sample
Made some small modifications...
static public void ProtectSection()
{
// Get the current configuration file.
System.Configuration.Configuration config =
ConfigurationManager.OpenExeConfiguration(
ConfigurationUserLevel.None);
// Get the section.
ConfigurationSection section = config.GetSection("appSettings");
// Protect (encrypt)the section.
section.SectionInformation.ProtectSection("RsaProtectedConfigurationProvider");
// Save the encrypted section.
section.SectionInformation.ForceSave = true;
config.Save(ConfigurationSaveMode.Full);
// Display decrypted configuration
// section. Note, the system
// uses the Rsa provider to decrypt
// the section transparently.
string sectionXml = section.SectionInformation.GetRawXml();
Console.WriteLine("Decrypted section:");
Console.WriteLine(sectionXml);
}
Try this & you'll get what you want :
Console.WriteLine(ConfigurationManager.OpenExeConfiguration(Path.GetFileName(Assembly.GetExecutingAssembly().CodeBase).ToString()).FilePath.ToString());
string[] readText = File.ReadAllLines(ConfigurationManager.OpenExeConfiguration(Path.GetFileName(Assembly.GetExecutingAssembly().CodeBase).ToString()).FilePath.ToString());
foreach (string s in readText)
{
Console.WriteLine(s);
}

Log messages going to previously created log file

I am using enterprise library 5.0 logging in my asp.net site,
My web.config file is as follows:
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true"/>
</configSections>
<loggingConfiguration name="FlatFileLogging" tracingEnabled="true"
defaultCategory="General">
<listeners>
<add name="Flat File Trace Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
fileName="C:\Logs\2013-06-28 14-21-53.log" header="" footer=""
formatter="Text Formatter" traceOutputOptions="DateTime" />
</listeners>
<formatters>
<add type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
template="{timestamp}, {severity}, {message}" name="Text Formatter" />
</formatters>
<categorySources>
<add switchValue="All" name="General">
<listeners>
<add name="Flat File Trace Listener" />
</listeners>
</add>
</categorySources>
<specialSources>
<allEvents switchValue="All" name="All Events" />
<notProcessed switchValue="All" name="Unprocessed Category" />
<errors switchValue="All" name="Logging Errors & Warnings">
<listeners>
<add name="Flat File Trace Listener" />
</listeners>
</errors>
</specialSources>
</loggingConfiguration>
<appSettings/>
<connectionStrings/>
<system.web>
<compilation debug="true" targetFramework="4.0">
</compilation>
<authentication mode="Windows"/>
<pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID"/></system.web>
</configuration>
I am changing log file path using following function:
public static void SetLogFilePath(string filePath)
{
//string logdirectory = AppDomain.CurrentDomain.BaseDirectory + "Logs\\";
//if (!Directory.Exists(logdirectory))
// Directory.CreateDirectory(logdirectory);
//logFilePath = logdirectory + (string.IsNullOrWhiteSpace(txtBatchName.Text) ? "" : (txtBatchName.Text + " ")) + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + ".log";
if (!File.Exists(filePath))
File.Create(filePath);
ConfigurationFileMap objConfigPath = new ConfigurationFileMap();
// App config file path.
string appPath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
objConfigPath.MachineConfigFilename = appPath;
Configuration entLibConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~");
LoggingSettings loggingSettings = (LoggingSettings)entLibConfig.GetSection(LoggingSettings.SectionName);
TraceListenerData traceListenerData = loggingSettings.TraceListeners.Get("Flat File Trace Listener");
FlatFileTraceListenerData objFlatFileTraceListenerData = traceListenerData as FlatFileTraceListenerData;
objFlatFileTraceListenerData.FileName = filePath;
entLibConfig.Save();
}
Whenever I change log file path and send log messages to file, the logs do not go to newly created file. Log messages go to previously set file path. seems that new setting is not reflecting immediately.
string path = "C:\\Logs\\" + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + ".log";
SetLogFilePath(path);
Logger.Write(message, "General", 1, 0, System.Diagnostics.TraceEventType.Information);
How to refresh the new settings to code immediately?
How do you define "immediately"? If you mean in the middle of the executing request then I don't think you can do that via configuration since the configuration will not be refreshed for that request.
Here's an implementation that seems to work for me based on the blog post Enterprise Library Programmatic Configuration. I don't write the config change back to disk but change it in memory instead.
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
EnterpriseLibraryContainer.Current.GetInstance<LogWriter>()
.Write("test", "General");
string path = "C:\\Logs\\anotherlogfile.log";
SetLogFilePath(path);
EnterpriseLibraryContainer.Current.GetInstance<LogWriter>()
.Write("Another test", "General");
}
public void SetLogFilePath(string filePath)
{
ConfigurationFileMap objConfigPath = new ConfigurationFileMap();
// App config file path.
string appPath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
objConfigPath.MachineConfigFilename = appPath;
Configuration entLibConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~");
LoggingSettings loggingSettings = (LoggingSettings)entLibConfig.GetSection(LoggingSettings.SectionName);
TraceListenerData traceListenerData = loggingSettings.TraceListeners.Get("Flat File Trace Listener");
FlatFileTraceListenerData objFlatFileTraceListenerData = traceListenerData as FlatFileTraceListenerData;
objFlatFileTraceListenerData.FileName = filePath;
IUnityContainer container = new UnityContainer();
container.AddNewExtension<EnterpriseLibraryCoreExtension>();
// Configurator will read Enterprise Library configuration
// and set up the container
UnityContainerConfigurator configurator = new UnityContainerConfigurator(container);
var loggingXmlConfigSource = new SerializableConfigurationSource();
loggingXmlConfigSource.Add(LoggingSettings.SectionName, loggingSettings);
// Configure the container with our own custom logging
EnterpriseLibraryContainer.ConfigureContainer(configurator, loggingXmlConfigSource);
// Wrap in ServiceLocator
IServiceLocator locator = new UnityServiceLocator(container);
// Release lock(s) on existing file(s)
EnterpriseLibraryContainer.Current.GetInstance<LogWriter>().Dispose();
// And set Enterprise Library to use it
EnterpriseLibraryContainer.Current = locator;
}
}
public class SerializableConfigurationSource : IConfigurationSource
{
Dictionary<string, ConfigurationSection> sections = new Dictionary<string, ConfigurationSection>();
public SerializableConfigurationSource()
{
}
public ConfigurationSection GetSection(string sectionName)
{
ConfigurationSection configSection;
if (sections.TryGetValue(sectionName, out configSection))
{
SerializableConfigurationSection section = configSection as SerializableConfigurationSection;
if (section != null)
{
using (StringWriter xml = new StringWriter())
using (XmlWriter xmlwriter = System.Xml.XmlWriter.Create(xml))
{
section.WriteXml(xmlwriter);
xmlwriter.Flush();
MethodInfo methodInfo = section.GetType().GetMethod("DeserializeSection", BindingFlags.NonPublic | BindingFlags.Instance);
methodInfo.Invoke(section, new object[] { XDocument.Parse(xml.ToString()).CreateReader() });
return configSection;
}
}
}
return null;
}
public void Add(string sectionName, ConfigurationSection configurationSection)
{
sections[sectionName] = configurationSection;
}
public void AddSectionChangeHandler(string sectionName, ConfigurationChangedEventHandler handler)
{
throw new NotImplementedException();
}
public void Remove(string sectionName)
{
sections.Remove(sectionName);
}
public void RemoveSectionChangeHandler(string sectionName, ConfigurationChangedEventHandler handler)
{
throw new NotImplementedException();
}
public event EventHandler<ConfigurationSourceChangedEventArgs> SourceChanged;
public void Dispose()
{
}
}

Cannot get localization working in MVC 4

I've been trying to follow the blog post found here. I've added a new assembly that hosts my resource files (I used a separate assembly as the resources may need to be shared between multiple projects). I have added the following to my web.config:
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="MyResources.Resources"/> <!-- New entry for resources -->
</namespaces>
</pages>
</system.web.webPages.razor>
And I have gone though and added resource strings to a few files for testing purposes. Now the problem that I seem to be running into, is that I cannot set the resource to be anything other than the default. So for example, in the generated resource file designer there is the following:
/// <summary>
/// Looks up a localized string similar to Log in was unsuccessful. Please correct the errors and try again..
/// </summary>
public static string Account_LoginUnsuccessful {
get {
return ResourceManager.GetString("Account_LoginUnsuccessful", resourceCulture);
}
}
If I set a breakpoint in this method, resourceCulture is NEVER anything but null. Even though I have tried the following:
In Global.asax.ca:
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
var culture = new System.Globalization.CultureInfo("fr");
// Modify current thread's cultures
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(culture.Name);
} // End of Application_AcquireRequestState
In a base mvc controller that all of my other controller inherit:
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
SetCulture(requestContext.HttpContext.Request);
base.Initialize(requestContext);
}
protected override void ExecuteCore()
{
SetCulture(Request);
base.ExecuteCore();
}
protected override void Execute(System.Web.Routing.RequestContext requestContext)
{
SetCulture(requestContext.HttpContext.Request);
base.Execute(requestContext);
}
protected override IAsyncResult BeginExecute(System.Web.Routing.RequestContext requestContext, AsyncCallback callback, object state)
{
SetCulture(requestContext.HttpContext.Request);
metrics = Metrics.BeginTimer();
return base.BeginExecute(requestContext, callback, state);
}
private void SetCulture(HttpRequestBase Request)
{
string cultureName = "fr";
// Validate culture name
cultureName = CultureHelper.GetImplementedCulture(cultureName); // This is safe
// Modify current thread's cultures
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureName);
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
}
Now, to my understanding setting the threading current culture should be causing my resource files culture to change. I can't seem to get this working of the life of me (hence why I have tried setting the cultire in about ten different locations).
Any suggestions on what I am doing wrong here?
Just to verify, try setting the language in a ActionFilterAttribute:
public class ChangeLanguageAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(
ActionExecutingContext filterContext)
{
string languageCode = "fr";
CultureInfo info =
CultureInfo.CreateSpecificCulture(languageCode.ToString());
Thread.CurrentThread.CurrentCulture = info;
Thread.CurrentThread.CurrentUICulture = info;
}
}
This is the exact code Im using, so I know this works.
this is the code i came up with, it should be on Application_OnBeginRequest :
public void Application_OnBeginRequest(object sender, EventArgs e)
{
System.Globalization.CultureInfo NC = new System.Globalization.CultureInfo(1036, true);
NC.NumberFormat.CurrencyDecimalDigits = 2;
NC.NumberFormat.CurrencySymbol = "euro";
NC.NumberFormat.CurrencyDecimalSeparator = ".";
NC.NumberFormat.PercentDecimalSeparator = ".";
NC.NumberFormat.NumberDecimalSeparator = ".";
NC.NumberFormat.CurrencyGroupSeparator = "";
NC.NumberFormat.PercentGroupSeparator = "";
NC.NumberFormat.NumberGroupSeparator = "";
System.Threading.Thread.CurrentThread.CurrentCulture = NC;
}

How to get the values of a ConfigurationSection of type NameValueSectionHandler

I'm working with C#, Framework 3.5 (VS 2008).
I'm using the ConfigurationManager to load a config (not the default app.config file) into a Configuration object.
Using the Configuration class, I was able to get a ConfigurationSection, but I could not find a way to get the values of that section.
In the config, the ConfigurationSection is of type System.Configuration.NameValueSectionHandler.
For what it worth, when I used the method GetSection of the ConfigurationManager (works only when it was on my default app.config file), I received an object type, that I could cast into collection of pairs of key-value, and I just received the value like a Dictionary. I could not do such cast when I received ConfigurationSection class from the Configuration class however.
EDIT:
Example of the config file:
<configuration>
<configSections>
<section name="MyParams"
type="System.Configuration.NameValueSectionHandler" />
</configSections>
<MyParams>
<add key="FirstParam" value="One"/>
<add key="SecondParam" value="Two"/>
</MyParams>
</configuration>
Example of the way i was able to use it when it was on app.config (the "GetSection" method is for the default app.config only):
NameValueCollection myParamsCollection =
(NameValueCollection)ConfigurationManager.GetSection("MyParams");
Console.WriteLine(myParamsCollection["FirstParam"]);
Console.WriteLine(myParamsCollection["SecondParam"]);
Suffered from exact issue. Problem was because of NameValueSectionHandler in .config file. You should use AppSettingsSection instead:
<configuration>
<configSections>
<section name="DEV" type="System.Configuration.AppSettingsSection" />
<section name="TEST" type="System.Configuration.AppSettingsSection" />
</configSections>
<TEST>
<add key="key" value="value1" />
</TEST>
<DEV>
<add key="key" value="value2" />
</DEV>
</configuration>
then in C# code:
AppSettingsSection section = (AppSettingsSection)ConfigurationManager.GetSection("TEST");
btw NameValueSectionHandler is not supported any more in 2.0.
Here's a good post that shows how to do it.
If you want to read the values from a file other than the app.config, you need to load it into the ConfigurationManager.
Try this method: ConfigurationManager.OpenMappedExeConfiguration()
There's an example of how to use it in the MSDN article.
Try using an AppSettingsSection instead of a NameValueCollection. Something like this:
var section = (AppSettingsSection)config.GetSection(sectionName);
string results = section.Settings[key].Value;
Source:
http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/d5079420-40cb-4255-9b3b-f9a41a1f7ad2/
The only way I can get this to work is to manually instantiate the section handler type, pass the raw XML to it, and cast the resulting object.
Seems pretty inefficient, but there you go.
I wrote an extension method to encapsulate this:
public static class ConfigurationSectionExtensions
{
public static T GetAs<T>(this ConfigurationSection section)
{
var sectionInformation = section.SectionInformation;
var sectionHandlerType = Type.GetType(sectionInformation.Type);
if (sectionHandlerType == null)
{
throw new InvalidOperationException(string.Format("Unable to find section handler type '{0}'.", sectionInformation.Type));
}
IConfigurationSectionHandler sectionHandler;
try
{
sectionHandler = (IConfigurationSectionHandler)Activator.CreateInstance(sectionHandlerType);
}
catch (InvalidCastException ex)
{
throw new InvalidOperationException(string.Format("Section handler type '{0}' does not implement IConfigurationSectionHandler.", sectionInformation.Type), ex);
}
var rawXml = sectionInformation.GetRawXml();
if (rawXml == null)
{
return default(T);
}
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(rawXml);
return (T)sectionHandler.Create(null, null, xmlDocument.DocumentElement);
}
}
The way you would call it in your example is:
var map = new ExeConfigurationFileMap
{
ExeConfigFilename = #"c:\\foo.config"
};
var configuration = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
var myParamsSection = configuration.GetSection("MyParams");
var myParamsCollection = myParamsSection.GetAs<NameValueCollection>();
This is an old question, but I use the following class to do the job. It's based on Scott Dorman's blog:
public class NameValueCollectionConfigurationSection : ConfigurationSection
{
private const string COLLECTION_PROP_NAME = "";
public IEnumerable<KeyValuePair<string, string>> GetNameValueItems()
{
foreach ( string key in this.ConfigurationCollection.AllKeys )
{
NameValueConfigurationElement confElement = this.ConfigurationCollection[key];
yield return new KeyValuePair<string, string>
(confElement.Name, confElement.Value);
}
}
[ConfigurationProperty(COLLECTION_PROP_NAME, IsDefaultCollection = true)]
protected NameValueConfigurationCollection ConfCollection
{
get
{
return (NameValueConfigurationCollection) base[COLLECTION_PROP_NAME];
}
}
The usage is straightforward:
Configuration configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
NameValueCollectionConfigurationSection config =
(NameValueCollectionConfigurationSection) configuration.GetSection("MyParams");
NameValueCollection myParamsCollection = new NameValueCollection();
config.GetNameValueItems().ToList().ForEach(kvp => myParamsCollection.Add(kvp));
Here are some examples from this blog mentioned earlier:
<configuration>
<Database>
<add key="ConnectionString" value="data source=.;initial catalog=NorthWind;integrated security=SSPI"/>
</Database>
</configuration>
get values:
NameValueCollection db = (NameValueCollection)ConfigurationSettings.GetConfig("Database");
labelConnection2.Text = db["ConnectionString"];
-
Another example:
<Locations
ImportDirectory="C:\Import\Inbox"
ProcessedDirectory ="C:\Import\Processed"
RejectedDirectory ="C:\Import\Rejected"
/>
get value:
Hashtable loc = (Hashtable)ConfigurationSettings.GetConfig("Locations");
labelImport2.Text = loc["ImportDirectory"].ToString();
labelProcessed2.Text = loc["ProcessedDirectory"].ToString();
Try this;
Credit: https://www.limilabs.com/blog/read-system-net-mailsettings-smtp-settings-web-config
SmtpSection section = (SmtpSection)ConfigurationManager.GetSection("system.net/mailSettings/smtp");
string from = section.From;
string host = section.Network.Host;
int port = section.Network.Port;
bool enableSsl = section.Network.EnableSsl;
string user = section.Network.UserName;
string password = section.Network.Password;
This works like a charm
dynamic configSection = ConfigurationManager.GetSection("MyParams");
var theValue = configSection["FirstParam"];

.Net Windows Service and Custom Trace Listeners

I have all of my service logic encapsulated in class library. When I instantiate the class library in a command line app I receive my trace information.
When I instantiate the class in a Windows Service, I see that my Custom trace listener has created the logs directory and start a file, but it stays a 0 KB.
Both applications have this in the .config:
<system.diagnostics>
<switches>
<add name="PTraceSwitch" value="Verbose" />
</switches>
<trace autoflush="true">
<listeners>
<add name="CustomXmlWriterTraceListener" />
<add name="MyServiceEventListener" />
<remove name="Default" />
</listeners>
</trace>
<sharedListeners>
<add
type="CustomUtilities.CustomXmlWriterTraceListener, CustomUtilities"
name="CustomXmlWriterTraceListener"
initializeData="Logs\MyService.svclog"
RollLogAtMidnight="True"
DateFormat="yyyy.MM.dd"
MaxFileSizeMB="1"
CompressLogsOlderThanTimeSpan="1.00:0:00"
DeleteLogsOlderThanTimeSpan="30.00:00:00"/>
<add name="MyServiceEventListener"
type="System.Diagnostics.EventLogTraceListener"
initializeData="MyServiceEventLog">
<filter type="System.Diagnostics.EventTypeFilter"
initializeData="Warning" />
</add>
</sharedListeners>
It seems that my CustomXmlWriterTraceListener behaves differently when run with a service than a class library or exe.
I added this to my service to debug the main entry point and walk through the init of my listener:
Debugger.Launch();
The underlying Writer is null when I run my service, it is normally populated during the base constructor. I used .NET Reflector to see what the XmlWriterTraceListener does. There is an internal method EnsureWriter(). This method should create the Writer. I cloned Microsoft's method and added it to my Listener, and all seems to be okay. My Listener is suitable for a service now.
internal bool EnsureWriter()
{
bool flag = true;
if (Writer == null)
{
flag = false;
if (baseFileName == null)
{
return flag;
}
Encoding encodingWithFallback = new UTF8Encoding(false);
string fullPath = Path.GetFullPath(baseFileName);
string directoryName = Path.GetDirectoryName(fullPath);
string fileName = Path.GetFileName(fullPath);
for (int i = 0; i < 2; i++)
{
try
{
Writer = new StreamWriter(fullPath, true, encodingWithFallback, 0x1000);
flag = true;
break;
}
catch (IOException)
{
fileName = Guid.NewGuid().ToString() + fileName;
fullPath = Path.Combine(directoryName, fileName);
}
catch (UnauthorizedAccessException)
{
break;
}
catch (Exception)
{
break;
}
}
if (!flag)
{
baseFileName = null;
}
}
return flag;
}

Categories

Resources