Dictionary in Settings throws an exception? - c#

I want to save a dictionary in the default UserSettings class of my project.
I've tried the following in the code of the Settings file but it fails:
<Setting Name="MediaKeys" Type="System.Collections.ObjectModel.Dictionary<System.Input.Key, System.IO.FileInfo>" Scope="User">
<Value Profile="(Default)" />
</Setting>
I get this error message when I look at the UI for the Settings:
Could not load file or assembly 'System.IO.FileInfo>' or one of its
dependencies. The parameter is incorrect. (Exception from HRESULT:
0x80070057 (E_INVALIDARG))
Bearing in mind, please, that I will deal with the whole Serializable Dictionary at another point in time, why am I getting this exception? Even non-serializable objects (such as FileInfo, as I have tested) do not throw this exception.

.NET settings do not handle generics very well. This is what I do:
[XmlRoot("dictionary")]
public class MyDictionary : SerializableDictionary<int, string>
{
}

Related

ConfigurationErrorException on null DateTime

I'm suddenly running into a ConfigurationErrorException for a WPF app which was running fine for a number of months (it's a task tray application).
The exception is being thrown by this auto-generated code in Settings.Designer.cs:
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public global::System.DateTime LastBackup {
get {
return ((global::System.DateTime)(this["LastBackup"]));
}
set {
this["LastBackup"] = value;
}
}
When the app first runs after being installed LastBackup is undefined/null/empty, which is what's causing the exception.
Interestingly, the auto-generated code lacks a [global::System.Configuration.DefaultSettingValueAttribute("")] attribute, which all the other auto-generated properties have.
If this was my own code it'd be easy enough to fix. But since it's generated by the Settings subsystem, any change I make would be overwritten.
There are a number of ways to work around this problem, including abandoning the built-in Settings subsystem and rolling my own configuration system. But I'm curious as to other approaches used to deal with undefined or null settings.
I was able to reproduce this error by altering App.config.
This is the default userSettings section I get when I create setting (named "Setting", sorry for the lack of imagination) in the VS Settings editor with an empty "Value" cell.
<userSettings>
<WPFTreeViewItemWrap.Properties.Settings>
<setting name="Setting" serializeAs="String">
<value />
</setting>
</WPFTreeViewItemWrap.Properties.Settings>
</userSettings>
This returns a non-null DateTime equal to {1/1/0001 12:00:00 AM}:
var x = Properties.Settings.Default.Setting;
But I get the same exception as you if I remove the empty <value /> element, like so:
<userSettings>
<WPFTreeViewItemWrap.Properties.Settings>
<setting name="Setting" serializeAs="String"></setting>
</WPFTreeViewItemWrap.Properties.Settings>
</userSettings>
System.Configuration.ConfigurationErrorsException: 'Required attribute 'value' not found.'
I would look at App.config. It may have been changed.

Replace #ifdefs in C# DLL

I have a C++ DLL which has #defines used like (these defines are automatically defined based on the build configuration, e.g. Debug, Release, etc)
#if defined(CONSTANT)
..
// Some code
#else
// Some other code
I need same functionality in C# dll.
Is it ok if I define some global constants in C# dll and use them
instead of defines?
e.g.
if(Globals.SomeConstant == SOMEVALUE)
// Do this
else
// Do smth else
Then when I want to ship the DLL I will in advance (probably as a default value during declaration) assign SOMEVALUE to Globals.SomeConstant - will this work this way? (Depending on which configuration I need).
I saw some similar questions but they weren't about DLLs.
You can use it similarly as in c++
You can define / undefine them in your source code or as a conditional compilation symbol. In visual studio this can be done using Solution Explorer - Properties - Build - conditional compilation symbols
However, nowadays people tend to use a configuration file for these constants. This way, you don't have to recompile your source code nor redistribute it to change the behaviour.
The most easy method is via visual studio solution explorer - properties - settings
You can add settings for most types. Booleans come closest to #define. Using an int can give you more than two possibilities. See the difficulties if you wanted to be able to use several values for a TimeSpan or an URI using #define.
The nice thing about using the settings is that a class is generated for you to easily access the settings.
Another method is to read the config file directly using the System.Configuration.ConfigurationManager class. This gives you more freedom about the format of the configuration. The disadvantage is that you have to convert the read values into proper types yourself, inclusive handling errors if the value can't be read.
Summarized: advantages of the config file method:
No need to change source files
No need to recompile
No need to re-install
only change the config file on those machines that need the change
improved type safety
My previous answer lead to a more questions than it answered. Hence I thought an example would help.
Suppose I have a DLL, called MyDll. It has a configuration setting that in really old times would have been defined using #define.
My C-synctax is a bit rusty, but it would look like:
#define UseAlternateGreeting
public class MyClass
{
public string GetGreeting()
{
#if defined UseAlternateGreeting
return "Hello World!";
#else
return "Here I am!";
#endif
}
}
Now suppose we have several programs that use this DLL. Program! wants to use the default setting. However Program2 wants to use the alternate setting. There is no way to solve this.
Besides if we want to change the value of the setting we have to recompile and redistribute everything to everyone.
Wouldn't it be easier if we could just edit a file with notepad to change the string?
Luckily Microsoft also saw the advantage of this. Over more than 10 years we have the idea of configuration files. Assemblies have a config file with the name of the application and the extension config. This file can easily be edited using any text editor by those who know what the configuration items mean.
If we replace the #define with an item in the config file the greeting could be changed to the alternate greeting without having to recompile and redistribute the whole program.
Luckily Visual Studio helps us a lot when creating the config file.
Preparations
Let Visual Studio Create a console application in a new Solution: name the program ConfigExample
Add a new Library to this application, name it MyDll
View the properties of MyDll
Add a Setting.
Name: MySetting,
Type: string,
Scope: application,
Value: Hello World! (without string quotes)
In project MyDll create a class MyClass
public class MyClass
{
public string GetText()
{
return Properties.Settings.Default.MySetting;
}
}
Go to project ConfigExample
Project Add reference to MyDll (via tab page solution)
Use the code in your main:
using MyDll;
static void Main(string[] args)
{
var obj = new MyClass();
var txt = obj.GetText();
Console.WriteLine(txt);
}
Compile and run, and you'll see the proper text displayed. If you go to the debug / bin directory of the program you'll find a text file ConfigExample.config. Open it in a text editor and you'll see... nothing abut hello world!
This means that your program is not really interested in a special setting, the setting that was default the time that MyDll was built may be used.
However, if you want to use a special setting,
In visual Studio, Go to project MyDll
open file app.config
a.o. you'll find the following
(to prevent the editor interfering with the formatting, I added an apostrophe to each line)
'</configSections>
'<applicationSettings>
'<MyDll.Properties.Settings>
'<setting name="MySetting" serializeAs="String">
'<value>Hello World!</value>
'</setting>
'</MyDll.Properties.Settings>
'</applicationSettings>
Copy paste this part to ConfigExample.Config
for all already distributed programs do this in the folder where the executable is (in your case: debug/bin)
for all ConfigExample programs that will be built in the future do this in visual studio in App.Config of the ConfigExample project.
The result will be as follows:
'<?xml version="1.0" encoding="utf-8" ?>
'<configuration>
' <configSections>
' <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="MyDll.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
' </sectionGroup>
' </configSections>
' <applicationSettings>
' <MyDll.Properties.Settings>
' <setting name="MySetting" serializeAs="String">
' <value>Hello World!</value>
' </setting>
' </MyDll.Properties.Settings>
' </applicationSettings>
'
' <startup>
' <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
' </startup>
'</configuration>
Now all we have to do is change the Hello World into an alternat greeting
' <MyDll.Properties.Settings>
' <setting name="MySetting" serializeAs="String">
' <value>Here I am!</value>
' </setting>
' </MyDll.Properties.Settings>
Run the program without building it and you'll see that the new value is used.
Advantages:
- It works with a lot of types that can be assigned from string - Type.IsAssignableFrom(typeof(string)). Visual Studio already supports a lot of types including TimeSpan and DateTime.
- You don't have to recompile your source code to change the value
- Several executables can use their own configuration setting: one program could use the original greeting, the other can use the alternate one
- If your program doesn't provide a value in the config file the default value is used.
- You don't have to read the configuration yourself.
- It is type safe: if you say it is a TimeSpan, then you have to do some serious typing to confuse it with for example an integer.
Well there is a lot more to be said about configuration, you can even have a configuration per user. But that's far outside your question about alternatives for plain C #define

System.TypeLoadException: Could not load type 'x' from assembly 'x'?

I would just like to apologise by the overload of information, I just want to provide anything that could be related, I'm not sure what's happening
Test method DCIM_Test_1.MySQLDefTest.ConnectionStringTest threw exception:
System.TypeLoadException: Could not load type 'DCIM_With_Test.Database.Database_MySQLDef' from assembly 'DCIM, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
I am getting this error when I run one of my tests, and I believe it's coming from how I re-named my Solution and Projects recently.
I renamed them all from 'DCIM_With_Test' to 'DCIM', (Changed the directories, file names etc.) but it is still causing inconsistencies here and there.
Another cause could possibly be the way the method that is causing the issue is running. I have changed the method from reading from a text file to reading from an app.config file and I am new to this process and XML so I could be missing a step somewhere.
Here is my app.config:
<?xml version="1.0"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
<connectionStrings>
<add name="mySqlConnectionString" connectionString="server=localhost;database=dcim;uid=root;pwd=LlmD62jL;"/>
</connectionStrings>
</configuration>
I am also getting three messages about app.config:
Could not find the schema information for the element 'supportedRuntime'.
Could not find the schema information for the attribute 'version'.
Could not find the schema information for the attribute 'sku'.
Here is the method being tested NOTE: The OLD METHOD runs fine, but the test still throws the exception, but the NEW METHOD doesn't work at all.
public static string GetConnectionString()
{
/* OLD METHOD
StreamReader rdr = new StreamReader(#"C:\Users\Benjamin\Documents\setupfile.txt");
mySqlConnectionString = rdr.ReadToEnd();
rdr.Close();
*/
// NEW METHOD
mySqlConnectionString = ConfigurationManager.ConnectionStrings["mySqlConnectionString"].ConnectionString;
return mySqlConnectionString;
}
And here is the test:
[TestMethod()]
public void ConnectionStringTest()
{
int actual = 0;
int expected = 1;
Database_MySQLDef.GetConnectionString();
if (Database_MySQLDef.mySqlConnectionString != "")
{
actual = 1;
}
else { actual = 0; }
Assert.AreEqual(expected, actual);
}
Does your test assembly have an app.config?
my test project doesn't but my main project does. Not sure if that's the same thing?
No, they aren't. I suspect what is happening is that ConfigurationManager.ConnectionStrings is going to the test assembly's app config (this is MSTest's behavior, I believe), not the your main project's app config. Then, because that doesn't exist, you are getting an Exception thrown in a static initializer or static constructor and this is wrapped by the TypeLoadException.
A simple way to solve this is to just add an app.config to your test assembly. Then you can put in whatever connection string you need for the tests.

Losing precision when saving a DateTimeOffset setting in project settings

When I save a DateTimeOffest in my project settings, I'm losing some precision :
The first variable is the original value, before serialization.
The second is the value after Deserialization.
In fact my variable is serialized like this in the config file :
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<userSettings>
<MyApp.Properties.Settings>
[...]
<setting name="LatestCheckTimestamp" serializeAs="String">
<value>02/22/2013 14:39:06 +00:00</value>
</setting>
[...]
</MyApp.Properties.Settings>
</userSettings>
</configuration>
Is there a way to specify some serialization parameters to increase precision ?
I know I can use some workaround, for example by storing the Ticks and the offset value or something like that, but I d'like to know if there's not a better way.
EDIT :
More info : I'm using the standard Visual Studio project settings to store my value :
MyApp.Settings.Default.LatestCheckTimestamp = initialLatestCheckTimestamp;
MyApp.Settings.Default.Save();
MyApp.Settings is the class generated by Visual studio when you edit settings in the project properties page.
EDIT 2 : Solution :
Base on the answer of Matt Johnson, this is what I did :
Renamed the setting from LatestCheckTimestamp to LatestCheckTimestampString but not in my code
Added the following Wrapper in an independent file to complete the partial class Settings :
.
public DateTimeOffset LatestCheckTimestamp
{
get { return DateTimeOffset.Parse(LatestCheckTimestampString); }
set { LatestCheckTimestampString = value.ToString("o"); }
}
The new config file now looks like :
<configuration>
<userSettings>
<MyApp.Properties.Settings>
[...]
<setting name="LatestCheckTimestampString" serializeAs="String">
<value>2013-02-22T16:54:04.3647473+00:00</value>
</setting>
</MyApp.Properties.Settings>
</userSettings>
</configuration>
... and my code still is
MyApp.Settings.Default.LatestCheckTimestamp = initialLatestCheckTimestamp;
MyApp.Settings.Default.Save();
The most reliable way to serialize a DateTimeOffset is with the RoundTrip pattern, which is specified with the "o" standard serialization string.
This uses the ISO8601 standard, which is highly interoperable with other systems, languages, frameworks, etc. Your value would look like this: 2013-02-22T14:39:06.0000000+00:00.
.Net will store fractional seconds to 7 decimals with this format.
If you can show some code of how you are storing and retrieving your app setting, I can show you where to specify the format string. In most cases, its simply .ToString("o").

ReadOnlyNameValueCollection (reading from ConfigurationManager.GetSection)

Ok, so.....
<section name="test" type="System.Configuration.NameValueFileSectionHandler" />
<test>
<add key="foo" value="bar" />
</test>
var test = ConfigurationManager.GetSection("test");
So far so good. The debugger shows test contains one key, foo.
But GetSection returns object, so we need a cast:
var type = test.GetType();
// FullName: System.Configuration.ReadOnlyNameValueCollection
// Assembly: System
Ok, this should be simple enough. So....
using System;
var test = ConfigurationManager
.GetSection("test") as ReadOnlyNameValueCollection;
error!
The type or namespace ReadOnlyNameValueCollection does not exist in the namespace System.Configuration. Are you missing an assembly reference?
err... wtf?
A cast to System.Collections.Specialized.NameValueCollection gets the code working, but I don't really understand why the error.
And a search for ReadOnlyNameValueCollection on MSDN shows there is no documentation on this class at all. It doesn't seem to exist. Yet I have an instance of that type in my code.
System.Configuration.ReadOnlyNameValueCollection is an internal class to the System.dll assembly. So you can't refer to it from your code. It derives from System.Collections.Specialized.NameValueCollection, though, so that's why you're able to do that with the cast.

Categories