[Setting up WLAN in Debian Squeeze 6.0.6 stable for HAMA WLAN USB-Stick]
После установки дистрибутива Debian-Stable 6.0.6 Squeeze не работал WLAN.
На борту находился WLAN-USB-Stick HAMA 62734.
Для его поднятия опирался на это руководство. Итак, по пунктам:
- Установил два пакета для работы с WLAN:
"apt-get install wireless-tools wpasupplicant"
- Команда "lspci" устройства, как-то обозначающиего WLAN, не находила, но нагуглил что мой стик базируется на чип-сете RT73USB.
- В "/etc/apt/sources.list" добавил строки:
deb http://ftp.de.debian.org/debian squeeze contrib non-free
deb-src http://ftp.de.debian.org/debian squeeze contrib non-free
Хотя, конечно же, можно было бы добавить "contrib non-free" к уже существующим источникам с флагом "main"
- Обновил список пакетов командой:
"aptitude update"
- Через Synaptic Менеджер Пакетов нашел необходимый для этого устройства пакет драйверов: "firmware-ralink". Здесь же его и установил.
- Перегрузил сеть командой: "/etc/init.d/networking restart"
- Проверил, что драйвера загружены командой "lsmod | grep ке73", которая выдает:
rt73usb 17605 0
crc_itu_t 1035 1 rt73usb
rt2x00usb 5673 2 rt73usb,rt2500usb
rt2x00lib 19101 3 rt73usb,rt2500usb,rt2x00usb
usbcore 99426 7 rt73usb,rt2500usb,rt2x00usb,usbhid,uhci_hcd,ehci_hcd
- Команда "iwconfig" должна отобразить для устройства wlan0 строку начинающуюся с "IEEE ..."
- Устройство готово к работе.
Сетевые настройки предпочитаю делать с помощью NetworkManager. Если он еще не установлен, можно это сделать следующей командой: "apt-get install network-manager-gnome". Но менеджер у меня не захотел работать из коробки, и беспроводные соединения оставались неактивными и в нем отображалось что-то вроде: "Не могу конфигурировать устройство". Для разрешения проблемы опираясь на это сделал примерно следующее:
- В файле '/etc/network/interfaces' закомментировал все упоминания eth и wlan. Устройство 'lo' не трогал
- В файле '/etc/NetworkManager/NetworkManager.conf' поменял строчку "managed=false" на "managed=true".
- Перегрузил менеджер соединений командой:
"/etc/init.d/network-manager restart"
- В менеджере выбрал точку WLAN и настроил соединение.
воскресенье, 27 января 2013 г.
воскресенье, 25 ноября 2012 г.
Хранение однотипных групп пользовательских настроек, созданных во время выполнения приложения [Visual Studio, .NET]
[Using of the user scope settings for the storing the similar setting groups in run-time]
Платформа .NET и Visual Studio предлагает относительно развитые возможности для простого использования настроек приложения. Это, например, разделение настроек на пользовательские (UserScope) и программные (AppScope), их статическая типизация, наличие встроенного в Visual Studio дизайнера и прочие вкусности. Неплохой обзор для VS 2005, но все еще актуальный, можно посмотреть здесь:
http://msdn.microsoft.com/en-us/library/aa730869(v=vs.80).aspx
Всего этого достаточно, чтобы покрыть большинство потребностей небольшого приложения. Но если вам когда-нибудь понадобиться выйти за границы статического объявления настроек и/или захочется хранить данные в своем формате и тем не менее оставаться в рамках стандартных механизмов .NET, вам, скорее всего, придется разбираться с классами ConfigurationManager и ConfigurationSection:
http://msdn.microsoft.com/en-us/library/system.configuration.configurationmanager(v=vs.100).aspx
Так случилось и со мной, мне понадобилось хранить группы определенных настроек, количество которых определялось во время выполнения программы. Группы нужно было идентифицировать по некоторому ключу, в моем случае им была уникальная строка. Но в своем решении я избежал сложностей испльзования ConfigurationManager, а просто “вложил” свою группу настроек в одну пользовательскую установку. Решение получилось достаточно простым, но не совсем очевидным, поэтому с удовольствием спешу поделиться этим знанием :)
Итак, дизайнер настроек позволяет определять различные предопределенные типы настроек. Последним пунктом в этом списке можно выбрать пользовательский тип из некоторого количества предоставленных имен пространств. Список этот ограничен, но можно вписать полный путь к произвольному типу вручную.
Здесь-то мы и добавим наш “контейнер” групп, но для начала нужно его объявить.
Для многократного использования общей конструкции я использовал общий (generic) класс. В моей задаче мне нужно было ко всему прочему иметь “глобальное” значение, которое бы использовалось как стандартное для всех новых и/или неопределенных значений. Ниже код:
Не каждый тип может быть использован в качестве типа установки, важное условие - он должен быть Xml-сериализуемым. Избегая излишних сложностей сериализации словарей (Dictionary) применил List и пары значение ключ.
Теперь этот generic-класс можно “конкретизировать” любым сериализуемым типом:
Конечно же, элементарными типами дело не ограничивается и можно использовать комплексные xml-сериализуемые типы, как например этот для хранения параметров окон:
Теперь все готово, можно объявить контейнеры настроек в дизайнере. Ограничусь для демонстрации двумя видами - SettingSetString и SettingSetWinPosSett:
Замечание! Что бы эти типы стали доступными для выбора, необходимо пересоздать проект.
Как вы может быть уже заметили, для таких типов в дизайнере невозможно определить стандартное значение (колонка “Value”, или “Wert“ у меня в немецкой студии). Поэтому просто, не усложняя, расширим созданный генератором класс настроек следующим методом:
Ну все, можно пользоваться:
Вот как выглядит часть файла user.config после выполненных операций:
Неплохо! Теперь попробуем использовать комплексный тип WinPosSett:
Сохраненные настройки выглядят так:
Пример приложения можно скачать здесь:
https://sites.google.com/site/selosite/GroupUserSettings.zip
Платформа .NET и Visual Studio предлагает относительно развитые возможности для простого использования настроек приложения. Это, например, разделение настроек на пользовательские (UserScope) и программные (AppScope), их статическая типизация, наличие встроенного в Visual Studio дизайнера и прочие вкусности. Неплохой обзор для VS 2005, но все еще актуальный, можно посмотреть здесь:
http://msdn.microsoft.com/en-us/library/aa730869(v=vs.80).aspx
Всего этого достаточно, чтобы покрыть большинство потребностей небольшого приложения. Но если вам когда-нибудь понадобиться выйти за границы статического объявления настроек и/или захочется хранить данные в своем формате и тем не менее оставаться в рамках стандартных механизмов .NET, вам, скорее всего, придется разбираться с классами ConfigurationManager и ConfigurationSection:
http://msdn.microsoft.com/en-us/library/system.configuration.configurationmanager(v=vs.100).aspx
Так случилось и со мной, мне понадобилось хранить группы определенных настроек, количество которых определялось во время выполнения программы. Группы нужно было идентифицировать по некоторому ключу, в моем случае им была уникальная строка. Но в своем решении я избежал сложностей испльзования ConfigurationManager, а просто “вложил” свою группу настроек в одну пользовательскую установку. Решение получилось достаточно простым, но не совсем очевидным, поэтому с удовольствием спешу поделиться этим знанием :)
Итак, дизайнер настроек позволяет определять различные предопределенные типы настроек. Последним пунктом в этом списке можно выбрать пользовательский тип из некоторого количества предоставленных имен пространств. Список этот ограничен, но можно вписать полный путь к произвольному типу вручную.
Здесь-то мы и добавим наш “контейнер” групп, но для начала нужно его объявить.
Для многократного использования общей конструкции я использовал общий (generic) класс. В моей задаче мне нужно было ко всему прочему иметь “глобальное” значение, которое бы использовалось как стандартное для всех новых и/или неопределенных значений. Ниже код:
public class SettingSetBase<T>
{
public SettingSetBase() { // ctor
KeyValList = new List<KeyVal<T>>();
DefaultValue = default(T);
}
public SettingSetBase(T defVal) { // ctor
KeyValList = new List<KeyVal<T>>();
DefaultValue = defVal;
}
public T DefaultValue { get; set; }
public List<KeyVal<T>> KeyValList { get; set; }
public T GetValue(string key) {
KeyVal<T> keyVal = KeyValList.FirstOrDefault(x => String.Equals(x.Key, key, StringComparison.CurrentCultureIgnoreCase));
if (keyVal == null) return DefaultValue;
else return keyVal.Val;
}
public void SetValue(String key, T val) {
KeyVal<T> keyVal = KeyValList.FirstOrDefault(x => String.Equals(x.Key, key, StringComparison.CurrentCultureIgnoreCase));
if (keyVal == null)
KeyValList.Add(new KeyVal<T>() { Key = key, Val = val });
else
keyVal.Val = val;
}
}
public class KeyVal<T>
{
public String Key { get; set; }
public T Val { get; set; }
}
* This source code was highlighted with Source Code Highlighter.Не каждый тип может быть использован в качестве типа установки, важное условие - он должен быть Xml-сериализуемым. Избегая излишних сложностей сериализации словарей (Dictionary) применил List
Теперь этот generic-класс можно “конкретизировать” любым сериализуемым типом:
// Например для String:
public class SettingSetString : SettingSetBase<String>
{
public SettingSetString() { }
public SettingSetString(String defVal) : base(defVal) { }
}
// Или для булевых значений:
public class SettingSetBool : SettingSetBase<bool>
{
public SettingSetBool() { }
public SettingSetBool(bool defVal) : base(defVal) { }
}
// Или для глобального уникального идентификатора (GUID):
public class SettingSetGuid : SettingSetBase<Guid>
{
public SettingSetGuid() { }
public SettingSetGuid(Guid defVal) : base(defVal) { }
}
// Или даже для его обнулямого варианта:
public class SettingSetGuidNullable : SettingSetBase<Guid?>
{
public SettingSetGuidNullable() { }
public SettingSetGuidNullable(Guid? defVal) : base(defVal) { }
}
* This source code was highlighted with Source Code Highlighter.Конечно же, элементарными типами дело не ограничивается и можно использовать комплексные xml-сериализуемые типы, как например этот для хранения параметров окон:
public class WinPosSett
{
public Point Location { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
* This source code was highlighted with Source Code Highlighter.Теперь все готово, можно объявить контейнеры настроек в дизайнере. Ограничусь для демонстрации двумя видами - SettingSetString и SettingSetWinPosSett:
Замечание! Что бы эти типы стали доступными для выбора, необходимо пересоздать проект.
Как вы может быть уже заметили, для таких типов в дизайнере невозможно определить стандартное значение (колонка “Value”, или “Wert“ у меня в немецкой студии). Поэтому просто, не усложняя, расширим созданный генератором класс настроек следующим методом:
internal sealed partial class Settings
{
public void InitDefaults() {
if (MyStringSet == null)
MyStringSet = new SettingSetString("DefaultValue");
if (MyWinPosSet == null)
MyWinPosSet = new SettingSetWinPosSett(new WinPosSett() { Height = 300, Width = 300, Location = new Point(100, 100) });
}
}
* This source code was highlighted with Source Code Highlighter.Ну все, можно пользоваться:
Properties.Settings.Default.InitDefaults();
String strGroup1 = Properties.Settings.Default.MyStringSet.GetValue("Group1"); // "DefaultValue"
Properties.Settings.Default.MyStringSet.SetValue("Group1", "My new Value");
Properties.Settings.Default.Save();
String strGroup1NewValue = Properties.Settings.Default.MyStringSet.GetValue("Group1"); // "My new Value"
String strGroup2 = Properties.Settings.Default.MyStringSet.GetValue("Group2"); // "DefaultValue"
* This source code was highlighted with Source Code Highlighter.Вот как выглядит часть файла user.config после выполненных операций:
<setting name="MyStringSet" serializeAs="Xml">
<value>
<SettingSetString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<DefaultValue>DefaultValue</DefaultValue>
<KeyValList>
<KeyValOfString>
<Key>Group1</Key>
<Val>My new Value</Val>
</KeyValOfString>
</KeyValList>
</SettingSetString>
</value>
</setting>
* This source code was highlighted with Source Code Highlighter.Неплохо! Теперь попробуем использовать комплексный тип WinPosSett:
WinPosSett WinPosMainWindow = Properties.Settings.Default.MyWinPosSet.GetValue("MainForm");
// .. используем значения для установки размеров и позиции окна
// ..
// .. окно закрывается, сохраняем значения:
Properties.Settings.Default.MyWinPosSet.SetValue("MainForm", new WinPosSett() { Height = 400, Width = 200, Location = new Point(10, 10) });
Properties.Settings.Default.Save();
* This source code was highlighted with Source Code Highlighter.Сохраненные настройки выглядят так:
<setting name="MyWinPosSet" serializeAs="Xml">
<value>
<SettingSetWinPosSett xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<DefaultValue>
<Location>
<X>100</X>
<Y>100</Y>
</Location>
<Width>300</Width>
<Height>300</Height>
</DefaultValue>
<KeyValList>
<KeyValOfWinPosSett>
<Key>MainForm</Key>
<Val>
<Location>
<X>10</X>
<Y>10</Y>
</Location>
<Width>200</Width>
<Height>400</Height>
</Val>
</KeyValOfWinPosSett>
</KeyValList>
</SettingSetWinPosSett>
</value>
</setting>
* This source code was highlighted with Source Code Highlighter.Пример приложения можно скачать здесь:
https://sites.google.com/site/selosite/GroupUserSettings.zip
воскресенье, 30 сентября 2012 г.
Добавление авто-подписи к каждому сообщению на blogspot
В одном своем блоге, который я использую как платформу для опубликования снимков различных объектов (своеобразный клипарт) мне захотелось добавить авто-подпись для всех опубликованных постов. В этой подписи я хотел еще раз упомянуть о том, что фотографии свободны и бесплатны для использования.
В blogspot можно создать шаблон, который используется при создании новых сообщений. Но если вы когда-нибудь захотите изменить авто-подпись, придется править ВСЕ сообщения вручную. Других стандартных способов для решения этой проблемы я не нашел и .. изобрел свой :)
Для этого было решено воспользоваться JavaScript для динамического изменения DOM (Document Object Model) страницы. JavaScript можно добавить на блог как гаджет в его дизайнере:
Подсмотрев код HTML блога установил, что все посты блога заключены в элемент DIV с классом "post-body entry-content". Этого абсолютно достаточно знать, что бы как я, не зная JavaScript (лишь смутные представления) при помощи поисковика накодить следующий скрипт:
Название в окне настроек гаджета я оставил пустым - теперь о наличии скрипта можно только догадываться.
Результат вы можете посмотреть на моем клипарт-блоге
Ссылки по теме:
Document Object Model (http://en.wikipedia.org/wiki/Document_Object_Model)
document.getElementsByClassName (https://developer.mozilla.org/en-US/docs/DOM/document.getElementsByClassName)
Adding elements to the DOM (http://www.javascriptkit.com/javatutors/dom2.shtml)
В blogspot можно создать шаблон, который используется при создании новых сообщений. Но если вы когда-нибудь захотите изменить авто-подпись, придется править ВСЕ сообщения вручную. Других стандартных способов для решения этой проблемы я не нашел и .. изобрел свой :)
Для этого было решено воспользоваться JavaScript для динамического изменения DOM (Document Object Model) страницы. JavaScript можно добавить на блог как гаджет в его дизайнере:
Подсмотрев код HTML блога установил, что все посты блога заключены в элемент DIV с классом "post-body entry-content". Этого абсолютно достаточно знать, что бы как я, не зная JavaScript (лишь смутные представления) при помощи поисковика накодить следующий скрипт:
<script type='text/javascript'>
// Тест, что DIV элементы скриптом находятся:
// var posts = document.getElementsByClassName("post-body entry-content");
// document.write("Posts: " + posts.length);
var posts = document.getElementsByClassName("post-body entry-content");
for (var i=0; i<posts.length; i++) {
newDiv = document.createElement("div");
// a) Содержимое DIV можно наполнить так:
// newLine1 = document.createTextNode("Этот текстовый элемент создан при помощи createTextNode()");
// newDiv.appendChild(newLine1);
// b) но мы воспользуемся другим способом: newDiv.innerHTML = '<li>text</li>';
str = "<span style='font-size: smaller;'>Это первая строка HTML-кода.</span><BR/><span style='font-size: smaller;'>Вторая строка.</span>";
newDiv.innerHTML = str;
// Добавим созданный DIV после DIV-сообщения.
// К сожалению, не существует insertAfter-функции, поэтому эмулируем ее с помощью "parentElement" и "nextSibling":
posts[i].parentElement.insertBefore(newDiv, posts[i].nextSibling);
}
</script>
* This source code was highlighted with Source Code Highlighter.
Название в окне настроек гаджета я оставил пустым - теперь о наличии скрипта можно только догадываться.
Результат вы можете посмотреть на моем клипарт-блоге
Ссылки по теме:
Document Object Model (http://en.wikipedia.org/wiki/Document_Object_Model)
document.getElementsByClassName (https://developer.mozilla.org/en-US/docs/DOM/document.getElementsByClassName)
Adding elements to the DOM (http://www.javascriptkit.com/javatutors/dom2.shtml)
понедельник, 24 сентября 2012 г.
Автоматическое изменение размеров FlowLayoutPanel в XtraLayoutControl (DevExpress)
[FlowLayoutPanel with autosize in XtraLayoutControl]
Работая с библиотекой элементов управления для WinForms от DevExpress в ходе практики накопилась небольшая тележка различных решений, обходных путей и просто интересных подходов. Со временем планирую их здесь опубликовать. Ну а пока, покажу, как я реализовал автоматическое изменение размеров FlowLayoutPanel в XtraLayoutControl, так чтобы все элементы, помещенные в FlowLayoutPanel, были всегда видны.
На рисунках отображен уже конечный результат, которого я хотел добиться:
XtraLayoutControl отлично управляет размерами и расположениями “своих” элементов управления из библиотеки DevExpress, но совершено не справляется со стандартными элементами управления, к которым относится FlowLayoutPanel. Дело в том, что XtraLayoutControl взаимодействует с элементами своеобразным образом и требует от элементов реализации специфичных интерфейсов. В нашем случае достаточно использовать интерфейс IXtraResizableControl, который я реализовал в унаследованном от панели классе:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using DevExpress.XtraLayout;
using System.Drawing;
using DevExpress.Utils.Controls;
namespace WindowsFormsApplication9
{
public class MyFlowLayoutPanel : FlowLayoutPanel, IXtraResizableControl
{
public MyFlowLayoutPanel() { // ctor
}
LayoutControl rootLayoutControl = null;
LayoutControlItem parentControlItem = null;
protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
if (System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime)
return;
LayoutControl parent = this.Parent as LayoutControl;
if (parent != null) {
this.rootLayoutControl = parent;
this.parentControlItem = FindItemByContentControl(parent.Root, this);
CalcAndUpdateSize();
this.rootLayoutControl.Resize += (x, y) => { CalcAndUpdateSize(); };
this.ControlAdded += (x, y) => { CalcAndUpdateSize(); };
this.ControlRemoved += (x, y) => { CalcAndUpdateSize(); };
}
}
void layoutControl_Changing(object sender, EventArgs e) {
CalcAndUpdateSize();
}
// REM. http://www.devexpress.com/Support/Center/p/Q396747.aspx (Hot to find a conatner LayoutcontrolItem of a control)
LayoutControlItem FindItemByContentControl(LayoutControlGroup layoutGroup, Control contentControl) {
foreach (BaseLayoutItem baseLI in layoutGroup.Items) {
LayoutControlItem lci = baseLI as LayoutControlItem;
if (lci != null && lci.Control == contentControl)
return lci;
LayoutControlGroup lcg = baseLI as LayoutControlGroup;
if (lcg != null)
return FindItemByContentControl(lcg, contentControl);
}
return null;
}
private void CalcAndUpdateSize() {
if (parentControlItem == null || parentControlItem.ViewInfo == null) return;
this.MaximumSize = new Size(parentControlItem.ViewInfo.ClientArea.Width, 0);
RaiseSizeChanged();
}
#region IXtraResizableControl Member
event EventHandler ChangedCore;
event EventHandler IXtraResizableControl.Changed {
add { ChangedCore += value; }
remove { ChangedCore -= value; }
}
protected void RaiseSizeChanged() {
if (ChangedCore != null) ChangedCore(this, EventArgs.Empty);
}
bool IXtraResizableControl.IsCaptionVisible { get { return false; } }
Size IXtraResizableControl.MaxSize { get { return new Size(0, this.PreferredSize.Height); } }
Size IXtraResizableControl.MinSize { get { return new Size(0, this.PreferredSize.Height); } }
#endregion
}
}
Ссылки по теме:
IXtraResizableControl Interface:
http://documentation.devexpress.com/#WindowsForms/clsDevExpressUtilsControlsIXtraResizableControltopic
FlowLayoutPanel with autosize and layout control:
http://www.devexpress.com/Support/Center/p/Q292593.aspx
Вот собственно и все. Исходный код можете скачать здесь
среда, 15 августа 2012 г.
Алфавитное упорядочивание файлов, созданных цифровой камерой Canon
[Alphabetical ordering of files created by digital cameras Canon]
Моя зеркальная камера Canon EOS 600D присваивает, в некоторых случаях, именам файлов префикс “_MG_” вместо ”IMG_”. Данная “проблема” известна также владельцам многих других моделей зеркальных камер от Canon. Это происхоит в том случае, если в установках для Цветового пространства выбрано значение “Adobe RGB” (у меня доступно в творческих режимах). Согласно документации, “Adobe RGB” используется для промышленной обработки фотографии и часто в при распечатке на несовместимых с этим форматом принтерах цвета выглядят несколько приглушенными. При съемке в сюжетных режимах автоматически используется значение “sRGB”.
Так или иначе, если вы пользуетесь обоими режимами (или пользовались ранее и теперь не будете :) ) на выходе получается коллекция фотографий, которую тяжело обрабатывать. Для упорядочивания файлов в порядке их съемки, их нужно соответственно переименовать. Для пользователей линукс есть отличное решение - это программа rename, которая является частью многих дистрибутивов. Чтобы, в конце концов, отличать используемые установки по имени файла, я добавил эту информацию в его конец, выразив ее пост-фиксом “_AdobeRGB”:
Файлы вида “_MG_1234.JPG” переименовываются в “IMG_1234_AdobeRGB.JPG”.
Для предварительного просмотра результатов операции без фактического переименования, можно использовать параметр “-n”. В общем, смотрите документацию к команде, я сам лично обожаю подробные описания wiki на ubuntuusers.de [http://wiki.ubuntuusers.de/rename] на немецком языке.
Моя зеркальная камера Canon EOS 600D присваивает, в некоторых случаях, именам файлов префикс “_MG_” вместо ”IMG_”. Данная “проблема” известна также владельцам многих других моделей зеркальных камер от Canon. Это происхоит в том случае, если в установках для Цветового пространства выбрано значение “Adobe RGB” (у меня доступно в творческих режимах). Согласно документации, “Adobe RGB” используется для промышленной обработки фотографии и часто в при распечатке на несовместимых с этим форматом принтерах цвета выглядят несколько приглушенными. При съемке в сюжетных режимах автоматически используется значение “sRGB”.
Так или иначе, если вы пользуетесь обоими режимами (или пользовались ранее и теперь не будете :) ) на выходе получается коллекция фотографий, которую тяжело обрабатывать. Для упорядочивания файлов в порядке их съемки, их нужно соответственно переименовать. Для пользователей линукс есть отличное решение - это программа rename, которая является частью многих дистрибутивов. Чтобы, в конце концов, отличать используемые установки по имени файла, я добавил эту информацию в его конец, выразив ее пост-фиксом “_AdobeRGB”:
rename -v 's/^(_)(MG_.*)\.JPG/I$2_AdobeRGB\.JPG/g' *
Файлы вида “_MG_1234.JPG” переименовываются в “IMG_1234_AdobeRGB.JPG”.
Для предварительного просмотра результатов операции без фактического переименования, можно использовать параметр “-n”. В общем, смотрите документацию к команде, я сам лично обожаю подробные описания wiki на ubuntuusers.de [http://wiki.ubuntuusers.de/rename] на немецком языке.
понедельник, 23 июля 2012 г.
UnityContainer. Передача параметра конструктора в метод Resolve()
[UnityContainer. Pass constructor parameters to Resolve() method]
В одном своем проекте использую UnityContainer - расширяемый DI-контейнер (dependency injection container) от Misrosoft. И понадобилось мне передавать в конструкторы создаваемых контейнером сервисов объекты (DataContext), временем жизни которых я управлял сам. То есть мне понадобилось примерно следующее:
Оказалось, что для этой довольно типичной задачи UnityContainer не предлагает однозначного решения. Это обстоятельство порождает множество подходов, которые можно найти в интернете. Одним из таких решений является разрешение (resolve) объекта с переопределением его параметра:
Но здесь нужно быть крайне осторожным, т.к. при изменении имени параметра, контейнер без какого-либо предупреждения инициализирует сервис им вновь созданным объектом DataContext, а наш объект будет попросту проигнорирован.
Многие другие найденные в интернете решения немного модернизированные базировались на указанном подходе. При этом имена параметров извлекались при помощи рефлексии, а реализация заворачивалась в метод расширение (extension).
Другой, приемлемый, на мой взгляд, подход - вынос инициализации сервисов контекстом в отдельный метод:
Я же задействовал потомка контейнера (ChildContainer), что, как мне показалось, является наиболее простым и естественным способом для решения поставленной задачи. Вот что у меня получилось:
Идея проста: В потомке контейнера регистрируем необходимую инстанцию DataContext, создаем с его помощью объект сервиса (Resolve) и в конце заметаем за собой следы (Dispose). Обратите внимание на указание внешнего контроллера времени жизни объекта dx1 (ExternallyControlledLifetimeManager) - тем самым мы исключаем DataContext из утилизации при вызове метода childContainer.Dispose().
Этот код лег в основу метода-расширения, которое я использовал в своем проекте:
Теперь вызовы выглядят так:
PS: К концу написания статьи натолкнулся на еще один способ реализации необходимого функционала - использование переопределения зависимости DependencyOverride:
Здесь объект DataContext встраивается в граф объектов контейнера и используется везде, где есть зависимость этого типа.
В одном своем проекте использую UnityContainer - расширяемый DI-контейнер (dependency injection container) от Misrosoft. И понадобилось мне передавать в конструкторы создаваемых контейнером сервисов объекты (DataContext), временем жизни которых я управлял сам. То есть мне понадобилось примерно следующее:
// работаем с одним контекстом данных
DataContext dx1 = container.Resolve();
container.Resolve(/*dx1*/); // конструктор сервиса: public Service1(DataContext dx)
container.Resolve(/*dx1*/); // конструктор сервиса: public Service2(DataContext dx)
// работаем с другим контекстом
DataContext dx2 = container.Resolve();
container.Resolve(/*dx2*/);
Оказалось, что для этой довольно типичной задачи UnityContainer не предлагает однозначного решения. Это обстоятельство порождает множество подходов, которые можно найти в интернете. Одним из таких решений является разрешение (resolve) объекта с переопределением его параметра:
DataContext dx1 = container.Resolve();
Service1 srv1 = container.Resolve(new ParameterOverride("dx", dx1));
Но здесь нужно быть крайне осторожным, т.к. при изменении имени параметра, контейнер без какого-либо предупреждения инициализирует сервис им вновь созданным объектом DataContext, а наш объект будет попросту проигнорирован.
Многие другие найденные в интернете решения немного модернизированные базировались на указанном подходе. При этом имена параметров извлекались при помощи рефлексии, а реализация заворачивалась в метод расширение (extension).
Другой, приемлемый, на мой взгляд, подход - вынос инициализации сервисов контекстом в отдельный метод:
DataContext dx1 = container.Resolve();
Service1 srv1 = container.Resolve();
Service2 srv2 = container.Resolve();
srv1.Init(dx1);
srv2.Init(dx1);
Я же задействовал потомка контейнера (ChildContainer), что, как мне показалось, является наиболее простым и естественным способом для решения поставленной задачи. Вот что у меня получилось:
DataContext dx1 = container.Resolve();
IUnityContainer childContainer = container.CreateChildContainer();
childContainer.RegisterInstance(dx1, new ExternallyControlledLifetimeManager()); // !!!
Service1 srv1 = childContainer.Resolve();
Service2 srv2 = childContainer.Resolve();
childContainer.Dispose();
Идея проста: В потомке контейнера регистрируем необходимую инстанцию DataContext, создаем с его помощью объект сервиса (Resolve) и в конце заметаем за собой следы (Dispose). Обратите внимание на указание внешнего контроллера времени жизни объекта dx1 (ExternallyControlledLifetimeManager) - тем самым мы исключаем DataContext из утилизации при вызове метода childContainer.Dispose().
Этот код лег в основу метода-расширения, которое я использовал в своем проекте:
public static class UnityExtensions
{
public static T xResolve(this IUnityContainer container, DataContext dx) {
using (IUnityContainer childContainer = container.CreateChildContainer())
{
childContainer.RegisterInstance(dx, new ExternallyControlledLifetimeManager());
return childContainer.Resolve();
}
}
}
Теперь вызовы выглядят так:
DataContext dx1 = container.Resolve(); // Первая инстанция
Service1 srv1 = container.xResolve(dx1);
Service2 srv2 = container.xResolve(dx1);
DataContext dx3 = container.Resolve(); // Вторая инстанция
Service1 srv3 = container.xResolve(dx3);
PS: К концу написания статьи натолкнулся на еще один способ реализации необходимого функционала - использование переопределения зависимости DependencyOverride:
container.Resolve(new DependencyOverride(dx1));
Здесь объект DataContext встраивается в граф объектов контейнера и используется везде, где есть зависимость этого типа.
понедельник, 2 июля 2012 г.
Исключение файлов из списка копирования xcopy в build событиях Visual Studio (2010)
[Exclude files using xcopy in the build events of Visual Studio (2010)]
Для копирования файлов папки по pre-/post build событиям студии можно использовать команду командной строки - xcopy. Команда имеет множество параметров для самых различных сценариев, полный список которых можно посмотреть, набрав в интерпретаторе cmd “xcopy /?” или, например, здесь (http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/xcopy.mspx?mfr=true). Я, в процессе использования этой команды не имея большого опыта работы с командной строкой и ее инструкциями, столкнулся с рядом не совсем очевидных для меня проблем. Ниже их краткое описание и их решения.
Задача 1: Копирование обновленных файлов одной директории в другую после построения проекта.
Моя команда копирования в “командной строке события после построения” (согласен, звучит сложно, но из песни слов не выкинешь - это официальное название командной строки post-build событий в VisualStudio) с использованием макросов выглядела так:
Здесь все выглядит довольно очевидно, но все же краткое описание используемых параметров:
/d - для копирования измененных файлов,
/e - копирование директорий и поддиректорий, даже если они пусты,
/i - будет создана папка назначения, если она не существует.
Замечание: Команда xcopy “чувствительна” к символу “\” в конце имени источника. Не стоит его подставлять, если источником копирования является папка. Команда завершится ошибкой:
Но для копирования всех файлов и под-папок директории можно использовать маску “*.*”. Следующая команда сработает как надо и эквивалентна первому рабочему варианту:
Задача 2. Исключение определенных файлов из списка копирования
Согласно справке для исключения файлов можно использовать параметр “/exclude” и его синтаксис в справке выглядит довольно просто:
Но, к сожалению, использование этой инструкции не так уж тривиально и вызвало у меня ряд затруднений. Например здесь filename1..N не имена исключаемых файлов, а имена файлов, которые содержат описания исключаемых файлов (по одному на каждую строку). Т.е. чтобы исключить файл “a.txt” нужно создать текстовый файл “excludeList.txt” в папке проекта и внести в него строку “a.txt”. Команда копирования файлов приобретает вид:
Но эта команда в ряде случаев не может быть выполнена и возвращает ошибку. Эта проблема часто связана с пустыми символами в пути к файлу, которые “разрывают” команду так, что она не может быть правильно интерпретирована. Поэтому мы и обрамили пути источника и цели в кавычки (“), но параметр /exclude не допускает их использования. Чтобы обойти эту проблему можно использовать относительный путь без кавычек, предварительно перейдя в нужную папку при помощи команды cd:
Теперь команда будет выполнятся, но исключаемый файл “a.txt” все равно окажется в целевой папке. Немножко помучившись выяснил (= случайно обнаружил), что первая строка файла excludeList.txt игнорируется интерпретатором и для успешного выполнения команды необходимо сместить описания на одну строчку ниже. Файл “excludeList.txt”:
На этом можно было бы поставить точку, но я не был удовлетворен тем, что описание исключаемых файлов скрыто от глаз в отельном файле. Мне захотелось все иметь в одном месте. Для этого файл с описанием исключений можно создавать прямо в скрипте (здесь исключаются файлы “a.txt” и “b.txt”):
Обратите внимание, что обошлось без создания пустой первой строки, скрипт срабатывает на как нужно. Почему? Осталось для меня загадкой, принял как должное.
Замечание. В списке исключаемых файлов не допускается использование символов маски (такие как “*” или “?”), но сами значения, если выражаться “языком шаблонов поиска файлов” как бы обрамляются звездочкой. Например для записи “a.txt” будет происходить исключение файлов соответствующих маске “*a.txt*”.
Для копирования файлов папки по pre-/post build событиям студии можно использовать команду командной строки - xcopy. Команда имеет множество параметров для самых различных сценариев, полный список которых можно посмотреть, набрав в интерпретаторе cmd “xcopy /?” или, например, здесь (http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/xcopy.mspx?mfr=true). Я, в процессе использования этой команды не имея большого опыта работы с командной строкой и ее инструкциями, столкнулся с рядом не совсем очевидных для меня проблем. Ниже их краткое описание и их решения.
Задача 1: Копирование обновленных файлов одной директории в другую после построения проекта.
Моя команда копирования в “командной строке события после построения” (согласен, звучит сложно, но из песни слов не выкинешь - это официальное название командной строки post-build событий в VisualStudio) с использованием макросов выглядела так:
xcopy "$(ProjectDir)SourceFiles" "$(ProjectDir)TargetFiles" /d /e /i
Здесь все выглядит довольно очевидно, но все же краткое описание используемых параметров:
/d - для копирования измененных файлов,
/e - копирование директорий и поддиректорий, даже если они пусты,
/i - будет создана папка назначения, если она не существует.
Замечание: Команда xcopy “чувствительна” к символу “\” в конце имени источника. Не стоит его подставлять, если источником копирования является папка. Команда завершится ошибкой:
xcopy "$(ProjectDir)SourceFiles\" "$(ProjectDir)TargetFiles" /d /e /i REM (НЕВЕРНО)
Но для копирования всех файлов и под-папок директории можно использовать маску “*.*”. Следующая команда сработает как надо и эквивалентна первому рабочему варианту:
xcopy "$(ProjectDir)SourceFiles\*.*" "$(ProjectDir)TargetFiles" /d /e /i
Задача 2. Исключение определенных файлов из списка копирования
Согласно справке для исключения файлов можно использовать параметр “/exclude” и его синтаксис в справке выглядит довольно просто:
/exclude:filename1[+[filename2]][+[filename3]] : Specifies a list of files containing strings.
Но, к сожалению, использование этой инструкции не так уж тривиально и вызвало у меня ряд затруднений. Например здесь filename1..N не имена исключаемых файлов, а имена файлов, которые содержат описания исключаемых файлов (по одному на каждую строку). Т.е. чтобы исключить файл “a.txt” нужно создать текстовый файл “excludeList.txt” в папке проекта и внести в него строку “a.txt”. Команда копирования файлов приобретает вид:
xcopy "$(ProjectDir)SourceFiles\*.*" "$(ProjectDir)TargetFiles" /d /e /i /exclude:$(ProjectDir)excludeList.txt
Но эта команда в ряде случаев не может быть выполнена и возвращает ошибку. Эта проблема часто связана с пустыми символами в пути к файлу, которые “разрывают” команду так, что она не может быть правильно интерпретирована. Поэтому мы и обрамили пути источника и цели в кавычки (“), но параметр /exclude не допускает их использования. Чтобы обойти эту проблему можно использовать относительный путь без кавычек, предварительно перейдя в нужную папку при помощи команды cd:
cd "$(ProjectDir)"
"$(ProjectDir)SourceFiles\*.*" "$(ProjectDir)TargetFiles" /d /e /i /exclude:excludeList.txt
Теперь команда будет выполнятся, но исключаемый файл “a.txt” все равно окажется в целевой папке. Немножко помучившись выяснил (= случайно обнаружил), что первая строка файла excludeList.txt игнорируется интерпретатором и для успешного выполнения команды необходимо сместить описания на одну строчку ниже. Файл “excludeList.txt”:
[CR][LF]
a.txt”
На этом можно было бы поставить точку, но я не был удовлетворен тем, что описание исключаемых файлов скрыто от глаз в отельном файле. Мне захотелось все иметь в одном месте. Для этого файл с описанием исключений можно создавать прямо в скрипте (здесь исключаются файлы “a.txt” и “b.txt”):
cd "$(ProjectDir)"
echo a.txt>EXCLUDE_LIST
echo b.txt>>EXCLUDE_LIST
REM или одной строкой: (echo a.txt&& echo b.txt)>EXCLUDE_LIST
xcopy "$(ProjectDir)SourceFiles\*.*" "$(ProjectDir)TargetFiles" /d /e /i /exclude:EXCLUDE_LIST
Обратите внимание, что обошлось без создания пустой первой строки, скрипт срабатывает на как нужно. Почему? Осталось для меня загадкой, принял как должное.
Замечание. В списке исключаемых файлов не допускается использование символов маски (такие как “*” или “?”), но сами значения, если выражаться “языком шаблонов поиска файлов” как бы обрамляются звездочкой. Например для записи “a.txt” будет происходить исключение файлов соответствующих маске “*a.txt*”.
Подписаться на:
Сообщения (Atom)






