C# deserialize a class - the "default" value of a variable - c#

I save local user settings to xml file. Program contains "Settings" class that serialize when the program is closed and deserialize when it is started next time.
But the problem is that the program is changed all the time, and when I create next version - I want the user settings to be saved. But the program may contains new fields of settings, and then the program will started and deserialised the old xml file - new fields will be null.
Now I check every fields as hard-code in the program, as like:
Settings sts = (Settings)Deserialise(path);
if(sts.Field2 == null) sts.Field2 = "defaultvalue2";
if(sts.Field3 == null) sts.Field3 = "defaultvalue3";
Of course it is not satisfied for me. Is it possible to do "default" value of a variable as the same time when I change code of Settings class? Like this:
class Settings
{
public string Field1 (DefaultValue: "defaultvalue1");
public string Field2 (DefaultValue: "defaultvalue2");
}
public void Main
{
Settings sts = (Settings)Deserialise(path);
foreach(var fld in typeof(sts))
{
if(fld.Value == null)
fld.Value = Settings.Fields[fld].DefaulValue;
}
}

Yes it is possible, simply use the standard way to set standard values:
class Settings
{
public string Field1 = "defaultvalue1";
public string Field2 = "defaultvalue2";
}
public void Main
{
Settings sts = (Settings)Deserialise(path);
/* not needed
foreach(var fld in typeof(sts))
{
if(fld.Value == null)
fld.Value = Settings.Fields[fld].DefaulValue;
}*/
}

https://learn.microsoft.com/zh-tw/dotnet/api/system.xml.serialization.xmlattributes.xmldefaultvalue?view=netcore-3.1
here I google it . maybe try it?
Use default attribute : DefaultValueAttribute
public class Pet
{
// The default value for the Animal field is "Dog".
[DefaultValueAttribute("Dog")]
public string Animal ;
}

The Settings.settings xml file was designed for static project settings and, using user scoped settings, can be saved at runtime. Are you changing the settings so much that it no longer have the 'old' values or just adding to the list of settings?
If just adding, you don't need to loop through the settings one by one and try to guess their types with values as you can just do this:
int myInteger = Properties.Settings.Default.MyIntegerSettingValue;
And writing to the settings file:
Properties.Settings.Default.MyIntegerSettingValue = myInteger;
So if you cannot replace your settings.xml file, my suggestion is to model your settings to a class that contain all of your settings loaded at runtime and for each one missing, just write it out to the Settings file with your default value:
Properties.Settings.Default.MyMissingSetting = "MyDefaultValue"
You can find some nice info on application settings usage here

Related

Remove metadata from Excel file using C#?

I'm currently using C# to set the custom attributes of multiple excel files. I'm using an imported library from Microsoft known as DSOFile to write to the CustomProperties property. One issue that I'm running into is whenever the code attempts to write to an excel file that already has custom properties written to it, such as the Company and Year, a COMException exception is thrown to indicate the custom properties of the file already has a field with that name. Exact Message: "An item by that name already exists in the collection". I would like to be able to delete that item in the collection so that I can rewrite to the file. For example, if I accidentally added the wrong year to the year attribute in the file, I would like the ability to clear that field and write a new value to it. I was unable to find a method in the DSOFile class that removes metadata. Is there anyway to "programmatically" clear metadata from a file without doing it through the file properties window?
Sample Code:
DSOFILE.OleDocumentProperties dso = new DSOFile.OleDocumentProperties();
dso.Open(#"c\temp\test.xls", false, DSOFile.dsoFileOpenOptions.dsoOptionDefault);
//add metadata
dso.CustomProperties.Add("Company", "Sony");
dso.Save();
dso.Close(false);
If you want to change the default properties used by Office like Company or Author, you can just update them via the SummaryProperties object:
OleDocumentProperties dso = new DSOFile.OleDocumentProperties();
dso.Open(#"c:\temp\test.xls", false, DSOFile.dsoFileOpenOptions.dsoOptionDefault);
//Update Company
dso.SummaryProperties.Company = "Hello World!";
dso.Save();
dso.Close(false);
Note, that you cannot change the default properties of documents that you can access via the SummaryProperties object via the CustomProperties object in dso. The CustomProperties are meant for additional properties used by the user, not the ones already introduced by Microsoft Office.
In order to change the custom properties, you have to be aware that CustomProperties is a collection that you can iterate over via foreach. So you can use the following two methods:
private static void DeleteProperty(CustomProperties properties, string propertyName)
{
foreach(CustomProperty property in properties)
{
if (string.Equals(property.Name, propertyName, StringComparison.InvariantCultureIgnoreCase))
{
property.Remove();
break;
}
}
}
private static void UpdateProperty(CustomProperties properties, string propertyName, string newValue)
{
bool propertyFound = false;
foreach (CustomProperty property in properties)
{
if (string.Equals(property.Name, propertyName, StringComparison.InvariantCultureIgnoreCase))
{
// Property found, change it
property.set_Value(newValue);
propertyFound = true;
break;
}
}
if(!propertyFound)
{
// The property with the given name was not found, so we have to add it
properties.Add(propertyName, newValue);
}
}
Here is an example on how to use UpdateProperty:
static void Main(string[] args)
{
OleDocumentProperties dso = new DSOFile.OleDocumentProperties();
dso.Open(#"c:\temp\test.xls", false, DSOFile.dsoFileOpenOptions.dsoOptionDefault);
UpdateProperty(dso.CustomProperties, "Year", "2017");
dso.Save();
dso.Close(false);
}

Iterate through settings files

I'm currently working on a VSTO project for which I have 5 .settings files:
Settings.settings (Default)
s201213.settings
s201314.settings
s201415.settings
s201516.settings
Over time there will be more settings files included following the same naming convention ('s' followed by a tax year).
I know I can iterate through a settings file, but is there a way to iterate through the actual settings files themselves?
I've tried things such as:
public void Example()
{
System.Collections.IEnumerator testSetting = MyAddIn.Properties.s201213.Default.Properties.GetEnumerator();
while (testSetting.MoveNext())
{
System.Diagnostics.Debug.WriteLine("Setting:\t" + testSetting.Current.ToString());
}
}
Which obviously only iterates through a single settings file, but I can't seem to figure out the logic of iterating through all the settings files as a collection, without explicitly naming each one in the code. I hope that makes sense, any help is appreciated.
Update:
I think I'm getting somewhere with the following code:
foreach(Type test in Assembly.GetExecutingAssembly().GetTypes())
{
if (System.Text.RegularExpressions.Regex.IsMatch(test.Name, "^s[0-9]{6}$"))
{
PropertyInfo value = test.GetProperty("LEL");
try
{
System.Diagnostics.Debug.WriteLine("Name:\t" + test.Name +
"\nNameSpace:\t" + test.Namespace +
"\nProperty:\t" + test.GetProperty("LEL").ToString() +
"\n");
}
catch(Exception e)
{
System.Diagnostics.Debug.WriteLine(e.Message);
}
}
}
Which seems to be recognising the settings files and the stored values:
Output:
Name: s201213
NameSpace: MyAddIn.Properties
Property: Double LEL
Name: s201314
NameSpace: MyAddIn.Properties
Property: Double LEL
Name: s201415
NameSpace: MyAddIn.Properties
Property: Double LEL
Name: s201516
NameSpace: MyAddIn.Properties
Property: Double LEL
However I can't seem to get the actual value of the "LEL" setting which should return a Double?
2nd Update
I've actually given up and decided to use a local DB instead - but I would still like to know if this is possible, and I think other people would like to know too so I'm going to throw a bounty at it to try and generate some interest.
Jeremy's answer got me to the finish post, but thought I'd post the final code I used so that it can be seen in context:
public void GetLEL()
{
var fileMap = new ConfigurationFileMap(AppDomain.CurrentDomain.BaseDirectory + #"CustomAddIn.dll.config");
var configuration = ConfigurationManager.OpenMappedMachineConfiguration(fileMap);
var sectionGroup = configuration.GetSectionGroup("userSettings");
var section = (ClientSettingsSection)sectionGroup.Sections.Get("MyAddIn.s201213");
var setting = section.Settings.Get("LEL");
System.Diagnostics.Debug.WriteLine(setting.Value.ValueXml.InnerXml);
// Prints "107" as expected.
}
The answer is really simple once you see it (no need to iterate through Types nor use System.IO directory):
using System.Configuration; //Add a reference to this DLL
...
var fileMap = new ConfigurationFileMap(Application.StartupPath + #"\GetSettingFilesValues.exe.config");
var configuration = ConfigurationManager.OpenMappedMachineConfiguration(fileMap);
var sectionGroup = configuration.GetSectionGroup("applicationSettings"); // This is the section group name, change to your needs, ie application or user
var section = (ClientSettingsSection)sectionGroup.Sections.Get("GetSettingFilesValues.Properties.s201415"); //This is the section name, change to your needs (you know the tax years you need)
var setting = section.Settings.Get("LEL");
System.Diagnostics.Debug.WriteLine(setting.Value.ValueXml.InnerXml);
I agree with your approach to use the dB, though I'm sure this will benefit other people too.
I believe you can use the System.IO namespace classes for iterating through files with the same extension. There is no built-in mechanisms or properties for that.

NRefactory: How do I access unresolved Named Arguments on a Property Attribute?

I apologize in advance for the long description of a simple question but I want to make sure people properly understand what I'm trying to do.
Background
I'm writing a tool that can read in a file generated by SqlMetal and create a class that contains methods for simple Inserting, Updating, Deleting and Selecting, which can then be exposed to a web service. The main advantage here is that if a table changes, I simply have to re-run the tool and the database-related code is automatically updated and everywhere that uses it will generate compile errors, making it easy to track down where manual changes need to be made. For example, if I have a Customer table that has the following fields:
CustomerId (PK, Identity)
FirstName
LastName
I want to be able to generate Insert and Delete methods as follows:
// I only want non-database-generated fields to be parameters here.
public static Customer InsertCustomer(String firstName, String lastName)
{
...
}
// I only want the primary key fields to be parameters here.
public static int DeleteCustomer(int customerId)
{
...
}
I am using SqlMetal to generate a Customer class. Now what I want to do is read that .cs file into my new tool in order to create another class with the above methods. This new class can then be exposed to the web service to grant access to this functionality without having to expose the underlying database. I am using NRefactory to read in the SqlMetal-generated file and so far, it's going well but I've run into a snag trying to read the property attributes on my Customer class.
SqlMetal generates its classes using a ColumnAttribute to identify each property that is derived from a database column. The ColumnAttribute will have a number of arguments to describe the database column's properties. In the above example, it would generate something like this:
...
[Column(Name="customerId", Storage="_CustomerId, DbType="INT NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
public int CustomerId
{
...
}
[Column(Name="firstName", Storage="_FirstName", DbType="NVarChar(100) NOT NULL", CanBeNull=false)]
public String FirstName
{
...
}
[Column(Name="lastName", Storage="_LastName", DbType="NVarChar(100) NOT NULL", CanBeNull=false)]
public String LastName
{
...
}
...
Problem
As you can see, SqlMetal gives me the attributes I need in order to identify which columns are database-generated and which ones are part of the primary key. So when I read this file into NRefactory and resolve the type, I would expect to be able to get at all of this information. However, I'm finding that while I can get to the ColumnAttribute, all of the arguments on it are unresolved and therefore aren't accessible via the NamedArguments or PositionalArguments properties.
Here's my code:
SyntaxTree syntaxTree = ...;
foreach(AstNode tableNode in syntaxTree.Children)
{
ResolveResult result = resolver.Resolve(tableNode);
var properties = result.Type.GetProperties();
foreach (IProperty p in properties)
{
var attributes = p.Attributes;
bool isPrimaryKeyField = false;
bool isDbGenerated = false;
bool isColumn = false;
foreach (IAttribute attr in attributes)
{
if (attr.AttributeType.Name == "Column")
{
isColumn = true;
foreach (var arg in attr.NamedArguments) // NamedArguments contains no items.
{
if (arg.Key.Name == "IsPrimaryKey")
{
isPrimaryKeyField = (bool)arg.Value.ConstantValue == true;
}
if (arg.Key.Name == "IsDbGenerated")
{
isDbGenerated = (bool)arg.Value.ConstantValue == true;
}
}
}
}
if (isColumn)
{
... // Create a parameter as appropriate.
}
}
}
This all works until I try to loop through the IAttribute.NamedArguments because the collection contains no elements. However, when I go through the debugger and examine the value of 'attr', I can see that there is a private variable called 'unresolved', which contains a list of all the arguments I want but I can find no way to access this through code.
How do I get at the contents of this 'unresolved' variable? Do I need to do something more with the Resolver? This is my first time using NRefactory so I'm not overly familiar with all the nuances yet. I've been having a tough time finding an example that goes into this level of depth on Google and the documentation I've seen for NRefactory doesn't seem to cover it. Any help would be appreciated.
I figured it out. I needed to load the assembly for System.Data.Linq into the IProjectContent before resolving the SyntaxTree.
...
CecilLoader loader = new CecilLoader();
Assembly[] assembliesToLoad = {
...
typeof(System.Data.Linq.Mapping.ColumnAttribute).Assembly
...};
IUnresolvedAssembly[] projectAssemblies = new IUnresolvedAssembly[assembliesToLoad.Length];
for(int i = 0; i < assembliesToLoad.Length; i++)
{
projectAssemblies[i] = loader.LoadAssemblyFile(assembliesToLoad[i].Location);
}
IProjectContent project = new CSharpProjectContent();
project = project.AddAssemblyReferences(projectAssemblies);
...

C# Dynamically Get Variable Names and Values in a Class

I am currently making a program (C# .Net 4) that has multiple options, which are saved to a file.
These options are their own variables in-code, and I was wondering if there was a way to get the variables and values of these options dynamically in code.
In my case, I have these options in a "Settings" class, and I access them from my main form class using Settings.varSetting.
I get and set these variables in multiple places in code; is it possible to consolidate the list of variables so that I can access and set them (for example, creating a Settings form which pulls the available options and their values and draws the form dynamically) more easily/consistently?
Here are the current variables I have in the Settings class:
public static Uri uriHomePage = new Uri("http://www.google.com");
public static int intInitOpacity = 100;
public static string strWindowTitle = "OpaciBrowser";
public static bool boolSaveHistory = false;
public static bool boolAutoRemoveTask = true; //Automatically remove window from task bar if under:
public static int intRemoveTaskLevel = 50; //percent
public static bool boolHideOnMinimized = true;
Thanks for any help,
Karl Tatom ( TheMusiKid )
You might want to consider using the Application Settings features built into the framework for loading and storing application settings.
var dict = typeof(Settings)
.GetFields(BindingFlags.Static | BindingFlags.Public)
.ToDictionary(f=>f.Name, f=>f.GetValue(null));
read about reflections:
http://msdn.microsoft.com/en-us/library/ms173183%28v=vs.100%29.aspx

Saving simple user preferences on Windows Forms with C#

I'm writing my first Windows Forms application using VS 2010 and C#. It does not use a database but I would like to save user settings like directory path and what check boxes are checked. What is the easiest way to save these preferences?
I suggest you to use the builtin application Settings to do it. Here is an article talking about it.
Sample usage:
MyProject.Properties.Settings.Default.MyProperty = "Something";
You can use the serializable attribute in conjunction with a 'settings' class. For small amount of information this is really your best bet as it is simple to implement. For example:
[Serializable]
public class MySettings
{
public const string Extension = ".testInfo";
[XmlElement]
public string GUID { get; set; }
[XmlElement]
public bool TurnedOn { get; set; }
[XmlElement]
public DateTime StartTime { get; set; }
public void Save(string filePath)
{
XmlSerializer serializer = new XmlSerializer(typeof(MySettings));
TextWriter textWriter = new StreamWriter(filePath);
serializer.Serialize(textWriter, this);
textWriter.Close();
}
public static MySettings Load(string filePath)
{
XmlSerializer serializer = new XmlSerializer(typeof(MySettings));
TextReader reader = new StreamReader(filePath);
MySettings data = (MySettings)serializer.Deserialize(reader);
reader.Close();
return data;
}
}
There you go. You can prety much cut and paste this directly into your code. Just add properties as needed, and don't forget the [XMLElement] attribute on your interesting properties.
Another benefit to this design is that you don't have to fiddle with the cumbersome Application.Settings approaches, and you can modify your files by hand if you need to.
I'd save the settings in an XML file. That way it's easy for the user to share their settings across machines etc.
You'll also be able to deserialize the XML as a class in your application, giving you easy access to the settings you require.
The easiest way would be in the app.config settings which you can set in the designer under project properties settings (make sure you set them as user setting not application settings or you wont be able to save them) you can then read and write them with C#
to read write just access properties on
Properties.Settings.Default.<your property>
there are also methods to save the properties to the users profile or to reset to defaults
Properties.Settings.Default.Reset();
Properties.Settings.Default.Save();
http://msdn.microsoft.com/en-us/library/a65txexh.aspx
What about adding a local dataset to your project (then create a setting table) and export the data finally to an xml file, its easy to use and more fixable
1- add columns (e.g.; settingName and settingValue) to your local table (DataTable1) using the designer,
then
//data set
DataSet1 ds = new DataSet1();
DataSet1.DataTable1DataTable settingsTable = (DataSet1.DataTable1DataTable)ds.Tables[0];
//add new setting
settingsTable.Rows.Add(new string[] { "setting1", "settingvalue1" });
//export to xml file
settingsTable.WriteXml("settings.xml");
//if you want to read ur settings back... read from xml
settingsTable.ReadXml("settings.xml");

Categories