В данной заметке я решил немного поразмышлять о том, чего же мне не хватает в BCS при планировании основных сервисов приложений, руководствуясь стандартными для SharePoint практиками. Большинство из этих ограничений связаны с природой сервисов BCS, а именно тем, что данные не находятся в локальном хранилище, соответственно не могут полностью контролироваться с помощью сервисов SharePoint.

Итак, начнём:

  • Нельзя давать разрешения на отдельные записи во внешнем списке (external list). Наконец-то SharePoint в исполнении 2010ой версии научили корректно использовать Access Control Lists (ACL), однако, по вполне понятным причинам данные возможности не доступны при работе с внешними данными
  • Возможности рабочих процессов не доступны полностью для внешних списков. Казалось бы, всё замечательно и готово для интеграции, однако внешние данные остаются внешними данными, и многие события, необходимые для работы с рабочими процессами недоступны для SharePoint, однако это не исключает использование внешних данных в рабочих процессах, работающих на обычных списках. Об этой проблеме и способах её разрешения довольно-таки подробно описано в статье Using SharePoint workflows with Business Connectivity Services (BCS).
  • Отсутствие версионности и истории изменений во внешних списках. Вполне логично, что внешние списки остаются лишь “обёрткой” для внешних данных и хранить изменения, а следовательно и обеспечивать версионность, довольно-таки неординарная задача, которую команда BCS решила не касаться, по той же причине “элементы социального взаимодействия”, такие как рейтинги и тегирование, так же будут недоступными при работе с внешними данными (BCS).
  • Экспорт в Excel. Очень странно почему данный функционал не доступен, т.к. не накладывает никаких ограничений на используемые данные, а просто меняет их представление. Как альтернативу данному подходу могу предложить что-нибудь наподобие Print List (http://www.sharepoint-tips.com/2007/01/how-to-add-print-list-option-to-list.html), т.е. решения по экспорту внешних данных из SharePoint будут подразумевать кастомизацию.
  • RSS каналы. С одной стороны причины схожи с экспортом в Excel, однако здесь, как мне кажется, противоречие даже со здравым смыслом, т.е. RSS-канал как правило должен отслеживать изменения в данных и оповещать об этом подписчика, в случае с внешними данными, они могут и не меняться, а будет меняться лишь выборка, что будет генерировать многочисленное количество неконтролируемых обновлений.
  • Просмотр в виде таблицы. Очень удобная функция, не понятно, что именно сподвигло на её отсутствие команду разработчиков, скорее всего возможности пакетной обработки в новом интерфейсе SharePoint, которыми она была так удобна.
  • Внешние данные не доступны для доступа через REST сервисы. Как мне кажется причина так же в возможности непрогнозируемого изменения данных произвольным образом, что не даёт никакой гарантии для потребителя данных сервисов.

В заключение

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

Введение

Меня переполняют эмоции. Это связанно с тем, что сегодня мой товарищ сообщил о следующем: есть полноценная реализация BDD движка для .NET.

Называется проект SpecFlow

Исходный код ныне базируется на GitHub http://github.com/techtalk/SpecFlow

Немного теории

Вопрос о том, что же такое Behavior Driven Development неоднозначен. Особенно противоречивы его отношения с Test Driven Development. Однако предлагаю абстрагироваться от сравнения двух подходов и договорится, что суть этого есть одно и тоже, т.к. особенных различий нет. Основной чертой выделяющей BDD от TDD по-моему усмотрению является ориентация на различных участников процесса производства программного обеспечения, будь то менеджер, разработчик, тестер, заказчик. Вы вместе пишете тесты, и вместе можете развивать проект в нужном направлении и вполне понятных терминах. Так же на ум в данном случае приходит такой термин, как Ubiquitous Language из Domain Driven Design. А ведь и в самом деле, обобщение терминологии описания вашей предметной области вполне может ложится на практику BDD.

Меня в BDD больше всего прельщает возможность описания функциональности на разговорном языке, что гарантирует актуальность документации (т.к. ваши behavior тесты и будут фактически вашей документацией). Так же при написании тестов на подобном уровне вы четко сконцентрированы на поставленной задаче, а не способах её разрешения и применяемых инструментах, что так же весьма полезно для качества как вашего дизайна, так и соответствии программного продукта назначению.

Все известные инструменты (к примеру mock-объекты) и практики (такие как шаблоны тестирования), используемые вами при разработке в стиле TDD, изменятся незначительно при переходе на BDD стиль, в связи с чем можно начать применять BDD уже сегодня. Так же вполне возможно, что вам необходимо будет произвести более низкоуровневое тестирование некоторых компонентов, что несомненно приведёт к смешиванию двух практик, что несомненно должно происходить осознано и с должен вниманием.

Снаружи вовнутрь. Процесс при разработке в стиле BDD так же немного смещается. Так как тестами двигают в равной мере все участники, то ориентация в первую очередь будет на интерфейсе пользователя. т.е. для описания необходимых фич, заинтересованным лицам (скорее всего заказчику) необходимо будет держать перед глазами эскизы эрканов для взаимодействия. Этот подход снижает риски неоправданных ожиданий, т.к. в большинстве случаев программный продукт для потребителя – всего лишь пользовательский интерфейс.

Инструменты

cuke_logo[1] На данный момент существует множество BDD framework’ов, однако “наиболее верным” с точки зрения родоначальников данного термина (Dan North, David Chelimsky, Aslak Hellesøy) является такой проект как: Cucumber (ранее известный как RSpec). Этот проект написан для Ruby. Большинство RubyOnRails разработчиков (как основные представители ruby сообщества) пользуются именно этим фреймворком для тестирования своих продуктов.

Известны некоторые способы запуска данного проекта под .NET с помощью IronRuby, однако должной поддержки со стороны как сред разработки в .NET, так и внимания сообщества такое решение не получило.

Инструменты для .NET в основном копируют формат и принципы, заложенные в Cucumber, их существует несколько:

  • SpecFlow – предмет сегодняшнего разговора (из плюсов – аналогия с Cucumber и интеграция в Visual Studio 2008 и 2010)
  • NBehave – работа с данным фреймворком близка к возможностям Cucumber в настоящее время (до этого он так же был в разряде “многословных” и  это, наверное, наиболее распространенный BDD фреймворк среди .NET разработчиков)
  • NSpecify, NSpec – довольно-таки “многословные” фреймворки для BDD
  • Specter – аналогично, примечателен тем, что написан на Boo

Теперь практика

Для того, чтобы проиграться с этим замечательным инструментом нам понадобятся:

Итак, давайте приступим. Если хочется открыть сразу всё это и посматривать в блог, а разбираться там, то вполне можно скачать пример, ссылка на него будет внизу текста.

Создавать мы с вами будет стандартный пример для подобного рода фреймвороков, а именно Калькулятор.image

Создадим новый solution в студии, в котором будет два проекта.

  1. Calculator
  2. Calculator.Tests

Для того, чтобы его описать в решении + подготовить реализацию нам понадобится немного инфраструктурных заморочек в проекте Calculator.Tests (ниже работа пока будет только с ним):

  • добавить reference на TechTalk.SpecFlow.dll (которая скорее всего окажется в папке с установленным SpecFlow)
  • добавить reference на nunit.framework.dll (его следует искать в GAC’е)

Теперь всё готово для того, чтобы погрузиться в мир BDD. Откиньтесь на спинки кресел и расстегните ремни.

В пункте Add New Item у вас наверняка появились новые пункты, начинающиеся на SpecFlowimage

Далее задайте имя нашей фиче addition.feature и щелкните Add.

Как вы догадались мы добавим описание первой фичи, для этого тот текст, который будет содержаться в файле по-умолчанию следует заменить на следующий:

http://github.com/aslakhellesoy/cucumber/blob/master/examples/i18n/en/features/addition.feature# language: en

Feature: Addition

  In order to avoid silly mistakes

  As a math idiot 

  I want to be told the sum of two numbers

  Scenario Outline: Add two numbers

    Given I have entered <input_1> into the calculator

    And I have entered <input_2> into the calculator

    When I press <button>

    Then the result should be <output> on the screen

  Examples:

    | input_1 | input_2 | button | output |

    | 20      | 30      | add    | 50     |

    | 2       | 5       | add    | 7      |

    | 0       | 40      | add    | 40     |

Жмём ctrl+s и большая часть работы по описанию выполнена.

Теперь нам надо сделать описание steps, для того, чтобы связать сценарий с тестируемым модулем. Для этого добавьте новый элемент SpecFlow Step Definition, назовём мы его calculator_steps.cs.

Данный файл уже содержит в себе некоторые записи по-умолчанию, в принципе они вполне подходят для нашего примера. (Кстати, обратите внимание как строго упорядочены все методы по шаблону AAA: Arrange, Act, Assert, в данной интерпретации Given, When, Then)

Однако немного их всё же придется изменить.

Внутри данного метода:

[Given("I have entered (.*) into the calculator")]
public void GivenIHaveEnteredSomethingIntoTheCalculator(int number)
{
  //TODO: implement arrange (recondition) logic
  // For storing and retrieving scenario-specific data,
  // the instance fields of the class or the
  //     ScenarioContext.Current
  // collection can be used.
  // To use the multiline text or the table argument of the scenario,
  // additional string/Table parameters can be defined on the step definition
  // method.

  ScenarioContext.Current.Pending();
}

Мы видим, что на нашем калькуляторе печатают некоторые числа, ок, подтвердим это:

[Given("I have entered (.*) into the calculator")]
public void GivenIHaveEnteredSomethingIntoTheCalculator(int number)
{
    Calculator.Input(number);

}

И не стесняйтесь того, что пока Visual Studio не совсем понимает, что такое Calculator ;)

Отлично, смотрим на следующий метод, его мы тоже немного поправим:

[When("I press add")]
public void WhenIPressAdd()
{

    Calculator.Add();

}

И, вслед за ним, удостоверимся, что получаем именно то, чего желаем:

[Then("the result should be (.*) on the screen")]
public void ThenTheResultShouldBe(int result)
{
    Assert.IsEqual(result, Calculator.Result);
}

Теперь самое время разобраться с нашим калькулятором.

Создадим следующее поле:

protected Calculator Calculator = new Calculator();

Теперь пора бы разобраться и с типом Calculator, его мы создадим в проекте Calculator. Так же добавим reference из проекта Calculator.Tests на проект Calculator. С типом так же всё в порядке, однако не хватает некоторых методов, их тоже следует создать.

Теперь, когда ваш проект скомпилируется, можете запустить ваш testrunner, чтобы убедится, что вы на красной полосе.

image

Теперь можно заняться имплементацией нашего калькулятора. На данный момент я предлагаю следующее:

public class Calculator
{
  private IList<int> _buffer = new List<int>();

  public int Result { get; private set; }

  public void Input(int number)
  {
    _buffer.Add(number);
  }

  public void Add()
  {
    Result = _buffer.Sum();
    _buffer.Clear();
  }

}

Запускаем наши тесты и видим, что они проходят, супер!

image

Вроде бы с первой фичей мы успешно разобрались, почему бы не взяться за вторую?

Второй возможностью будет деление:

http://github.com/aslakhellesoy/cucumber/blob/master/examples/i18n/en/features/division.feature

# language: en

Feature: Division

  In order to avoid silly mistakes

  Cashiers must be able to calculate a fraction

  Scenario: Regular numbers

    Given I have entered 3 into the calculator

    And I have entered 2 into the calculator

    When I press divide

    Then the result should be 1.5 on the screen

*в оригинале для cucumber у aslakhellesoy несколько иной синтаксис, но он на данный момент не поддерживается в SpecFlow

данную фичу мы добавим в наш тестовый проект под названием devision.feature. как не трудно заметить, что основные шаги мы с вами уже описали в предыдущем примере, сейчас осталось только добавить 1 метод:

[When("I press divide")]
public void WhenIPressDevide()
{
  Calculator.Devide();
}

Добавить так же метод в класс Calculator.

Скомпилировать наш проект. И запустить тесты. Конечно же они не пройдут, т.к. деление мы с вами ещё не реализовали:

public void Devide()
{
  Result = _buffer[0] / _buffer[1];
  _buffer.Clear();
}

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

Материалы

Исходный код рассматриваемого проекта http://dl.dropbox.com/u/4070949/Calculator.zip

Скринкаст от создателей SpecFlow http://www.specflow.org/specflow/screencast.aspx

Моя статья Пример практики BDD при работе со Specter Framework

Книга The RSpec Book: Behaviour Driven Development with RSpec, Cucumber, and Friends

Википедия http://en.wikipedia.org/wiki/Behavior_Driven_Development

Участники: butaji & dnesteruk

Persistence в .Net-приложениях

Подкасты Петерубргской Группы Alt.Net

Подкасты Петерубргской Группы Alt.Net

Подкасты о разработке в среде .Net. Ключевые слова: C#, F#, Boo, Visual Studio, .Net, PostSharp, Asp.Net

личная подкаст-лента Петербургская Группа Alt.NetПетербургская Группа Alt.Net (подробнееRSS-поток)

Введение

Предлагаю рассмотреть несколько примеров работы с SharePoint 2010 Business Connectivity Services.

Все ресурсы, связанные с Business Connectivity Services можно найти на Business Connectivity Services Resource Center. Кстати на нём же можно скачать замечательный постер-шпаргалку по технологии.

image

Для работы с рассматриваемыми примерами предлагаю вам скачать SharePoint 2010 SDK.

Adventure Works Web Service

В данном примере показана реализация стандартного asp.net asmx сервиса, ориентированного на работу с BCS. Этот сервис представляет доступ к данным из SQL Server. С помощью LINQ to SQL реализовано объектно-реляционное преобразование.

Вы наверняка согласитесь с утверждением, что при прочих равных условиях asmx сервисы создаются намного проще WCF сервисов

Для работы с ним понадобится sample database с соответствующим названием “AdventureWorks”.

Примеры баз данных (таких как AdventureWorks) для основных версий SQL Server, а так же решений для них, теперь централизованны и хранятся на CodePlex: http://msftdbprodsamples.codeplex.com/

Сервис WebService.asmx предоставляет ряд методов для стандартных операций взаимодействия с данными (Create, Read, Update, Delete).

Для наглядности предметной области основные сущности определены в виде POCO классов, к примеру:

public class SalesCustomer
{
    public int CustomerId { get; set; } 

    public String Title { get; set; }
    public String FirstName { get; set; }
    public String MiddleName { get; set; }
    public String LastName   { get; set; }
    public String EmailAddress { get; set; }
    public String Phone { get; set; }
    public DateTime ModifiedDate { get; set; }
}

Классы же, генерируемые LINQ to SQL используются по назначению, т.е. в качестве DataModel, которая в последствии отображается на модель предметной области. Хоть пример и учебный, было очень приятно, что он имеет довольно таки логичное и обоснованное применение технологий по назначению.

Предметная область в данном случае состоит из 3 основных сущностей:

  1. SalesCustomer
  2. SalesOrderDetail
  3. SalesOrderHeader

Причём дизайн данных сущностей ориентирован на работу с BCS, это можно проследить по построению ассоциаций между сущностями не по ссылке, а по идентификатору. К примеру, связь между SalesCostumer и SalesOrderHeader:

public class SalesOrderHeader
{

    public int CustomerID { get; set; }

    …

}

Здесь стоит пояснить механизм работы с ассоциациями в BCS. Ассоциации в BCS – основной механизм связи нескольких “внешних типов содержимого”. Для его реализации используются так называемые Association Methods. По организации работы принцип вторит тому, что применяется в реляционных базах данных для реализации отношений, т.е. через доступ по внешнему ключу (Foreign Key).

В связи с данными особенностями в сервисе определяются специализированные методы, например этот:

[WebMethod]
public SalesOrderHeader[] GetOrdersForCustomer(int customerId)
{
    List<SalesOrderHeader> salesOrderHeaders = new List<SalesOrderHeader>();
    Adventureworks.SalesOrderHeader[] sohArr =
        (from soh in dataContext.SalesOrderHeaders
         where soh.CustomerID == customerId
         select soh).ToArray();
    PopulateSalesOrderHeader(salesOrderHeaders, sohArr);
    return salesOrderHeaders.ToArray();
}

Данные методы в дальнейшем будут привязаны к BCS Associations Methods, которые и будут работать для связанных сущностей.

Представленный веб-сервис следует опубликовать в IIS.

Далее, воспользовавшись SharePoint Designer 2010 создать и опубликовать BDC Model:

  • Открыть в SharePoint Designer 2010 интересующий вас узел
  • Перейти в секцию External Content Types
  • Создать новый “внешний тип содержимого”
  • В качестве источника данных выбрать WCF Service
  • Указать URL подключения и тип соединения
  • С помощью мастера произвести отображение всех необходимых веб-методов на методы BDС Model
  • Сохранить внешний тип содержимого

Данный процесс наглядно описан в SDK http://msdn.microsoft.com/en-us/library/ee556431(office.14).aspx

 

Пример файла, описывающего BDC Model
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Model xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.microsoft.com/windows/2007/BusinessDataCatalog BDCMetadata.xsd" Name="AdventureWorksWSModel" IsCached="false" xmlns="http://schemas.microsoft.com/windows/2007/BusinessDataCatalog">
   <LobSystems>
    <LobSystem Type="Wcf" Name="AdventureWorksWS">
      <Properties>
        <Property Name="WsdlFetchAuthenticationMode" Type="System.String">PassThrough</Property>
        <Property Name="WcfMexDiscoMode" Type="System.String">Disco</Property>
        <Property Name="WcfMexDocumentUrl" Type="System.String">http://webserver:90/webservice.asmx?wsdl</Property>
        <Property Name="WcfProxyNamespace" Type="System.String">BCSServiceProxy</Property>
        <Property Name="WildcardCharacter" Type="System.String">*</Property>
      </Properties>
      <LobSystemInstances>
        <LobSystemInstance Name="AdventureWorksWS">
          <Properties>
            <Property Name="WcfAuthenticationMode" Type="System.String">PassThrough</Property>
            <Property Name="WcfEndpointAddress" Type="System.String">http://webserver:90/webservice.asmx</Property>
            <Property Name="ShowInSearchUI" Type="System.String"></Property>
          </Properties>
        </LobSystemInstance>
      </LobSystemInstances>
      <Entities>
        <Entity Namespace="AdventureWorks” Version="1.0.0.0" EstimatedInstanceCount="10000" Name="WSCustomer" DefaultDisplayName="WSCustomer">
          <Properties>
            <Property Name="OutlookItemType" Type="System.String">Contact</Property>
          </Properties>
          <Identifiers>
            <Identifier TypeName="System.Int32" Name="CustomerId" />
          </Identifiers>
          <Methods>
            <Method IsStatic="false" Name="GetCustomerById">
              <Parameters>
                <Parameter Direction="In" Name="customerId">
                  <TypeDescriptor TypeName="System.Int32" IdentifierName="CustomerId" Name="customerId" />
                </Parameter>
                <Parameter Direction="Return" Name="GetCustomerById">
                  <TypeDescriptor TypeName="BCSServiceProxy.SalesCustomer, AdventureWorksWS" Name="GetCustomerById">
                    <TypeDescriptors>
                      <TypeDescriptor TypeName="System.Int32" ReadOnly="true" IdentifierName="CustomerId" Name="CustomerId" />
                      <TypeDescriptor TypeName="System.String" Name="Title" />
                      <TypeDescriptor TypeName="System.String" Name="FirstName">
                        <Properties>
                          <Property Name="OfficeProperty" Type="System.String">FirstName</Property>
                        </Properties>
                      </TypeDescriptor>
                      <TypeDescriptor TypeName="System.String" Name="MiddleName" />
                      <TypeDescriptor TypeName="System.String" Name="LastName">
                        <Properties>
                          <Property Name="OfficeProperty" Type="System.String">LastName</Property>
                        </Properties>
                      </TypeDescriptor>
                      <TypeDescriptor TypeName="System.String" Name="EmailAddress" />
                      <TypeDescriptor TypeName="System.String" Name="Phone" />
                      <TypeDescriptor TypeName="System.DateTime" Name="ModifiedDate" />
                    </TypeDescriptors>
                  </TypeDescriptor>
                </Parameter>
              </Parameters>
               <MethodInstances>
                <MethodInstance Type="SpecificFinder" ReturnParameterName="GetCustomerById" Default="true" Name="GetCustomerById" DefaultDisplayName="Read Item WSCustomer">
                 <Properties>
                   <Property Name="LastDesignedOfficeItemType" Type="System.String">Contact</Property>
                 </Properties>
                </MethodInstance>
               </MethodInstances>
            </Method>
          </Methods>
        </Entity>
      </Entities>
    </LobSystem>
  </LobSystems>
</Model>

AdventureWorksDll

Данный пример ориентирован на подключение .NET типа в качестве источника данных для внешнего типа содержимого. Фактически пример не отличается от предыдущего. Принципиальная разница состоит в том, что в данном случае сборка должна находится локально по отношению к установленному SharePoint. Все принципы дизайна методов доступа сохранены.

Кстати, данный пример практически ничем не уступает в поддержке актуальности варианту с веб-сервисом, т.к. современные методы дистрибуции .NET решений (к примеру ClickOnce) позволяют поддерживать в актуальном состоянии решение у большого количества клиентов.

Так что же происходит в BCS при работе с веб-сервисами

Механизм вполне логичен и является стандартным во многих ситуациях. Запрашивается URL, который указан в качестве адреса сервиса. На основе полученной схемы (как правило в SOAP это WSDL). Далее происходит самое интересное, в рантайме генерятся прокси-объекты для доступа к данным. Данные объекты компилируются в памяти и кэшируются для дальнейших вызовов.

Custom Connector

Кроме подключения к .NET типу, так же существует возможность реализации “своего коннектора”. В чём же принципиальная разница? В том, что коннектор является свободной абстракцией над источником данных. Т.е. подразумевается его настройка пользователем, к примеру, в SharePoint Designer’e, в отличии от .NET типа, который будет отражаться на конкретный “внешний тип содержимого”. Подробнее можно узнать здесь: http://msdn.microsoft.com/en-us/library/ee554911(office.14).aspx

BCS Cache

В заключение хочу немного рассказать о способах оптимизации ваших решений на основе BCS.

В BCS существует мощных механизм кэширования. Реализуется он на основе так называемых Cache Description. Описываются они в XML файле subscription.xml и должны располагаться в папке вместе в XML описанием BDC Model. С помощью можно описывать правила кеширования определенных методов, определенных сущностей (по их идентификатору), а так же связанных сущностей.

Так выглядит Cache Description

  <?xml version="1.0" encoding="utf-8"?>
<Subscription xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Name="CustomerQuery Item List" IsCached="true" EntityName="Customer" EntityNamespace="http://example.com" RefreshIntervalInMinutes="60" View="CustomerRead Item" LobSystemInstanceName="AdventureWorks" xmlns="http://schemas.microsoft.com/office/2006/03/BusinessDataCatalog">
  <Queries>
    <Query Name="27aa2ba2-fd5f-452c-87bb-24cf505f6071" IsCached="true" RefreshIntervalInMinutes="60" MethodInstanceName="CustomerQuery Item List" Enabled="false" />
  </Queries>
</Subscription>

Подробнее о кэшировании можно узнать здесь: http://msdn.microsoft.com/en-us/library/ee556385(office.14).aspx

Сегодня наконец-то дошли руки до Reflector 6 Pro Beta. Если вы ещё не слышали, то я повторюсь, что эта версия рефлектора умеет дебажить внешние сборки в Visual Studio. Конечно же меня в первую очередь заинтересовала возможность дебага Microsoft.SharePoint.dll

При первом запуске рефлектора, он автоматически установит плагин в студию. Далее с помощью появившегося пункта меню можно подгрузить интересующую сборочку:

image

Microsoft.SharePoint.dll загружался довольно таки приличное количество времени (несколько минут), после чего передо мной была следующая картинка:

image

Далее я выбрал в том же пункте меню Explore Decompiled Assemblies, нашёл в Object Browser класс SPWeb, выбрал в меню Go To Decompiled Definition, подключился к рабочему процессу w3wp с SharePoint и увидел следующее:

image

Считаю, что это очень полезная возможность. Рекомендую приобрести данный продукт, так как он сможет сэкономить большое количество вашего времени.

Сегодня немного поразмышлял о напрасно потраченных человекогодах, а именно о следующем.

Вспомните, что вы обычно делаете, когда вам через чур необходимо воспользоваться какой-нибудь программой, либо её функцией, а перед вами появляется окно сообщающее о какой-либо ужасающей ошибке? Правильно, вы перепечатываете сообщение об ошибке в строку поиска (на это и тратится некоторая часть времени). Ну так вот, мои размышления пришли к тому, а почему бы не ввести в качестве “хорошего тона для разработчиков” кнопку, для того, чтобы поисковик для вас открывался автоматически.

image

Причём, для “агностицизма” поисковика, можно детектить тот, который стоит по-умолчанию в вашем браузере.

Введение

Существует два подхода к аутентификации при работе с внешними источниками данных:

  • идентификатор пользователя
  • имперсонализация

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

Второй тип аутентификации подразумевает под собой наличие специализированной учетной записи, через которую пользователи вашей системы получают данные из внешних источников.

Явным примером двух типов аутентификации может являться работа с SQL Server для развертывания SharePoint.

При наличии у разработчика локально-развернутой копии SharePoint, получение данных происходит через идентификатор текущего пользователя, т.к. разработчик обычно выполняет все действия под единым для SharePoint и SQL Server пользователем.

Однако при варианте развертывания SharePoint на ферме, появляется необходимость имперсонализации пользователя для доступа к данным из SQL Server. Как правило заводится специализированная доменная учетная запись с необходимыми для работы с SQL Server разрешениями. И соответственно, все действия с данными SharePoint производит именно через неё, независимо от того, какой пользователь в данное время аутентифицирован в нём.

Рассмотрим, как же организовать имперсонализацию при работе с внешними источниками данных в SharePoint 2010.

Single Sign-On или Secure Stored Service

Вы наверняка знаете, что такое SSO (single sign-on), и помните, что в предыдущих версиях SharePoint она называлась именно так. В SharePoint 2010 данная служба сменила своё название, теперь она называется Secure Store Service (русский эквивалент: Служба Безопасного Хранения).

Данная служба занимается проверкой подлинности на сервере приложений, если быть точным, то она обеспечивает взаимодействие пользователей/групп пользователей с различными системами без необходимости повторного входа в систему. Основное назначение данной службы в SharePoint – взаимодействие с внешними системами. Если в вашей организации имеются приложения, данные из которых вы хотели бы предоставить для пользователей SharePoint, а так же обеспечить безопасность доступа, то вы должны воспользоваться SSS (Secure Store Service).

При настройке SSS вы указываете отображение данных пользователя SharePoint на данные, передаваемые внешней системе. Данными параметрами могут Имя пользователя, Пароль, Идентификатор пользователя. Набор полей не ограничен и может быть изменен в зависимости от ваших нужд. Для обеспечения взаимодействия с настольными приложениями так же доступны сервисы определения разрешений на основе идентификатора Windows пользователя.

Настройка SSS

Secure Stored Service представляет собой сервис аутентификации на сервере приложений, а так же базу данных, содержащую информацию о разрешениях пользователей. Как и SSO, он поддерживает федеративную аутентификацию, а так же имперсонализацию и делегирование.

На основе хранимых соответствий во время подключения внешних источников данных каждому пользователю назначаются соответствующие

Кстати, в отличии от SSO и BDC, который был доступен только в MOSS, Secure Stored Service и Business Connectivity Services будут доступны и в SharePoint Server 2010 и в SharePoint Fundation 2010

SSS можно включить в SharedServices. По-умолчанию данный сервис отмечен галочкой в мастере настройке фермы, так что велика вероятность того, что у вас он уже установлен.

Создадим Secure Store Application, для этого нам понадобится:

  1. Пройти к Central Administration – Application Management – Manage Service Applications
  2. Перед созданием приложения необходимо так же сгенерировать для него ключик:
  3. Далее вам необходимо будет указать администратора и настройки приложения, а так же отображаемые поля (по-умолчанию они называются Windows User Name, Windows Password, название не несёт в себе никакой нагрузки, кроме описательной, в связи с чем можно переименовать их просто в UserName и Password, чтобы не путаться).
  4. В конце создания укажите разрешения для приложения:

Вот мы и создали приложение для безопасного хранения, зачем же оно нам может понадобиться?

Рассмотрим пример со сторонним приложением, данные которого хранятся в SQL Server, однако по историческим причинам в нём заведены для доступа отдельные учетные записи. Перед вами стоит задача организовать приложение на BCS для доступа к этим данным.

Для этого вам необходимо:

  1. Запустить SharePoint Designer 2010
  2. Перейти к секции External Content Types
  3. Создать новый тип содержимого
  4. В качестве источника данных указать SQL Server
  5. В окне доступа к данным уточнить наименование сервера, наименование базы данных, так же имя для отображения. Так же вам потребуется выбрать способ имперсонализации (в данном случае на основе Windows Identity) и указать идентификатор (его наименование, которое вы указывали при его создании) Secure Store Application

 

Если соединение удастся, то вы всё сделали верно, остальные действия ничем не будут отличаться от стандартных, а так же будут действовать все указанные вами в Secure Store Application правила.

Давайте также разберемся с терминологией SharePoint аутентификации

Users Identity (PassThrough)

Пользователь, прошедший идентификацию обращается к BCS со своим идентификатором. Это обозначает, что Windows идентификатор будет передан напрямую через BCS приложение к IIS, который в свою очередь отправит его на SQL Server (к примеру), где в зависимости от его разрешений будут предоставлены данные.

BDC Identity (RevertToSelf)

Как не трудно догадаться в данном случае доступ к внешнему источнику данных будет осуществляться под идентификатором пользователя пула приложения (под ним выполняются службы BCS). Соответственно вам необходимо предоставить доступ к данным для этой учетной записи.

Impersonated Windows Identity (WindowsCredentials)

Под данным термином и подразумевается SSS аутентификация.

Impersonated Custom Identity

В основном под данным пунктом подразумевается Claims based Federated Authentication. К примеру поддерживающий SAML аккаунты пользователей

SAML (англ. Security Assertion Markup Language — язык разметки подтверждения безопасности) — основанный на языке XML стандарт, разработанный OASIS для обмена данными об аутентификации и авторизации между защищенными доменами. Одной из важных проблем, которую пытается решить SAML это обеспечение сквозной аутентификации (Технология единого входа, Single Sign On) при работе через Web-браузер.

Данный тип аутентификации так же весьма подходит для WCF сервисов, позволяющих передавать резрешения в Secure Token Service.

Если вы будете писать,  к примеру настольное приложение для работы с BCS, то данный вид аутентификации будет вам весьма полезен. Аналогично работают и Office-клиенты (Outlook, SharePoint Workspace). Каждый клиент обращается к Security Token Service на ферме SharePoint, где получает ”доверенный идентификатор”, с которым впоследствии отправляется в BCS.

RdbCredentials

Этот тип аутентификации используется для обработки идентификатора пользователя, полученного из не-Windows среды с помощью SSS.

Разрешения в BCS

Разрешения для BCS можно указывать для пользователей, групп пользователей, а так же запросов на нескольких уровнях. Разрешения доступа к данным хранятся в метаданных BCS, а так же имеют иерархическую структуру.

Напомню, что основными элементами описания в BCS являются:

  • Metadata store – фактически все описания, содержащиеся в BCS
  • Model – описание так называемой BDC Model, может содержать несколько внешних типов содержимого, связи с внешними данными, а так же типаутентификации
  • External system – источник данных (база данных, веб-сервис, .NET сборка)
  • External content type – внешний тип содержимого
  • Method – операция над типом содержимого
  • Method instance – настройка метода, к примеру параметры по-умолчанию

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

В SharePoint BCS существуют 4 типа разрешений:

  1. Edit – практически на всех уровнях иерархии позволяет редактировать элементы, а так же создавать новые
  2. Execute – как правило определяет какие методы могут быть вызваны
  3. Selectable in clients – означает, что пользователь может создавать на основе данного типа содержимого списки
  4. Set permissions – пользователь может указывать разрешения для других пользователей на данном элементе

Так же существуют специализированные группы разрешений для SharePoint:

  • Администртор фермы – привилегированный пользователь BCS, имеет полный набор прав по-умолчанию. Данные полномочия предоставлены для возможности восстановления прав. Однако администратор фермы не имеет прав Execute, пока ему их не предоставит администратор экземпляра BDC
  • Клиенты объектной модели – сюда попадают скриптовые сценарии (PowerShell, IronPython, STSADM Extensions), а так же приложения, написанные на .NET (C#, VB). Они так же имеют полный набор разрешений
  • Пользователи, под которыми запущены пулы приложений на серверах приложений – имеют те же права, что и администраторы ферм, это связанно, с тем, что есть необходимость в разрешениях для установки определений
  • Пользователи SharePoint Designer – имеют, как правило, полный набор прав, за исключением предоставления прав (set permissions), хотя могут убирать права

Редактировать разрешения можно через SharePoint Designer, узел администрирования, редактируя описание BDC Model’и, а так же через объектную модель. Первые два средства соответственно не дают возможностей тонкой настройки.

Материалы

http://www.lightningtools.com/blog/ – блог о SharePoint BDC, BCS от создателей BDC MetaMan и MCS MetaMan

http://technet.microsoft.com/en-us/library/ee681491(office.14).aspx – планирование BCS на Technet

Первая встреча RUSUG в Москве в новом году состоится 22 января.

Местонахождение: Офис «Microsoft Russia» в Москве

План встречи:

19:00 — доклад «Пользуемся готовыми решениями сообщества для SharePoint», Виталий Баум;
20:30 — перерыв;
20:45 — доклад «PowerShell и SharePoint», Василий Гусев.
Регистрация: http://sharepoint.su/UG/Lists/Jan2010Reg/NewForm.aspx

На эту встречу к нам приедет Виталий Баум из Санкт-Петербурга. Если вы смотрите видеозаписи встреч группы, то уже видели один доклад Виталия, посвящённый введению в SharePoint. Тема доклада Виталия «Пользуемся готовыми решениями сообщества для SharePoint».

Он расскажет нам о следующем:

  • Почему надо использовать решения с CodePlex
  • Почему не надо использовать решения с CodePlex
  • Как надо использовать решения с CodePlex
  • Обзор доступных решений от сообщества для SharePoint

Вторым докладчиком будет Василий Гусев, известный эксперт, MVP по PowerShell. Он расскажет нам о связке PowerShell и SharePoint. После краткого знакомства с основами PowerShell вы узнаете что он может дать разработчику, и как можно его использовать для работы с SharePoint.

Встречались:

http://spbalt.net : butaji и dnesteruk

http://csharpus.com/ : dimapasko и tihobrazov

Холиварим, .NET и аналоги:

http://highscalability.com/blog/2009/8/5/stack-overflow-architecture.html

http://stackoverflow.com/questions/791447/windows-azure-vs-amazon-ec2-vs-google-app-engine

 

Обсуждение новых фич .NET 4.0, в частности:

 

Подробнее о подкастах:

Подкасты Петерубргской Группы Alt.Net
Подкасты Петерубргской Группы Alt.Net

Подкасты о разработке в среде .Net. Ключевые слова: C#, F#, Boo, Visual Studio, .Net, PostSharp, Asp.Net
личная подкаст-лента
Петербургская Группа Alt.NetПетербургская Группа Alt.Net (подробнее, RSS-поток)

 
csharpus podcast

Подкаст о разработке на платформе .NET
личная подкаст-лента
Dima PaskoDima Pasko (подробнее, RSS-поток)

Снова о unit-tests?


Да, я уже рассказывал о том, как “правильно” писать юнит-тесты для SharePoint, однако сегодня наткнулся на одну интересную разработку, речь о которой пойдёт далее.

В состав Pex входит инструментарий для создания stub-объектов под названием Mole, он умеет работать с закрытыми для наследования объектами, на коих строится объектная модель SharePoint.

Если вы не знаете что такое Pex

В компании Microsoft есть подразделение, занимающееся научными разработками, называется оно Microsoft Research, подробнее узнать о том, чем же занимается это подразделение можно узнать здесь: http://research.microsoft.com/en-us/research/default.aspx

Одно из разработок данного подразделения – средство white-box тестирования для .NET под названием Pex. Разработка доступна для скачивания: http://research.microsoft.com/en-us/downloads/d2279651-851f-4d7a-bf05-16fd7eb26559/.

Конечно же концептуально Pex противоречит некоторым аспектам юнит-тестирования, т.к. проверяет как написан код, а не что он должен делать.

Насколько известно для SharePoint удобно писать лишь интеграционные тесты, которые помогут вам убедится, в том, что решение благополучно “прилунилось” и проверить, что вся необходимая логика исполняется на “живых” объектах SharePoint.

Однако во многих случаях хочется избавиться от всех инфраструктурных накладок интеграционного тестирования (производительность: я не хочу уходить пить кофе каждый раз при запуске тестов, обновление данных и проч.) и проводить полноценное модуль тестирование в изолированной среде, что обычно и позволяют делать многочисленные фреймворки для создания “тестовых двойников”, примеры для .NET:

Список может продолжить Mole, однако это научная разработка, имеющая вполне своеобразный синтаксис и подход.

Пример работы с Mole

Предположим нам необходимо протестировать некоторый участок кода:

public void UpdateTitle(SPItemEventProperties properties) {
    using (SPWeb web = new SPSite(properties.WebUrl).OpenWeb()) {
        SPList list = web.Lists[properties.ListId];
        SPListItem item = list.GetItemById(properties.ListItemId);
        item["Title"] = item["ContentType"];
        item.SystemUpdate(false);
    }
}

Для этого нам необходимо будет произвести ряд действий для генерации тестового проекта, которые я опущу (они подробно описаны в литературе на которую я ссылаюсь). Больше всего интересует код, который будет заниматься э(mole)яцией типов SharePoint:

string url = "http://foo";
// с помощью рефикса MS мы подскажем исполняющей среде, что данный тип э(mole)ирует // SPItemEventProperties и что по вызову свойства WebUrl
// (заметьте что Get свойства опять же опрделен специфичностью синтаксиса) необходимо // будет вернуть заданную нами строку
var properties = new MSPItemEventProperties {
    WebUrlGet = () => url
};

Аналогично и для остальных объектов:

MSPSite.ConstructorString = (site, _url) => {
    new MSPSite(site);
};

new MSPSite(site) {
    OpenWeb = () => new MSPWeb()
};

Так будет выглядеть пример Assert’ов

ListsGet = () => new MSPListCollection {
    ItemGetGuid = id => {
        Assert.IsTrue(listId == id);
        return new MSPList();
    }
}

Думаю данных примеров будет достаточно, для того, чтобы понять, что достойной альтернативы TypeMock Isolator пока нет, однако, с точки зрения сравнения подходов к решению проблемы с промышленной и академической стороны Mole очень интересен.

Материалы

Статья об использовании Mole: http://research.microsoft.com/en-us/projects/stubs/stubstutorial.pdf

Статья о тестировании SharePoint с помощью Mole и Pex: http://research.microsoft.com/en-us/projects/pex/pexsharepoint.pdf