Итак, есть у нас некий набор параметров программы, который надо сохранить при задании его пользователем, и восстановить при запросе из основной программы, т.е. файл конфигурации.
Обычно под управление конфигурацией делается отдельный класс, задача которого сохранить/загрузить конфиг и в нужный момент выдать запрашиваемый параметр. В качестве хранилища данных можно использовать DataSet. Во-первых, потому что все параметры можно представить в удобном виде типа таблицы базы данных, а во-вторых, DataSet
умеет сохранять свое содержимое в XML и загружать его в автоматическом режиме. Но вот с заполнением DataSet возникают некоторые проблемы. Обычно я заполнял его почти вручную, что приводило к появлению некрасивых простыней кода, в которых, к тому же, легко допустить опечатку. А добавление нового параметра, приводило к необходимости добавлять его в несколько мест в коде.
Как оказалось, можно все сделать гораздо проще - обойти в автоматическом режиме все поля класса, собрать или загрузить в них все нужные значения, а также построить таблицу в DataSet
, содержащую все необходимые поля нужного типа.
Надо подключить пространства имен
System.Data
и System.Reflection
using System.Data;
using System.Reflection;
и завести приватные переменные, собственно
DataSet
, переменную под имя конфиг-файла и переменную под имя таблицы:private string configFile = "";
private string TableName = "";
private DataSet dsNetConfig = new DataSet();
Далее в конструкторе класса запрашиваем имя файла конфигурации, формируем имя таблицы и вызываем функцию, создающую таблицу
DataSet
со всеми необходимыми полями, которые будут хранить значения свойств класса.public NetSettings(string filename)
{
configFile = filename;
TableName = this.GetType().Name;
CreateDataSet();
}
private void CreateDataSet()
{
dsNetConfig.Tables.Add(TableName);
PropertyInfo[] properties = this.GetType().GetProperties();
foreach (PropertyInfo pr in properties)
{
dsNetConfig.Tables[TableName].Columns.Add(pr.Name,
pr.PropertyType);
}
}
- Добавляем в
DataSet
таблицу- Получаем список свойств класса в виде массива PropertyInfo:
PropertyInfo[] properties = this.GetType().GetProperties();
- В цикле
foreach
создаем колонки в таблице DataSet
, задавая имя и тип данных:dsNetConfig.Tables[TableName].Columns.Ad d(pr.Name, pr.PropertyType);
public bool SaveConfig()
{
// [...]
ConfigError = null;
dsNetConfig.Tables[TableName].Rows.Clear();
DataRow dr = dsNetConfig.Tables[TableName].NewRow();
PropertyInfo[] properties = this.GetType().GetProperties();
foreach (PropertyInfo pr in properties)
{
string propName = pr.Name;
object propValue = pr.GetValue(this,null);
dr[propName] = propValue;
}
dsNetConfig.Tables[TableName].Rows.Add(dr);
try
{
dsNetConfig.WriteXml(configFile);
}
catch (Exception ex)
{
ConfigError = ex.Message;
return false;
}
return true;
}
- Добавляем в таблицу новый
DataRow
(у меня строка должна быть всего одна, потому для начала очищаю содержимое таблицы на всякий случай)dsNetConfig.Tables[TableName].Rows.Clear( );
DataRow dr = dsNetConfig.Tables[TableName].NewRow();
- Далее опять же получаю массив
PropertyInfo
и обрабатываю его в цикле foreach
- Получаю имя поля:
string propName = pr.Name;
- И его значение:
object propValue = pr.GetValue(this,null);
Второй параметр в функции
GetValue
- индекс для свойств, имеющих индексацию. Например, если свойство является массивом, то нужно будет задавать индекс элемента для получения конкретного значения. В данном случае таких свойств в классе нет, посему в качестве второго параметра указывается null
.- Записываю значение на свое место в
DataSet
:dr[propName] = propValue;
- Добавляю в таблицу сформированную строку:
dsNetConfig.Tables[TableName].Rows.Add(d r);
- Сохраняю содержимое
DataSet
в XML:dsNetConfig.WriteXml(configFile);
В разбираемом примере есть такое свойство
public string ConfigError
, хранящее сообщение об ошибке при загрузке/сохранении конфига, но в самом конфигурационном файле не нужное.Тут три пути:
1. Самый простой. Забить и плюнуть, ненужное свойство будет сохраняться в конфиге, загружаться из него, да и пусть.
2. Компромиссный. Обнулить свойство перед сохранением. Тогда оно будет как поле в таблице
DataSet
, но в конфиге его не будет. Так сделано в разбираемом классе Способ хорош, если таких ненужных свойств мало. И место в файле оно занимать не будет, и не нужны дополнительные проверки.3. Составить список, хоть в виде строковой переменной, где перечислить "лишние" поля, и проверять список перед сохранением и созданием таблицы.
public NetConfigStatus LoadConfig()
{
//[...]
try
{
dsNetConfig.ReadXml(configFile);
}
catch (Exception ex)
{
ConfigError = ex.Message;
return NetConfigStatus.Error;
}
//загрузка свойств класса из DataSet
if (dsNetConfig.Tables[TableName].Rows.Count > 0)
{
PropertyInfo[] properties = this.GetType().GetProperties();
foreach (PropertyInfo pr in properties)
{
string propName = pr.Name;
object propValue = dsNetConfig.Tables[TableName].Rows[0][propName];
if (propValue.GetType() != typeof(System.DBNull))
{
pr.SetValue(this, propValue, null);
}
}
//[...]
}
return NetConfigStatus.OK;
}
Все делается точно также, только в обратном порядке.
- Загружаем XML
- Получаем список свойств
- Устанавливаем значения в цикле с помощью
SetValue
Необходимы только две проверки - на количество записей в таблице
DataSet
и на то, не является ли значение ячейки DBNull
Весь код класса на PasteBin
Киберфорум
MSDN
Это репост с сайта http://tolik-punkoff.com
Оригинал: http://tolik-punkoff.com/2018/05/15/c-o-k