Ласкаво просимо до dev.net.ua Увійти | Приєднатися | Допомога

Ivan Bilokon

This blog is about my findings
The type or namespace name 'Name' could not be found (are you missing a using directive or an assembly reference?)

If you migrate custom workflow activities from SharePoint 2010 to SharePoint 2013 you may receive error in ULS while attempting to run the workflow: Microsoft.SharePoint.SPException: <Error><CompilerError ... Text="The type or namespace name '<Name>' could not be found (are you missing a using directive or an assembly reference?)" /></Error>. Also similar error is displayed in SharePoint Designer in place of the activity.
I found the solution in this great article . To resolve the issue you should move the <authorizedType> node to

<authorizedTypes>

    <targetFx version="v4.0">

in the web.config of the SP 2013 web app.

Happy upgrading

userPrincipal.GetAuthorizationGroups(). An error occurred while enumerating the groups. The group could not be found.

Hello!

Today I struggled a bit with weird issue trying to enumerate Active Directory groups for some user. The code I used is below

using (var principalContext = new PrincipalContext(ContextType.Domain))

 {
using (var userPrincipal = UserPrincipal.FindByIdentity(principalContext, System.DirectoryServices.AccountManagement.IdentityType.SamAccountName, userLogin))
{
if (userPrincipal != null)
{
using (var groups = userPrincipal.GetAuthorizationGroups())
{
result = groups.Select(g => g.Sid.ToString()).ToArray();
}
}
}

For the user this code throws NoMatchingPrincipalException exception  in the line



result = groups.Select(g => g.Sid.ToString()).ToArray();

The cause of the error was some group that was located in container where my account didn't have read access. Weird that the group was actually listed in the collection but attempt to refer it failed with exception.

Below is my workaround. Instead of groups.Select(g => g.Sid.ToString()).ToArray(); or foreach... I wrote

var sids = new List<string>();                  

 var enumerator = groups.GetEnumerator();
///foreach does not work in cases when group is located in container where the app pool account does not have read permissions
///In this case enumerator.Current throws NoMatchingPrincipalException
while (enumerator.MoveNext())
{
try
{
var g = enumerator.Current;
sids.Add(g.Sid.ToString());
}
catch (NoMatchingPrincipalException)
{
}
}

Hope that will help somebody.

The workflow could not update the item, possibly because one or more columns for the item require a different type of information.
Recently I had an issue with SharePoint Designer 2010 workflow. While it was operational on some list item, running on another item caused an workflow cancelling and an error in the history list saying that "The workflow could not update the item, possibly because one or more columns for the item require a different type of information". The error occured even if the first activity was Log to history list "Hello world" message. Searching the internet resulted no relevant solution. With trial and error I determined that Stop workflow activity can be used without an error if it is placed before the problematic activity. Using that approach I found out that the problem was in Set workflow variable activity. I was trying to set string variable to calculated field value of an item from another list. According to item retrieval conditions no item returned for specified values and that caused such a wierd error message.
Windows Azure VM disappeared after trial limit reached
Recently I set up SharePoint farm in my customer's Windows Azure trial subscription. In a few days he received e-mail saying that the subscription reached allowed limit. So he decided to move to pay-as-you-go subscription.  But (as you may know) all the Virtual machines and virtual networks were deleted. Instead empty Cloud Services named exactly as you VMs appeared. The problem and possible solutions described in many posts like:
http://blogs.msdn.com/b/narahari/archive/2012/10/18/windows-azure-virtual-machine-disappeared-or-gone-how-do-i-recover.aspx
http://social.msdn.microsoft.com/Forums/en-US/WAVirtualMachinesforWindows/thread/94b52c79-5487-453d-81b7-016baec62d28/

Generally, folks suggest 2 possible recovery approaches:
  1. Create new VMs using existing disks as templates.
  2. If (1) don't work (as was for me because of "A lease conflict occurred with the blob" error), download CloudXplorer and copy VHDs into different place.

I found alternative solution that worked for me.

  1. First you need to delete all the Cloud Services that are named after your VMs. Otherwise you will not be able to create VMs with the same DNS names.
  2. Go to Virtual Machines and select Disks. You will see all you disks that are not attached to any VM. You should delete them, but be careful: DON'T delete associated VHD (you will be prompted for that option).
  3. Now you will be able to create Images from VHDs. Go to Images and create new images from the VHDs.
  4. Recreate your virtual networks with the same settings (hope you remember them).
  5. Recreate your VMs using Images you already created and their original DNS names. If you set up DNS server IP in you virtual networks you need to create the DNS server VM first.

Wait a bit while your VM is creating. Because you didn't run sysprep before image creation, the VM will stuck in "Running (Provisioning)". Don't worry - the VM is ready to use and seems to be operational anyway.

Error starting Lotus Notes Connector Service
Hi!
Recently I had to set up SharePoint Lotus Notes Connector to crawl content from Lotus Notes database (good howto is here ). I've completed all the steps but received an error while attempting to start Lotus Notes Connector Service. The message said that the service cannot be started and advised to walk through several steps I had already been done. Trying to troubleshoot the problem I found following error in Event Viewer:

"The COM+ System Application service depends on the System Event Notification Service which failed to start because of the following error:
The service cannot be started either because it is disabled or because it has no enabled devices associated with it.
"

The System Event Notification Service was really disabled on the server. Changing startup type to Manual solved the problem.
Hope this helps somebody.
Good Luck!
How to create SharePoint 2013 search Result Types in C#.
Hello!
Recently I had to create Visual Studio 2012 project to deploy SharePoint 2013 search results customizations. Due to lack of documentation the task was quite challenging. In this post I'd like to share my experience.

Introduction.

SharePoint 2013 introduces new web part rendering based on client side code (javascript). For UI customizations developers can create Display Templates. This process described well in the post: Using Query Rules, Result Types and Display Templates for a Custom Search Sales Report in SharePoint 2013 . According to it you have to configure result types (RTs) to map your custom template to specific document or list item type. Lets look how to do this using code.

Object Model.

Analyzing SharePoint 2013 MSDN Search Class Library documentation I found following classes that represent required entities:
  • ResultItemType - represents actual RT.
  • PropertyRule - represents property to value mapping rule that indicates what content should match the rule.
  • SearchServiceApplicationProxy - represents Search Service Application Proxy connection.
When I tried to recreate the manually created RTs using the object model I had no luck. Actually RTs were created and I could ensure that using object model but when I tried to open OOTB RT editing page I received an exception. In order to find out how SharePoint does the same things I looked through RT editing page code using .NET Reflector and was very surprised that most of valuable properties and methods are internal. Luckily it is possible to call them using reflection.

Implementation.

Lets start with RT creation method. If you look at RT settings page you will notice that you should provide name, set of rules, display template URL and optionally check "Optimize for frequent use" checkbox. Hence the method contains all the parameters mentioned.

public static ResultItemType CreateResultType(SPWeb web, string name, string displayTemplateUrl, PropertyRule[] rules, bool optimizeForFrequentUse)
        {
            ResultItemType resType = new ResultItemType(new SearchObjectOwner(SearchObjectLevel.SPWeb, web));
            resType.Name = name;
            resType.SourceID = new Guid();
            resType.DisplayTemplateUrl = "~sitecollection" + displayTemplateUrl;
            SPFile file = web.GetFile(SPUtility.ConcatUrls(web.ServerRelativeUrl, displayTemplateUrl));
            resType.DisplayProperties = ParseManagePropertyMappings(file.ListItemAllFields["Managed Property Mappings"].ToString());
            resType.Rules = new PropertyRuleCollection(new List<PropertyRule>(rules));
            typeof(ResultItemType).GetProperty("OptimizeForFrequentUse").SetValue(resType, optimizeForFrequentUse);
            return resType;
        }


The first interesting thing that DisplayTemplateUrl should be a path to *.js file in "~sitecollection/...."  format (eg. "~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Template.js"), so in this case displayTemplateUrl parameter should be in "/.../file.js" format.
The second important property is DisplayProperties. It should correspond the list of Managed Properties you use in the Display Template. You should specify comma separated Managed Property names here. To retrieve the property list dynamically we can use file's property named  "Managed Property Mappings". The trouble is that in the property the list is represented in the same way as in Display Template (in "Name1":"Name2" comma separated values format). So I'm using ParseManagePropertyMappings method (see below) for conversion.
 private static string ParseManagePropertyMappings(string mappings)
        {
            string[] propArray = mappings.Replace("'", "").Replace("\"", "").Split(',');
            for (int i = 0; i < propArray.Length; i++)
            {
                if (propArray[і].Contains(":"))
                {
                    int n = propArray[і].LastIndexOf(':');
                    if ((n > 0) && (n < (propArray[і].Length - 1)))
                    {
                        propArray[і] = propArray[і].Substring(n + 1);
                    }
                }
                propArray[і] = propArray[і].Replace(";", ",");
            }
            return string.Join(",", propArray);
        }

The logic of the method was borrowed from Microsoft's one.
And finally OptimizeForFrequentUse property has internal setter so we should utilize reflection to set its value.

Next valuable part is to create rules for RT. There are 2 types of rules: predefined rules, such as Word document (Mapped rules) and regular property-to-value mapping rules. Lets consider how do we create predefined rules:
public static PropertyRule CommonPropertyRule(string typeOfContent)
        {
            Type type = typeof(PropertyRule.MappedPropertyRules);
            FieldInfo info = type.GetField(typeOfContent, BindingFlags.NonPublic | BindingFlags.Static);
            object value = info.GetValue(null);
            return (PropertyRule)value;
        }

We have to use reflection again to retrieve predefined rules. typeOfContent corresponds values from the "type of content" dropdown of the editing page (Word, Excel, PDF etc).
To create another type of rules we can use following method:
public static PropertyRule CustomPropertyRule(string propertyName, PropertyRuleOperator.DefaultOperator propertyOperator, string[] values)
        {
            Type type = typeof(PropertyRuleOperator);
            PropertyInfo info = type.GetProperty("DefaultOperators", BindingFlags.NonPublic | BindingFlags.Static);
            object value = info.GetValue(null);
            var DefaultOperators = (Dictionary<PropertyRuleOperator.DefaultOperator, PropertyRuleOperator>)value;
            PropertyRule rule = new PropertyRule(propertyName, DefaultOperators[propertyOperator]);
            rule.PropertyValues = new List<string>(values);
            return rule;
        }


Finally, in order to add result types to a Web we can use for example FeatureActivated event of site collection scoped feature:
public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            SPSecurity.RunWithElevatedPrivileges(() =>
                {
                    SPSite site = properties.Feature.Parent as SPSite;
                    SPWeb web = site.RootWeb;
                    SPServiceContext serviceContext = SPServiceContext.GetContext(site);
                    if (serviceContext != null)
                    {
                        SPServiceApplicationProxy proxy = serviceContext.GetDefaultProxy(typeof(SearchServiceApplicationProxy));
                        SearchServiceApplicationProxy searchAppProxy = proxy as SearchServiceApplicationProxy;
                        SearchService searchService = SearchService.Service;
                        SearchServiceApplication searchApp = searchService.SearchApplications.GetValue<SearchServiceApplication>(searchAppProxy.GetSearchServiceApplicationInfo().SearchServiceApplicationId);
                        ResultItemType documents = CreateResultType(web, "MyDocuments", "/_catalogs/masterpage/Display Templates/Search/MyCustomDocuments.js",
                            new PropertyRule[] { CommonPropertyRule("Word"),
                                CommonPropertyRule("Excel"),
                                CommonPropertyRule("PowerPoint"),
                                CommonPropertyRule("PDF"),
                                CommonPropertyRule("Text")
                            }, true);
                        searchAppProxy.AddResultItemType(documents);
                        ResultItemType MyTask = CreateResultType(web, "MyTask", "/_catalogs/masterpage/Display Templates/Search/MyTask.js",
                            new PropertyRule[] { CustomPropertyRule("ContentType", PropertyRuleOperator.DefaultOperator.IsEqual, new string[] { "Task" }) });
                        searchAppProxy.AddResultItemType(MyTask);                       
                    }
}
            );
        }


Thats all. Happy coding!
Good luck!
Ошибка &quot;The field with Id {&lt;Field ID&gt;} defined in feature {&lt;Feature ID&gt;} was found in the current site collection or in a subsite.&quot; при активации фичи в SharePoint 2013
Сегодня столкнулся с такой вот проблемой. Начитавшись о совместимости WSP между SP 2010 и SP 2013 решил попробовать установить пакет, изначально созданный в VS 2010 для SP 2010 на SP 2013. Проект содержал описание полей (Field Definitions) и тип контента (Content Type), использующий эти поля. Итак, запустил PowerShell, ввел Add-SPSolution, Install-SPSolution. Инсталляция прошла без проблем, но при активации фичи получил ошибку:

Error occurred in deployment step 'Activate Features': The field with Id {7f4d0e75-fb5a-41ff-ab25-0d64c082550f} defined in feature {892ab683-f32f-4038-b118-bcf4a95e6244} was found in the current site collection or in a subsite.


Учитывая, что устанавливал на только что созданную коллекцию сайтов, ошибка более чем странная. Попробовал открыть проект в VS 11 и установить из нее, но ошибка та же. Попробовал создать новый проект в 11 студии, скопипастил туда описания полей и типа контента - инсталляция прошла на ура. В конце-концов при помощи гугла узнал, что WSP и проекты для SP 2010 по умолчанию устанавливаются в \14 ветку SP 2013 (он имеет обе 15 и 14). Для того, чтобы явно указать 15 ветку при инсталляции из PowerShell в команде Install-SPSolution следует использовать ключ -AddToLatestVersion. Решил проверить, в этом ли проблема. Инсталляция с этим ключом моего проекта прошла успешно.

Следовательно, при появлении вышеописанной проблемы инсталлировать WSP нужно с ключом -AddToLatestVersion.
Почему так - пока не знаю. Так же еще не понял, как заставить VS 11 сразу деплоить проекты SP 2010 в 15 ветку.

Надеюсь, мой пост сохранит кому-то часик-другой :)
Удачи и спасибо за внимание!
Автоматизация создания отказоустойчивого кластера с CSV на Microsoft Windows Server 2008 R2 (2).

В предыдущей статье мы поговорили о первых шагах к настройке отказоустойчивого кластера, а именно автоматизированной настройке соединения с iSCSI Target и активации фичи Failover Clustering. Сейчас же мы рассмотрим само создание кластера и добавление к нему узлов.

В отличие от предыдущей задачи, автоматизация настройки кластера очень упрощается, если использовать PowerShell.

Настройка кластера – CreateCluster.ps1.

import-module FailoverClusters

# считываем список хостов в переменную

$hosts=Get-Content hostlist.txt

$ClusterFqdn = Read-Host "Enter Cluster Name for the new cluster"

$ClusterIP = Read-Host "Enter cluster primary IP"

#Создание нового кластера

New-Cluster -Node $hosts -Name $ClusterFqdn -NoStorage -StaticAddress "$ClusterIP"

#Далее мы должны добавить диски к кластеру. Если диски уже ранее были использованы в другом кластере, при добавлении может возникнуть ряд неприятностей, одна из которых – ошибка типа «Cluster disk with identifier (identifier) has a persistent reservation on it ,the disk might be part of other cluster.». Поэтому выводим пользователю сообщение о возможных проблемах и методом их решения.

Write-Host "Configure iSCSI target disks. Assign letters to them and press Enter. If discs were used earlier for Cluster Shared Volume you need to run cluster.exe node <TheNodeName> /clear:<disknumber> for all the iSCSI disks of the node..." -ForegroundColor yellow

Read-Host

# Добавляем диски к кластеру

Write-Host "Add disks to Windows 2008 R2 Cluster..." -ForegroundColor yellow

Get-ClusterAvailableDisk | Add-ClusterDisk

# Изменяем тип кворума

Write-Host "Change the Quorum model Windows 2008 R2 Cluster..." -ForegroundColor yellow

Set-ClusterQuorum -NodeMajority

#Включаем CSV

Write-Host "Enabling Cluster Shared Volume" -ForegroundColor yellow

Get-Cluster | %{$_.EnableSharedVolumes = "Enabled/NoticeRead"}

#Добавляем наши iSCSI диски к CSV

Write-Host "Adding Cluster Disks to Cluster Shared Volume..." -ForegroundColor yellow

Get-ClusterResource | where {$_.OwnerGroup -match "Available Storage"} | Add-ClusterSharedVolume

 

Данный скрипт нужно запускать на одном из узлов кластера. Конечно можно реализовать и удаленность, но с PsExec PowerShell работает плохо, а Remoting надо дополнительно настраивать. Смысла в удаленном запуске данного скрипта немного, а вот для удобства могу посоветовать запускать его из bat файла примерно такого содержания:

powershell.exe -NoExit -File CreateCluster.ps1 -ExecutionPolisy RemoteSigned

Это позволит запустить скрипт, не настраивая при этом политики запуска всей оболочки PowerShell.

На этом пока все.

Спасибо за внимание!

Автоматизация создания отказоустойчивого кластера с CSV на Microsoft Windows Server 2008 R2 (1).

Кто хоть раз в жизни настраивал отказоустойчивый кластер (Failover Cluster) с CSV (Cluster Shared Volume) на Microsoft Windows Server 2008 R2 с количеством узлов больше 2-х, тот скорее всего очень мечтал об автоматизации этого процесса. Лично мне иногда приходится это делать для реализации миграции виртуальных машин на Microsoft Hyper-V. Хорошее пособие по настройке этого дела вручную здесь.

Основной неприятностью в данном процессе является необходимость подключения iSCSI Target к каждому из узлов кластера а так же активация фичи Failover Clustering на всех узлах. Процесс настройки самого iSCSI хранилища я рассматривать не буду, т. к. их существует множество разновидностей, как аппаратных, так и программных. Я использовал Microsoft iSCSI Software Target. Итак, стартовые позиции: на всех необходимых узлах установлена Windows Server 2008 R2 Enterprise (желательно с последним комплектом обновлений) с установленной ролью Hyper-V но без Failover Clustering, настроенный работоспособный iSCSI Target со всеми необходимыми дисками. Естественно, все узлы в одном домене.

Описание всего процесса я решил разбить на 2 статьи, в каждой из которых рассматриваются логически почти независимые операции.

Первая статья будет посвящена подключению таргета и активации фичи. Учитывая тот замечательный факт, что управлять встроенным в ОС iSCSI инициатором и настройкой фичей можно из командной строки, создание скрипта напрашивается само собой. Для универсальности процесс работы с инициатором и активацию фичи я решил разнести по разным скриптам.

Подключение iSCSI Target – connectTarget.bat.

@echo off

Rem устанавливаем автозапуск сервиса iSCSI инициатора

sc config msiscsi start= auto

rem запускаем сервис

net start msiscsi

rem для универсальности скрипт предполагает запуск с параметрами connectTarget <TargetPortal> <Port> <TargetIQN>

rem добавляем сервер

iscsicli addtargetportal %1 %2

rem добавляем таргет

iscsicli QAddTarget %3 %1

rem настраиваем автосоединение с таргет после перезагрузки

rem мы не используем аутентификацию, поэтому chapusername chappassword игнорируются

iscsicli persistentlogintarget %3 T * * * * * * * * * * * chapusername chappassword 0 * 0

rem выполняем соединение с таргетом

iscsicli qlogintarget %3

   

Активация фичи – AddFeature.bat.

@echo off

Rem параметром скрипта будет имя фичи

DISM /online /enable-feature /featurename:%1

   

Все вроде бы отлично, но эти скрипты позволяют выполнить свою функцию только в случае запуска непосредственно на каждом из узлов. А очень хочется запустить где-то в одном месте и чтобы все магическим образом сработало. Тут на помощь приходит замечательная утилита PsExec. Она позволяет выполнять команды на удаленных компьютерах, а в случае скриптов – может даже скопировать их на удаленную машину и выполнить. Для реализации нашей идеи создаем текстовый файл hostlist.txt, каждая строчка которого будет либо IP либо имя удаленного хоста. Приведенный ниже скрипт выполняет интересующую нас настройку на всех будущих узлах.

PreConfig.bat

@echo off

rem Initial parameters

rem iSCSI Target server IP or hostname

set TargetPortal=10.25.2.26

rem iSCSI Target port. Default is 3260

set TargetSocket=3260

rem iSCSI Target IQN

set TargetIQN=iqn.1991-05.com.microsoft:win-fn2t41fu88p-vmstore-target

rem Configuring Targets

echo Configuring Targets...

psexec @hostlist.txt -c connectTarget.bat %TargetPortal% %TargetSocket% %TargetIQN%

echo.

rem Configuring Failover Cluster Feature

echo Configuring Failover Cluster Feature...

rem запускаем скрипт на каждом хосте, указанном в hostlist.txt.

psexec @hostlist.txt -c AddFeature.bat FailoverCluster-FullServer

rem на всякий случай перезагружаем узлы, при этом указав причины перезагрузки и задержку в 1 минуту

psexec @hostlist.txt shutdown /r /t 60 /d p:2:4 /c "Restarted by Failover Setup Script"

   

Как видно из исходного кода, параметры таргета я задаю прямо в скрипте. Все 3 файла скриптов вместе с hostlist.txt нужно поместить в одну папку на одном из компьютеров, входящих в один домен с будущими узлами (это может быть не обязательно сам узел). Запуск PreConfig.bat производим из командной строки, запущенной от имени доменного администратора (или другого доменного пользователя с правами администратора).

Продолжение здесь.

Спасибо за внимание!