I'm trying to write an IXmlSerializable class that stays synced with an XML file. The XML file has the following format:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<logging>
<logLevel>Error</logLevel>
</logging>
...potentially other sections...
</configuration>
I have a DllConfig class for the whole XML file and a LoggingSection class for representing <logging> and its contents, i.e., <logLevel>. DllConfig has this property:
[XmlElement(ElementName = LOGGING_TAG_NAME,
DataType = "LoggingSection")]
public LoggingSection Logging { get; protected set; }
What I want is for the backing XML file to be updated (i.e., rewritten) when a property is set. I already have DllConfig do this when Logging is set. However, how should I go about doing this when Logging.LogLevel is set? Here's an example of what I mean:
var config = new DllConfig("path_to_backing_file.xml");
config.Logging.LogLevel = LogLevel.Information; // not using Logging setter, but a
// setter on LoggingSection, so how
// does path_to_backing_file.xml
// have its contents updated?
My current solution is to have a SyncedLoggingSection class that inherits from LoggingSection and also takes a DllConfig instance in the constructor. It declares a new LogLevel that, when set, updates the LogLevel in the base class and also uses the given DllConfig to write the entire DllConfig out to the backing XML file. Is this a good technique?
I don't think I can just serialize SyncedLoggingSection by itself to the backing XML file, because not all of the contents will be written, just the <logging> node. Then I'd end up with an XML file containing only the <logging> section with its updated <logLevel>, instead of the entire config file with <logLevel> updated. Hence, I need to pass an instance of DllConfig to SyncedLoggingSection.
It seems almost like I want an event handler, one in DllConfig that would notice when particular properties (i.e., LogLevel) in its properties (i.e., Logging) were set. Is such a thing possible?
Since you are implementing IXmlSerializable yourself, you can make DllConfig.Logging private. Then add a public method on DllConfig to set LogLevel instead of setting Logging.LogLevel directly. That will be the only way you can change the LogLevel. In the implementation of that public method, you can serialize the whole DllConfig to overwrite the xml.
Related
I'm trying to unit test a custom ConfigurationSection I've written, and I'd like to load some arbitrary configuration XML into a System.Configuration.Configuration for each test (rather than put the test configuration xml in the Tests.dll.config file. That is, I'd like to do something like this:
Configuration testConfig = new Configuration("<?xml version=\"1.0\"?><configuration>...</configuration>");
MyCustomConfigSection section = testConfig.GetSection("mycustomconfigsection");
Assert.That(section != null);
However, it looks like ConfigurationManager will only give you Configuration instances that are associated with an EXE file or a machine config. Is there a way to load arbitrary XML into a Configuration instance?
There is actually a way I've discovered....
You need to define a new class inheriting from your original configuration section as follows:
public class MyXmlCustomConfigSection : MyCustomConfigSection
{
public MyXmlCustomConfigSection (string configXml)
{
XmlTextReader reader = new XmlTextReader(new StringReader(configXml));
DeserializeSection(reader);
}
}
You can then instantiate your ConfigurationSection object as follows:
string configXml = "<?xml version=\"1.0\"?><configuration>...</configuration>";
MyCustomConfigSection config = new MyXmlCustomConfigSection(configXml);
Hope it helps someone :-)
I think what you're looking for is ConfigurationManager.OpenMappedExeConfiguration
It allows you to open a configuration file that you specify with a file path (wrapped inside a ExeConfigurationFileMap)
If what the other poster said is true, and you don't wish to create a whole new XML file for testing, then I'd recommend you put your Configuration edits in the Test method itself, then run your tests against the freshly changed configuration data.
Looking at the members of the class, I'd say the answer is probably no*. I'm not sure why you'd want to do this anyway, rather than create your own XML configuration file.
*That's no, excluding messy reflection hacks
i have a class library, with domain objects (linq based objects, in .net
4.0).
i want to have this library use connection strings for its own app.config.
there are some problems:
following the model used by Settings : ApplicationSettingsBase, i created a
wrapper that should read the settings from app.config.
however, unlike the model, i provide default values.
the problem is that the properties which should load the data from app.config
internal sealed class ApplicationSettings : ApplicationSettingsBase
{
// other properties
[SpecialSetting( SpecialSetting.ConnectionString )]
[global::System.Configuration.DefaultSettingValueAttribute("Server=.;Database=RFPLUSSTD;User Id=SYSADM;Password=SYSADM;")]
public string MainConnectionString
{
get { return (string)this["MainConnectionString"]; }
}
// other properties
}
in app.config:
<connectionStrings>
<add name="MainConnectionString"
connectionString="..."
providerName="System.Data.SqlClient" />
</connectionStrings>
now, this doesn't work...
i tried to set the name of the conn string to the fully qualified name like
namespace.class.property, but to avail.
i don't want to use the default Settings class, used by the dbml because
it compiles the settings :D and i can't change them without recompiling...
i'm already using a different model of app settings class in a project in
1.1, but i thought 3.5 has grown enough and have its own objects, that
work..
So, why is not working and how can i make it work?
Thank you
You need to make sure that you have permissions in the file system to make the change. I hope you have considered that. If you are sure that you changed the config file and if it only brings the default settings, it might be loading the config file from output bin folder not in the project root. If you are sure that the modification fails, please post the error message.
Updated:
Hi Jack, I think the main issue with your code is that it is creating new instance of the ApplicationSettings class every time and if the setting is in user scope, you will be having null value and then it results to default value every time.
You could easily do it with the built in Settings class. By default the Settings can only be accessed within the Assembly, internal sealed partial class Settings (in Settings.Designer.cs). If you change this to public sealed you will be able to access the Settings from any assembly and the next thing is you have to keep the setting to Application Scope not User scope. Once you have done these two, you can retrieve and save without any problem.
I'm trying to read an event log saved as an XML file from .Net / C#, the event log xml format looks (approximately) like this:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Events>
<Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'>...</Event>
<Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'>...</Event>
</Events>
Where the ... bit is a relatively complex series of types defined in a schema file event.xsd.
My plan was to use XSD.exe to generate a C# wrapper type for reading this event log XML from this schema file and then deserialise the XML using the XmlSerializer class, like so:
using (FileStream stream = File.OpenRead(filename))
{
XmlSerializer serialiser = new XmlSerializer(typeof(Events));
return (Events)serialiser.Deserialize(stream);
}
The trouble is that the schema file doesn't contain a definition for the Events element (because its not in the schema), and so the above doesn't compile as there is no Events type.
I've tried a couple of variations, including using the type EventType[] instead of Events (which resulted in the exception " was not expected."). I also attempting to craft my own C# container Events type:
public class Events
{
[XmlElement]
public EventType[] Items
{
get;
set;
}
}
However the above simply results in the Items array being null.
How can I read Events Logs saved to XML format from C#?
So I managed this by using the following class:
[Serializable]
[XmlType(AnonymousType = true)]
[XmlRoot(Namespace = "", IsNullable = false)]
public class Events
{
[XmlElement("Event", Namespace = "http://schemas.microsoft.com/win/2004/08/events/event")]
public EventType[] Items
{
get;
set;
}
}
I'm not entirely certain what it is that made this work where previously it failed (I suspect its the Namespace property), however I found this out by using xsd.exe to generate a schema from a saved event log file and and then again to generate C# classes from that file, like so:
xsd /c eventlog.xml
xsd /c eventlog.xsd eventlog_app1.xsd
(Because it writes two xsd files you need to name both of them on the command line). I then looked at the resulting C# and compared / experimented until it worked.
This is copied example from:
How to read custom config section in app.config in c#
I want to read following custom section from app.config:
<StartupFolders>
<Folders name="a">
<add folderType="Inst" path="c:\foo" />
<add folderType="Prof" path="C:\foo1" />
</Folders>
<Folders name="b">
<add folderType="Inst" path="c:\foo" />
<add folderType="Prof" path="C:\foo1" />
</Folders>
</StartupFolders>
And this is my case too. However, I don't want to create custom class for handling values, defining this class in web.config, and then finally using it. It is heavy-weight for my needs.
Instead I would like to do something very simple -- retrieve a section as XML. Then I could use regular Linq.Xml to parse it. This way, I don't need to create new classes per each section, I don't need to declare them. For my purpose it is sufficient on one hand, and minimal at the other (I do it once, key-value mapper for nested sections). I.e. perfect.
The only missing piece is (my question) -- how to get a web.config section as XML?
Note about the section:
it cannot be encoded, because it has to be edited by hand
it cannot be serialized for the same reason
So I am not looking for a workaround how to squeeze entire section as value in appSettings, but I am really looking for a method to get proper section as XML.
I would like to get it from ConfigManager (!), because this way I don't have to deal with resolving which web.config should I read, etc. I.e. less chance to make mistake than mimicing web.config precedence manually.
Forgive me for reminding this, but please avoid "answers", you shouldn't do this, use custom class per each section, etc. I already considered this, and opted against it.
I think you either have to do it manually and load the Web config into memory:
XmlDocument doc = new XmlDocument();
doc.Load(Server.MapPath("~/Web.config"));
Or you will need to create the custom configuration sections you want to avoid.
You can define a re-usable custom config section that exposes the section XML as you desire. The key is, that you don't have to define a different class for each custom config section.
For clarity, my project namespace is "ConsoleApp1" as is the assembly name (this appears in type definitions).
First, create a custom config section that exposes the XML reader:
public class XmlConfigSection : ConfigurationSection
{
public XmlReader Xml { get; private set; }
override protected void DeserializeSection(XmlReader reader)
{
Xml = reader;
}
}
You can then define any of your custom sections to use this class in the app.config:
<configSections>
<section name="StartupFolders" type="ConsoleApp1.XmlConfigSection, ConsoleApp1" />
<section name="AnotherCustomSection" type="ConsoleApp1.XmlConfigSection, ConsoleApp1" />
</configSections>
Then in your code, you can access raw XmlReader of the config section like this:
var xmlReader = (ConfigurationManager.GetSection("StartupFolders") as XmlConfigSection).Xml;
If you then want an XML string instead of the reader you can do something like this (though I'd suggest sticking with the XmlReader):
StringBuilder sb = new StringBuilder();
while (xmlReader.Read())
sb.AppendLine(xmlReader.ReadOuterXml());
var xmlStr = sb.ToString();
Totally untested but could you use something like this? :
ConfigurationSection exampleSection =
(ConfigurationSection)ConfigurationManager
.GetSection("system.web/exampleSection");
Then possibly use exampleSection.ElementInformation to get more info?
I'm trying to unit test a custom ConfigurationSection I've written, and I'd like to load some arbitrary configuration XML into a System.Configuration.Configuration for each test (rather than put the test configuration xml in the Tests.dll.config file. That is, I'd like to do something like this:
Configuration testConfig = new Configuration("<?xml version=\"1.0\"?><configuration>...</configuration>");
MyCustomConfigSection section = testConfig.GetSection("mycustomconfigsection");
Assert.That(section != null);
However, it looks like ConfigurationManager will only give you Configuration instances that are associated with an EXE file or a machine config. Is there a way to load arbitrary XML into a Configuration instance?
There is actually a way I've discovered....
You need to define a new class inheriting from your original configuration section as follows:
public class MyXmlCustomConfigSection : MyCustomConfigSection
{
public MyXmlCustomConfigSection (string configXml)
{
XmlTextReader reader = new XmlTextReader(new StringReader(configXml));
DeserializeSection(reader);
}
}
You can then instantiate your ConfigurationSection object as follows:
string configXml = "<?xml version=\"1.0\"?><configuration>...</configuration>";
MyCustomConfigSection config = new MyXmlCustomConfigSection(configXml);
Hope it helps someone :-)
I think what you're looking for is ConfigurationManager.OpenMappedExeConfiguration
It allows you to open a configuration file that you specify with a file path (wrapped inside a ExeConfigurationFileMap)
If what the other poster said is true, and you don't wish to create a whole new XML file for testing, then I'd recommend you put your Configuration edits in the Test method itself, then run your tests against the freshly changed configuration data.
Looking at the members of the class, I'd say the answer is probably no*. I'm not sure why you'd want to do this anyway, rather than create your own XML configuration file.
*That's no, excluding messy reflection hacks