пятница, 21 января 2011 г.

SharePoint 2010: How to work with Taxonomy Fields using Client Object Model

Продолжу тему работы с Managed Metadata полями в SharePoint 2010, затронутую в прошлом посте.

Если посмотреть внутрь TaxonomyField, то оказывается, что это почти обычный LookUpField, который ссылается на скрытый лист TaxonomyHiddenList, живущий в рутовом сайте по адресу http://server/Lists/TaxonomyHiddenList. Этот лист содержит все Term'ы, которые использовались в данной коллекции. Хочу особо подчеркнуть, что именно использовались, т.к. если Term был добавлен, но ни разу не использовался в этом листе его не будет. Создается, он автоматически, при вызове метода SetFieldValue()()

К сожалению, Client Object Model ничего не знает о типах TaxonomyField и TaxonomyFieldValue и всем прочем, что находится в сборке "Microsoft.SharePoint.Taxonomy.dll". И как следствие полноценная работа с Taxonomy через Client OM невозможна.

Но не все так уж плохо. TaxonomyFieldValue имеет строковое представление, очень похожее на строковое представление LookUp полей:

5;#SuperNewTerm|f8b19ce9-ea14-4831-8284-e230026e6476

и следовательно его можно установить через Client OM, используя вот такой код:

var webUrl =  "http://server";
using (var clientContext = new ClientContext(webUrl))
{
    var itemCreation = new ListItemCreationInformation();
    itemCreation.UnderlyingObjectType = FileSystemObjectType.File;
    var lst = clientContext.Web.Lists.GetByTitle("TaxList");
    var item = lst.AddItem(itemCreation);
    item["Title"] = "Csom created";
    item["MyTerm"] = "5;#SuperNewTerm|f8b19ce9-ea14-4831-8284-e230026e6476";
    item.Update();
    clientContext.ExecuteQuery();
}
 

Сложность состоит в том, что при начальном формировании TermSet'ов TaxonomyHiddenList пуст. Но покопавшись в коде TaxonomyField.SetFieldValue() я нашел способ заполнения этого листа без непосредственного создания ссылающихся item'ов. К сожалению делать это приходится используя Server OM.

var url = "http://server/Lists/TaxList";
using (var site = new SPSite(url))
{
    using (var web = site.OpenWeb())
    {
        var lst = web.GetList(url);
        var taxfld = lst.Fields["MyTerm"] as TaxonomyField;
        var tSes = new TaxonomySession(site);
        var tStore = tSes.TermStores[taxfld.SspId];
        var tSet = tStore.GetTermSet(taxfld.TermSetId);
        foreach (var term in tSet.Terms)
        {
            int effectiveLcid = tStore.DefaultLanguage;
            string text = term.GetDefaultLabel(effectiveLcid) + TaxonomyField.TaxonomyGuidLabelDelimiter + term.Id;
            var tv = new TaxonomyFieldValue(taxfld);
            tv.PopulateFromLabelGuidPair(text);
        }
    }
}
 

После того как все Term'ы пред созданы с ними можно работать через Client OM.

PS: Только не забывайте что Term'ы могут содержать внутри себя другие Term'ы, и в моем коде, для простоты, это не учтено. :)

среда, 19 января 2011 г.

SharePoint 2010: How to set value to taxonomy list field

В SharePoint 2010 появился интересный сервис - Managed metadata, который позволяет создавать целые иерархии метаданных, централизовано ими управлять и использовать на всей ферме.

Мне понадобилось устанавливать значения для таких полей через код.

Для работы необходим reference на 'Microsoft.SharePoint.Taxonomy.dll' и namespace Microsoft.SharePoint.Taxonomy

Пример того как из существующей в листе колонки "MyTerm" получить определение "termName" и установить его в качестве значения колонки

var url = "http://server/Lists/TaxList";
using (var site = new SPSite(url))
{
    using (var web = site.OpenWeb())
    {
        var lst = web.GetList(url);
        var item =  lst.Items.Add();
        item[SPBuiltInFieldId.Title] = "super title";
        var taxfld = lst.Fields["MyTerm"] as TaxonomyField;
        var tSes    = new TaxonomySession(site);
        var tStore  = tSes.TermStores[taxfld.SspId];
        var tSet    = tStore.GetTermSet(taxfld.TermSetId);
        var tCol     = tSet.GetTerms("termName", true);
        if (tCol.Count > 0)
        {
            var term = tCol[0];
            var tv = new TaxonomyFieldValue(taxfld);
            taxfld.SetFieldValue(item, term);
        }
        item.Update();
    }
}
 

понедельник, 10 января 2011 г.

Мнение классиков по поводу переписывания кода

Недавно прочел книжку Джоэла Спольски "Джоэл о программировании" и сейчас читаю "Психбольница в руках пациентов" Алана Купера. Зацепился за то, что авторы крайне противоположно относятся к идеи "полного переписывания" кода.

Алан:
"При каждом изменении программы - будь то исправление ошибок или добавление функций - появляются новые рубцы. Именно поэтому программы следует выбрасывать и полностью переписывать каждые пару десятков лет. Рубцовая ткань с течением времени становится настолько толстой, что препятствует нормальной работе."

Джоэл посвятил этому целую главу "То, чего делать нельзя часть первая" и в частности пишет следующее:
"Выкидывая код и начиная все с начала, вы выбрасываете и все знания. Все эти накопленные исправления ошибок. Годы программистского труда"

При всем при этом оба автора склоняются к безусловной необходимости проектирования и прототипирования и недопустимости применения прототипов как основы готовых проектов.

Лично мне не доводилось видеть проектов, которые целиком переписывались с нуля, но, как и любой разработчик, я сам много раз мечтал об этом. При этом проекты живущие по многу лет и преживающие множество разработчиков обрастают таким слоем мха старых ф-ии и непонятных решении, что зачастую эти части просто страшно трогать.

Теоретически эту проблему должно решать наличие автоматических тестов, позволяющих при любых изменениях иметь возможность проверить всю функциональность. На практике же мне не приходилось работать в проектах где покрытие тестов с одной стороны не приводило бы даже мелкие изменения к диким трудностям с "озеленением" тестов, а с другой обеспечивало проверку всех особенностей приложения добавленных за многие годы.

А что думают о переписывании проектов мои уважаемые читатели?

суббота, 8 января 2011 г.

Mercurial and WinMerge as diff tool

Под Windows пользоваться hg diff не очень то удобно и наглядно. Для удобного diff и merge есть более подходящие средства, например WinMerge (открытый и бесплатный).

Чтобы он начал работать под hg в файл ".hgrc", из домашнего каталога, добавляем вот такие строчки:

[extensions]
hgext.extdiff =

[extdiff]
cmd.wdiff=winmergeu

[merge-tools]
winmergeu.args=/e /ub /dl other /dr local $other $local $output
winmergeu.regkey=Software\Thingamahoochie\WinMerge
winmergeu.regname=Executable
winmergeu.fixeol=True
winmergeu.checkchanged=True
winmergeu.gui=True

После чего можно смело писать hg wdiff myfile.txt

И видеть вот такой результат: