четверг, 27 марта 2014 г.

Процесс разработки ПО. Наглядно.




Как проверить существует ли объект (список, поле и т.п.) на сайте SharePoint

В Sharepoint почему-то все не однозначно устроено на предмет получения информации о наличии того или иного элемента.

Самый очевидный и простой способ - это типы контента. Достаточно сделать вот так:
  var myType = web.AvailableContentTypes["Special Document"];
  if (myType!=null) //тип контента существует

На этом простое заканчивается. О существовании списка уже так не узнаешь. Если списка нет, то подобная конструкция вызовет исключение. И, обертывание доступа к списку в try-catch, в общем-то, единственный способ узнать о том, что списка на сайте нет и не уронить приложение. Так что, рекомендую написать примерно такой метод расширения и использовать при необходимости его.
    public static bool ListExists(this SPWeb web, string listName)
    {
       try
       {
          var list = web.Lists[listName];
          return true;
       }
       catch
       {
           return false;
       }            
    }
Ну а самое интересное с полями. Представьте, что вы создали вот такое поле:


среда, 26 марта 2014 г.

Static в .Net - почему иногда это плохо и как с этим быть?

На так давно я писал про статические конструкторы, а сегодня давайте разберемся что это вообще такое - статические объекты и что значит ключевое слово static. В общем, статический объект - это такой объект который в домене приложения существует в единственно экземпляре. А теперь перейдем к частностям.

Начнем с классов. Если вы объявляете класс статическим, то на него автоматически накладываются несколько ограничений:
1) Статический класс может содержать только статические методы.
2) Статический класс автоматически помечается как sealed, и следовательно от него нельзя наследовать
3) Нельзя создавать экземпляры статических классов.

Исходя из всего этого напрашивается вывод что единственные кандидаты в статические классы - это классы которые не должны хранить состояние. То есть классы, методы которого получают что-то на вход и на базе этих данных выдают результат. Как самый показательный пример - класс Math.
И самый правильный вариант использования начиная с .Net 3.0 - создание классов содержащих методы расширения (extension methods). Их разрешено объявлять только в статических классах.

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


Как развернуть решение на удаленном сервере SharePoint с помощью PowerShell

Я часто сталкиваюсь с ситуацией, когда, вроде бы, вполне вменяемые люди ставят Visual Studio на продакшн-сервер где установлен Sharepoint. И совершенно не для отладки (я говорю именно про вменяемых людей, которые обычно знают о существовании Remote Debug), а исключительно для развертывания решений. Часть из них просто не хочет изучать какой-то еще PowerShell, часть думает, что для того чтобы развернуть решение на удаленной машине нужно устанавливать Sharepoint Designer и делать это с его помощью.... в общем отмазки разные, а результат один и тот же.
На самом же деле абсолютно ничего сложно в развертывании решении с помощью PowerShell нет - достаточно запомнить (или записать) очень простой порядок действий:

1. Скопируйте на сервер файл решения (тот который с расширением wsp).
2. Зайдите на сервер с помощью, например, Remote Desctop и откройте Sharepoint 2010 Management Shell
3. Теперь загрузите файл решения вот этой командой:

Add-SPSolution -LiteralPath [путь к wsp-файлу]
например: PS C:\Users\eugene> Add-SPSolution -LiteralPath C:\Solution\MyWebPart.wsp

4. Теперь установите загруженное решение:

Install-SPSolution -Identity MyWebPart.wsp -GACDeployment -AllWebApplications

В принципе, команда Install-SPSolution должна автоматически активировать фичу, содержащуюся в решении, но если это не произошло то нужен последний шаг:

Enable-SPFeature -Identity "MyWebPart_Feature1" -URL [адрес приложения для которого активировать фичу]

Вот и все, ничего сложного в этом нет. А вот последовательность команд для удаления решения:

Disable-SPFeature -identity "MyWebPart_Feature1" -URL [адрес приложения для которого деактивировать фичу]

Uninstall-SPSolution -Identity MyWebPart.wsp -AllWebApplications

Remove-SPSolution -Identity MyWebPart.wsp


вторник, 25 марта 2014 г.

Шифруем строки соединения с БД в web.confg

Одно из правил "хорошего тона" при созданий приложений с претензией на безопасность и защищенность от взлома - нигде не писать логины и пароли. Особенно пароли к локальным БД в которых, как правило, живут очень-важные-секретные-данные (а вот скажите честно, вы всегда руководствуетесь принципом минимально возможных прав доступа, или честно под sa соединяетесь? ;)


Так вот, в ASP.NET начиная с версии 2.0 существует очень простая возможность прозрачного шифрования строк соединения.  Прежде всего вам необходимо предоставить пользователю, под которым работает IIS, доступ к хранилищу ключей RSA:

aspnet_regiis -pa "NetFrameworkConfigurationKey" "NT Authority\Network Service"

А после этого, для того, чтобы зашифровать содержимое секции достаточно сделать очень простую вещь:
    public void EncryptConnString()
    {  
       Configuration config = WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
       ConfigurationSection section = config.GetSection("connectionStrings");
       if (!section.SectionInformation.IsProtected)
       {
           section.SectionInformation.ProtectSection("RsaProtectedConfigurationProvider");
           config.Save();
       }
    }

И что самое главное, зашифровав секцию таким образом вы все равно сможете ей пользоваться совершенно прозрачно, как будто она и не зашифрована. Единственный минус, работа с зашифрованным секциями несколько медленнее.
   string connString =  WebConfigurationManager.ConnectionStrings["MyConnString"].ConnectionString;


Статические конструкторы в .Net

Многие знают, что в .Net есть такая вещь, как статические конструкторы. И то, что для статических конструкторов CLR гарантирует две вещи:

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

В общем-то из-за второго свойства статические конструкторы обычно используют для создания объектов по шаблону Singleton. Примерно вот так:
    
    public class SampleSingleton
    {
        private static SampleSingleton instance;

        public static SampleSingleton Instance
        {
            get
            {
                return instance;
            }
        }

        private SampleSingleton() { }

        static SampleSingleton()
        {
            instance = new SampleSingleton();
        }

        public void SomeMethod()
        {
            Console.WriteLine("блаблабла");
        }
    }

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


воскресенье, 23 марта 2014 г.

Как удалить элементы из списка.

Представьте себе, что у нас есть список, содержащий какие-то элементы, а нам нужно в какой-то момент удалить часть из них. Какие варианты есть? Самый очевидный для многих вариант - с использованием foreach работать не будет:

            foreach (var item in list)
            {
                if (item % 2 == 0)
                { list.Remove(item); }
            }


Не будет он работать вот почему. foreach компилятором разворачивается в примерно такую конструкцию:
            
            List< t >.Enumerator enumerator = list.GetEnumerator();
            try
            {
               while (enumerator.MoveNext())
               {
                 ....
               }
            }
            finally
            {
               enumerator.Dispose();
            }

А если мы используем что-то типа Resharper и посмотрим на метод MoveNext(), то увидим, что он сравнивает версию коллекции, и если она изменилась, то выбрасывает исключение.
            
            [__DynamicallyInvokable]
            public bool MoveNext()
            {
                List< t > list = this.list;
                if ((this.version == list._version) && (this.index < list._size))
                {
                    this.current = list._items[this.index];
                    this.index++;
                    return true;
                }
                return this.MoveNextRare();
            }

            private bool MoveNextRare()
            {
                if (this.version != this.list._version)
                {
                    ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
                }
                this.index = this.list._size + 1;
                this.current = default(T);
                return false;
            }


Давайте попробуем дрогой вариант - цикл for.


Кое-что про числа в .Net

Посмотрите на этот код. Как думаете какой будет результат?

            Double a = 1;
            Double b = 3;
            Double c = a/b;
            Double d = c * 3;

            Single a3 = 1;
            Single b3 = 3;
            Single c3 = a3 / b3;
            Single d3 = c3 * 3;

            decimal a2 = 1;
            decimal b2 = 3;
            decimal c2 = a2 / b2;
            decimal d2 = c2 * 3;

            Console.WriteLine("Double:");
            Console.WriteLine(c.ToString());
            Console.WriteLine(d.ToString());

            Console.WriteLine();
            Console.WriteLine("Single:");
            Console.WriteLine(c3);
            Console.WriteLine(d3);

            Console.WriteLine();
            Console.WriteLine("Decimal:");
            Console.WriteLine(c2);
            Console.WriteLine(d2);

Не смотря на то, что, казалось бы, все три результата должны быть идентичны, только при использовании типа decimal результат такой же как и при обычном делении/умножении на бумажке. И double и single в результате дадут единицу. Почем так получается, спросите вы? Все из-за того, что single и double содержат два дополнительных разряда в конце, благодаря которым можно получить исходное значение при выполнении арифметических действий в обратном порядке. Увидеть их можно вот так:
            Console.WriteLine("{0:R}", c);

На этом, кстати, интересности относительно single и double не заканчиваются как думаете что будет в результате выполнения вот этого кода?
            Double number = 123;
            Double zero = 0;
            Double result = number / zero;