четверг, 28 марта 2019 г.

Структурная архитектура для MVC 5

Эта статья о реализации архитектуры для MVC 5. Код для этой статьи взят с Github.
Прежде, чем начать разработку системы, нам потребуется некоторое время, чтобы определиться с архитектурой. Причина, по которой я на этом концентрируюсь, заключается в том, что множество разработчиков либо недооценивают архитектуру приложения, либо, если вы начинающий, то большинство вещей еще не знаете.
Архитектура приложения определяет структурную модель системы. Я рекомендую вам потратить некоторое время, чтобы определиться со структурной моделью, ибо в последствие изменение архитектуры влелит вам в копеечку. В целом архитектура приложения означает разделение системы на различные компоненты, которые должны отвечать за выполнение своих функций. Эти элементы взаимодействуют между собой, чтобы обеспечить цельную работу системы.
Итак, я расскажу вам о том, как я проектирую архитектуру решения для своего проекта электронной коммерции ECommecrMVC. И для чего я это делаю.
Некоторые из вас, возможно, будут в недоумении (в основном начинающие), ведь MVC сам по себе представляет прекрасное разделение ответственности, а тогда зачем же нужна еще и архитектура? Ну да, в чем-то я с вами всеми согласен. Я сам разрабатывал свой первый проект MVC без всяких мыслей об архитектуре, но в последствии понял - код должен быть более управляемым и должен быть разделен, согласно своим обязанностям.

В решении ECommerceMVC мне требуются классы предметной области, т есть уровень домена (Domain Layer), сервисы - уровень бизнес-логики (Business Logic Layer) и пользовательский интерфейс UI - уровень представления (Presentation Layer). В ASP.NET MVC, согласно нашим требованиям, мы можем разделить приложение на Модель (Model), Представление (View) и контроллер (Controller), нам не обязательно требуется такая же архитектура, как в Visual Studio по умолчанию, которая генерирует все модели представления и контроллеры в одной корневой папке.

Для ECommerceMVC структура решения состоит из трех проектов. На начальном этапе я ограничусь темя проектами. Я организовал проекты таким образом, чтобы если потребуется переместить созданный ранее код в другой проект, я бы смог переместить его туда без дополнительных затрат.
Итак, три проекта:
  • Core Project (Class library): Библиотека классов, в структурированном виде реализующая все классы сущностей, моделей предметной области, бизнес-логику и файлы относящиеся к базам данных. One can also divide this into separate, но лично я предпочитаю содержать их в одном проекте, и перемещать их позже по необходимости. Я покажу вам это ниже в этой статье

  • Web Project (MVC Project): Это будет нашим главным проектом для запуска. Он содержит модели (для ViewModels), представления и контроллеры, а также прочие файлы, связанные с UI.

  • Test Project (Unit Test Project): Этот проект содержит модульные тесты.

Все эти проекты созданы и добавлены в пустое решение. Решение имеет следующий вид:



Добавление к имени проекта префикса такого как ECommerce позволит мне отличать один проект от другого. На рисунке выше представлено три проекта.

  • ECommerce (class library): В этом проекте я создал еще три папки. Как я уже говорил, изначально все уровни бизнес логики, уровень доступа к данным DAL, классы сущностей я создаю в данном проекте и затем по необходимости перемещаю их в другие проекты. Когда в VS мы создаем новый класс, его пространство имен автоматически включает путь к папке, где находится файл класса. Так, если я создам класс Entity.cs в папке Data, его пространство имен будет Ecommerce.Data. Структура такого вида позволяет мне переместить класс Entity.cs в проект с названием Ecommerce.Data, и никакой лишней работы делать не надо.

  • ECommerce.Web (MVC Project): Самый обычный проект MVC, который создает Visual Studio.

  • ECommerce.Test (Unit Test Project): Здесь находятся модульные тесты.

Рассмотрим каждый проект в отдельности.

1) ECommerce (Class Library):
Библиотека классов (ECommerce) имеет три папки Data, Services и Common.
В папка Data будут содержаться все классы сущностей. В папке Services будут содержаться все классы доступа к данным DAL и классы бизнес логики. В папке Common будут храниться классы общие для всех проектов.

Почему я не используею паттерн Repository?
Отвечаю: Потому что я использую Entity Framework.

Множество разработчиков использует паттерн Repository вместе с Entity Framework. Но я являюсь исключением наряду с многими другими.
Паттерн Repository является одним из самых популярных в DDD. Он скрывает реализацию логики доступа к данным. Неплохо было бы добавить в проект такую классную вещь. Но ведь Entity Framework (далее EF по тексту) делает то же самое.
EF - это ORM, избавляющая вас от написания кода доступа к данным.
Я лично не думаю, что для EF нужна обертка в виде Repository, это напоминает абстракцию абстракции. Entity Framework сам реализует паттерн Repository. Repository реализует связку паттернов паттерн "Единица работы" Unit of Work (UoW) & репозиторий, то же сделано в EF, он имеет DbContext (UoW) & DbSet (Repository), так что нет необходимости реализовывать еще один уровень поверх EF. А если мы сделаем обертку Repository поверх EF, то потеряем множество преимуществ EF. Вы можете узнать больше здесь.

Примечание: Нет ничего предосудительного в паттерне Repository, и вы можете использовать его по своему желанию.

Рассмотреим бибилиотеку классов более детально.

a) Data:

Здесь будут созданы все классы сущностей. Эта папка будет также содержать подпапки для организации сущности.
Добавим класс ProductEntity в папку Product.


В приведенном выше коде атрибуты Key (соответствует первичный ключ в таблице БД), Required (обязательное поле в форме представления),ScaffoldingColumn (отображать свойство в представлении или нет) и т.д. специфицированы, как аннотации данных в
System.ComponentModel.DataAnnotations.

Эти аннотации данных используются, как дляподдержки представления, так для и генерирования таблицы в БД.

b) Сервисы:
В этой папке будут находиться все классы бизнес-логики наряду с DbContext, и как уже говорилось, нет никакой необходимости для еще одного уровня над Entity Framework. Мне больше нравится писать сервисы, которые непосредственно возвращают значения, чем просто queryable.
Добавляя DbContext, имейте также в виду, что он является конкретной реализацией и для тестирования на DbContext потребуется создать mock-объект
Чтобы этого достичь, я внес некоторые изменения, как это описано в статье из MSDN Testing with your own doubles (EF6 onwards) для DBContext. Я установил Entity Framework 6 для проекта ECommerce через NuGet package mangager. Я буду использовать подход EF Code-First. Класс DBContext выглядит так:


ОБНОВЛЕНИЕ: Я обновил DBContext и далее создам mock-объект для его тестирования.
Проблема с вышеприведенным подходом заключается в том, что я использую фейковый класс DBContext, а это значит, что мне придется поддерживать два класса DBContext: один реальный, а второй для тестирования проекта. Чтобы этого избежать, я собираюсь создать mock-объект для DBContext из проекта ECommerce и использовать его для тестирования, не создавая отельного класса-заглушки. (как предложено egenesis on reddit)
I’m going to create generic DbContext, as I’ll be using it directly in services. Прежде, чем заняться конкретной реализацией, я для DBContext создал инерфейс IDBContext.


А вот конкретная реализация этого интерфейса:


Из этой реализации видно, что я еще и переопределил метод SaveChanges. Я буду вызывать этот метод больше в контроллере, чем в сервисах.
DBConString представляет собой строку соединения в файле app.config проекта библиотеки. Разрешив миграцию, я смогу создавать из классов сущностей таблицы БД прописанной в DBConString.

Добавим простой класс ProductServices в папку Product.
Приведенный выше код смотрится неплохо, но имеется проблема с его конкретной реализацией, хорошо бы для этих классов создать интерфейы. Чтобы создать интерфейс к классу, нажмите правой кнопкой на имени класса > Quick Action -> Extract Interface. После этого будет создан вот такой интерфейс. (Класс выше станет автоматической реализацией этого интерфейса.)
ОБНОВЛЕНИЕ: Я обновил класс ProductServices так, чтобы он использовал DbContext.
Для задействования возможностей нового DBContext, я еще немного подкорректировал класс ProductServices.
Теперь он выглядит так:


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

c) Common: В этой папке будет содержаться вспомогательный код код или общий код используемый во всех проектах.

2) ECommerce.Web(MVC Project):
Данный проект будет нашим главным проектом для запуска (Startup Project в Visual Studio).
Он содержит в корне три папки Controllers, Models и Views. As I have moved the business logic layer and data access layer to another layer, the Model folder will only contain ViewModels.
В этом проекте я через Reference Manger добафил референс на библиотеку ECommerce как показано ниже.


Для класса ProductEntity создадим контроллер и представление, используя возможности скаффолдинга. Кликните правой кнопкой на папке Controller и выберете New Scaffolded Item.


Выберите MVC 5 Controller with Views, using Entity Framework. Будут сгенерированы контроллер и Views для класса ProductEntity.



After VS does Scaffolding the solution structure will look like this for ECommerce.Web.


Для проекта ECommerceMVC, есть ряд требований требований к Панели администратора.
Если создать контроллер и представления админской панели проект ECommerce.Web наряду с Front End UI, это сделает это сделает код более неуправляемым.
Для этих целей можно создать, скажем, проект ECommerce.Admin, но мне не требуется два проекта MVC и do all the configuration stuff which I already did for ECommerce.Web.
Для таких ситуаций ASP.NET MVC предоставляет особенность, называемую области (Areas). С ее использованием я могу создать новую область, где я размещу весть код, касающийся панели администратора.
Создать область в проекте ECommerce.Web довольно простоe, надо правой кнопкой мыши кликнуть на имени проекта и выбрать Areas, после чего в нашем случае назвать область “Admin“. В проекте ECommerce.Web будет создана новая папка “Areas”.
Структура Areas папки выглядит так:

The areas creates MVC sub-project within Main MVC Project. Above diagram shows MVC structure for Area Admin, where I will write code for admin panel.
The routes for this area can be customized in AdminAreaRegistration.cs file.
To register routes for areas, I added following code to the Global.asax file in Application_Start method.
Я также добавил Connection String из app.config библиотеки классов Library project в Web.Config нашего MVC проекта.

This will be structure for ECommerce.Web для проекта ECommerceMVC.

3) ECommerce.Test (Unit Test Project):
Данный проект создается через шаблон проекта Unit Test вдиалоговом окне New Project Window.
Лично я предпочитаю в начале создавать тесты для каждого сервиса, чтобы убедиться в корректной реализации бизнес-логики.
Прежде чем начать писать тесты, я добавил к проекту следующие ссылки.
  1. Референс на библиотеку (ECommerce)
  2. Референс на проект MVC (ECommerce.Web)
  3. Entity Framework

ОБНОВЛЕНИЕ: Такой подход не рекомендуется.

Для сервисов нам потребуется реализовать фейковый DBContext, который будет использован как mock-объект для DBContext, используемого в сервисах. Чтобы этого добиться, я добавил еще два класса TestContext и TestDbSet взято отсюда. Добавим простой модульный тест для ProductService и назовем его TestProductService.cs.



ОБНОВЛЕНИЕ: Для тестирования нашего нового обобщенного DbContext сделаны следующие изменения.
А теперь изменился тест для ProductServices to mock DBContext через Moq.

Этот тест вызывает метод GetProductByCode класса ProductServices. Метод возвратит объект ProductEntity, совпадающий с тем, что мы передали в параметре метода.


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


среда, 6 марта 2019 г.

JavaScript - playing with Canvas. Cellular Automata. Клеточный автомат, играемся с Canvas.

Пример двухмерного клеточного автомата из 6 состояний (0-5) и с начальным состоянием "1" в топ-центре сетки. Каждому состоянию сопоставляется цвет (0 - чёрный, 1 - красный, 2 - синий, 3 - зеленый, 4 - желтый, 5 - белый). В пустой сетке клетки заполняются состоянием "0". Правила переходов (кодовое число) генерируются случайным образом.
Поиграться можно здесь: https://jsfiddle.net/mishau/tja1dpy8/129/

Клеточный автомат

Кодовое число:

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

Рефакторинг говнокода на Observer

Изначальная реализация паттерна проектирования "Наблюдатель" (Observer) для погоды

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
 
namespace lb2
{
 class Program
 {
 public static void Main()
 {
 WeatherData weatherData = new WeatherData();
 CarrentConditionDisplay carrentDisplay = new CarrentConditionDisplay(weatherData);
 //...
 weatherData.setMeasurements(80, 65, 30.4);
 weatherData.setMeasurements(82, 70, 29.2);
 weatherData.setMeasurements(78, 90, 29.2);
 
 Console.Read();
 }
 
 }
 public interface Subject
 {
 public void registerObserver(Observer 0);
 public void removeObserver(Observer 0);
 public void notifyObservers();
 }
 public interface Observer
 {
 public void update(float temp, float hum, float press);
 }
 public interface DisplayElement
 {
 public void display();
 }
 
 public class WeatherData : Subject
 {
// private string SubjectState { get; set; }
 private ArrayList observers;
 
 private float temp;
 private float hum;
 private float pressure;
 
 public WeatherData()
 {
 observers = new ArrayList ();
 }
 public void registerObserver(Observer 0)
 {
 observers.Add(0);
 }
 public  void removeObserver(Observer 0)
 {
 int i = observers.indexOF(0);
 if (i>=0) observers.remove(i);
 }
 public void notifyObservers()
 {
 foreach (Observer obs in Observers)
 Obs.opdate(temp, hum, pressure);
 }
 public  void measurementsChanges()
 {
 notifyObservers();
 }
 public  void SetMeasurements(float temp, float hum, float press);
 {
 this.temp = temp;
 this.hum = hum; 
 this.pressure = press;
 measurementsChanges();
 }
//реализовать другие методы класса
 
public class CarrentConditionDisplay:Observer, DisplayElement
 {
 private float temper;
 private float humid;
 private Subject weatherData;
 
 public CarrentConditionDisplay(Subject wData)
 {
 this.weatherData = wData;
 wData.registerObserver(this);
 }
 
 public void update(float temp, float hum, float press)
 {
 this.temper = temp;
 this.humid = hum;
 Display();
 }
 public void Display()
 {
 System.Console.WriteLine("Текущие условия" + temper + "С и " + humid + "% влажности");
 }
 }
}

пятница, 28 декабря 2018 г.

Остаток от деления без % в C#. Анализ алгоритмов.

Итак, на собеседовании по C# время от времени попадается задача нахождения остатка от деления (число R) целочисленных положительных чисел M и N без применения оператора %;

Алгоритмы

Первое, что приходит в голову, это следующая формула:

    R = M-M/N*N

Второе, что приходит в голову, это стандартная функция в лоб:

i = 0;
do {
i++;
r = m-n*i;

} while (r>=n)
return n;

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

i = 1;
k = 0;


while (true)
{ r = m - (k + i) * n;

if (r >= 0 && r < n)
{
return r;
}

if (r > 0)
{
i *= 2;
}
else
{
k += (i / 2);
i = 1;
}
}

Исходные коды методов для "стандартного" и "интеллектуального" алгоритмов:

Стандартный
        static int StdReminder(int m, int n)
        {
            if (m < n)
            {
                return m;
            }

            var i = 0;
            var r = 0;
            do 
            {
                i++;
                r = m - i * n;
            } while (r >= n)
            return r;
        }
Интеллектуальный
        static int IntelReminder(int m, int n)
        {

            if (m < n)
            {
                return m;
            }

            var i = 1;
            var k = 0;


            while (true)
            {
                var r = m - (k + i) * n;

                if (r >= 0 && r < n)
                {
                    return r;
                }

                if (r > 0)
                {
                    i *= 2;
                }
                else
                {
                    k += (i / 2);
                    i = 1;
                }
            }

       }

Код для тестирования времени выполнения:

        static void Main(string[] args)
        {
            var rnd = new Random();

            var m = rnd.Next(3, 100000000);
            var n = rnd.Next(1,  19);

            var sw = Stopwatch.StartNew();
            var r = m % n;
            sw.Stop();
            Console.Write("Operator M % N:     ");
            Console.WriteLine(m + " % " + n + " = " + r + "; Time: " + sw.Elapsed.ToString());

            sw = Stopwatch.StartNew();
            r = m - m/n*n;
            sw.Stop();
            Console.Write("Operator M - M/N*N: ");
            Console.WriteLine(m + " % " + n + " = " + r + "; Time: " + sw.Elapsed.ToString());


            sw = Stopwatch.StartNew();
            r = StdReminder(m, n);
            sw.Stop();
            Console.Write("Standard Func:      ");
            Console.WriteLine(m + " % " + n + " = " + r + "; Time: " + sw.Elapsed.ToString());


            sw = Stopwatch.StartNew();
            r = IntelReminder(m, n);
            sw.Stop();
            Console.Write("Intellectual Func:  ");
            Console.WriteLine(m + " % " + n + " = " + r + "; Time: " + sw.Elapsed.ToString());

            Console.ReadKey();
        }

Анализ результатов

Вот, что у меня получилось в консоли

Operator M % N: 17485844 % 13 = 12; Time: 00:00:00.0000009
Operator M - M/N*N: 17485844 % 13 = 12; Time: 00:00:00.0000003
Standard Func: 17485844 % 13 = 12; Time: 00:00:00.0037792
Intellectual Func: 17485844 % 13 = 12; Time: 00:00:00.0001679

Выводы:

- Самым быстрым оказался алгоритм M-M/N*N. Он работает в три раза быстрее, чем стандартный оператор %;
- Самый худший - это "стандартный" алгоритм, он дает существенно большее время, чем все прочие.
- "Интеллектуальный" алгоритм работает примерно в 22 раза быстрее, чем "стандартный" и в 560 раз медленнее, чем формула M - M/N*N.

вторник, 20 июня 2017 г.

Прощай Codeplex!

Codeplex закрывается. :(

Прошло почти 11 лет с момента создания CodePlex, и теперь настало время сказать "прощай". CodePlex старотовал в 2006 году потому что мы, разработчики, как и все остальные в нашей индустрии, нуждались в хорошей площадке, чтобы делиться софтом.

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

Сейчас отключена возможность создавать на CodePlex новые проекты. В октябре CodePlex заработает в режиме только-чтение, прежде чем окончательно отключится 15 декабря 2017 года.

Подробнее узнать об этом можно здесь

пятница, 14 октября 2016 г.

JavaScript ES5. Пространство имен, наследование, защита от дублирования.

Защита от дублирования скриптов

Следующая функция предотвращает повторную загрузку файла JavaScript

  function scriptLoader(src, callback) {
     "use strict";
      var script = document.querySelector('script[src="'+src+'"]');
      if (script == null) {    
        var head= document.getElementsByTagName('head')[0];
        script= document.createElement('script');
        script.type= 'text/javascript';
        script.src= src;
        script.async = false;
        script.onreadystatechange= function () {
           if (this.readyState == 'complete') callback();
        }
        head.appendChild(script);
      }
      if (script.addEventListener) {
        script.addEventListener("load", callback); 
      } else if (script.attacheEvent) {
        script.addEventListener("onload", callback); 
      } 
  }

Предположим у нас есть следующий скрипт файле alerter.js:

  var Alerter = function() {};
  Alerter.prototype.say = function(smth) { alert(smth); };

Пример использования функции для загрузки alerter.js

  scriptLoader("alerter.js", function() {
    var a = new Alerter();
    a.say("Hi");
  });

  scriptLoader("alerter.js", function() {
    var a = new Alerter();
    a.say("Hi again");
  });

В результате функция выведет в диалоговом окне сначала Hi, затем - Hi again

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

Предотвращение повторного использования переменных

Для предотвращения повторного использования переменных и ухода от глобального контекста код лучше оборачивать в (function() { //код })();
Например следующий код:

<script>
  var a = 1;
  var v = 2;
</script>

Лучше переписать как:

<script>
(function() {
  var a = 1;
  var v = 2;
})();
</script>

Защита приватных полей

Для защиты приватных полей от внешнего доступа целесообразно использовать конструктор с замыканием. Примерно так:

var MyClass = function() {
  "use strict";
  var _name; // приватная переменная, захватывается функциями getName и setName
  this.getName = function() { return _name}
  this.setName = function(value) {
    _name = value;
  }
 
}

Пример выполнения:

var myObj = new MyClass();
myObj.setName("This is the name");
console.log(myObj.getName()); // Выведет This is the name

Пространство имен

Следующая функция реализует пространство имен из строки, где имена разделены точками. Она возвращает пустой объект

function nameSpace(namespace)
{
  "use strict";
  var parts = namespace.split(".");
  var obj = window;
  for (var i = 0; i < parts.length; ++i) {
    obj[parts[i]] = obj[parts[i]] || {};
    obj = obj[parts[i]];
  };
  return obj;
};

Пример запуска:

   nameSpace("Compsphere.Demo");
   console.log(Compsphere.Demo); //Выводит в консоль Compsphere.Demo

Можно ее использовать и так:

   var obj = nameSpace("Compsphere.Demo");
   obj.NewClass = function() { this.x = "Hi!" }
   console.log((new Compsphere.Demo.NewClass).x); //Выводит в консоль Hi!

Генератор классов с пространством имен

Функцию nameSpace можно модифицировать, превратив ее в генератор классов в некоем пространстве имен.
Вот ее вид:
function createClass(_namespace, _constructor, _prototype) {
  var parts = _namespace.split(".");
  var obj = window;
  for (var i = 0; i < parts.length-1; ++i) {
    obj[parts[i]] = obj[parts[i]] || {};
    obj = obj[parts[i]];
  };

  if (typeof _constructor == "function") {
    obj[parts[i]] = _constructor;
    if (typeof _prototype != "undefined") {
      obj[parts[i]].prototype = _prototype;
    }
  } 
  else {
    obj[parts[i]] = obj[parts[i]] || {};
  }
  return obj[parts[i]];
};

На вход подается пространство имен, функция - конструктор класса и необязательный объект для прототипа. Если конструктор и объект для прототипа не переданы, то createClass отработает аналогично вышеописанной nameSpace. Если не передан только объект для прототипа, то прототип не создастся.

Примеры использования

(function() {
  var myclass = createClass(
    "Compsphere.Demo", 
     function(name) { alert(name) }, 
     {say: function(word) { alert(word); } } 
   ); 

  myclass.prototype.sayAgain = function(word) { alert(word) };
  (new Compsphere.Demo("constructor")).say("Hi");  //Выведет фразу Hi

  var myClassInstance = (new myclass("this is the constructor")); //Выведет фразу this is the constructor
  myClassInstance.say("'say' method"); //Выведет фразу this is the constructor 'say' method
  myClassInstance.sayAgain("'sayAgain' method"); //Выведет фразу 'sayAgain' method
})();

пятница, 22 июля 2016 г.

Будущее и настоящее разработки на SharePoint - это чистый JavaScript

Мы занимаемся разработкой на SharePoint для интрасети наших заказчиков и по мере расширения нашей команды, проводим собеседования разработчиков SharePoint developers. В настоящее время, историческому разработчику SharePoint, который работает с C#, ASP.NET, фермерскими решениями, и т.п., приходят на смену разработчики, которые видят SharePoint и Office 365, просто как еще одну платформу JavaScript для веб. Когда мы собеседуем разработчиков SharePoint, мы спрашиваем их об их опыте работы с фреймворками JavaScript, стилями CSS styling, модульным дизайном MVVM и работе с JSOM, REST, и т.п. В данный момент мы нанимаем людей, у которых есть опыт работы с CSS, HTML и фреймворками JavaScript, такими как Knockout JS, Angular, React, и т.п., так как это является видом кастомизации, который мы осуществляем. В последнее время мы не занимаемся активной разработкой на C#, за исключением тех клиентов, которые все еще используют предыдущие версии SharePoint и хотят мигрировать на SharePoint 2016, но сохранив свои существующие фермерские решения, свои рабочие процессы, формы InfoPath, и т.д. Microsoft стимулирует (такой) переход от разработки на серверной стороне к разработке на клиентской стороне уже как несколько лет, начиная с SharePoint 2010 и значительно форсированы с Office 365. Когда Office 365 исключил фермерские решения и серверный код, разработчики перешли на 100% клиентскую разработку с применением JavaScript, HTML, CSS, и т.д. В SharePoint 2016 вы пока еще можете использовать C# или ASP.NET для создания фермерских решений, но главным образом это оставлено для обратной совместимости. Чтобы решение поддерживало, как Office 365, так и SharePoint 2016 рекомендуется использовать кастомизацию только на клиентской стороне.

В выходящем в ближайшее время SharePoint Framework, Microsoft даже еще глубже продвигается в мир JavaScript. Исчезнувшие веб-части / апп-части / и расширения типа контейнеров компонентов IFrame, уступили место встроенным Add-in расширениям JavaScript, которые динамически подключаются к странице. Эти расширения JavaScript могут быть либо развернуты в Office 365, либо загружены из внешней CDN. Фреймворк SharePoint использует 100% кода JavaScript и такие инструменты, как TypeScript, Node.JS, React, JSON, и Gulp для построения кастомизаций. В отличие от текущей модели Add-in, код включается непосредственно в веб-страницу без каких-либо старых контейнеров, базирующихся на IFrame, которые используются в более старых версиях SharePoint.

Если вы являетесь одним из разработчиков SharePoint/C, которые по-прежнему думают, что JavaScript это язык второго сорта, подумайте еще – он должен восприниматься, как первоклассный язык. Microsoft его усиленно продвигает, и разработка на SharePoint, сейчас в основном, является разработкой на клиентской стороне и будет продолжать оставаться таковой в будущем.


Перевод статьи For SharePoint Development, the Future (and Present) is Clearly JavaScript

четверг, 30 июня 2016 г.

Печальное известие

7 июня в Нижнекамске в автокатастрофе в возрасте 34 лет трагически погиб мой бывший коллега по Softline, ближайший сосед в офисе по креслу, руководитель отдела региональных продаж Департамента решений Microsoft Александр Громыко.

Саня всегда был очень веселым парнем.

Буду помнить...

воскресенье, 17 апреля 2016 г.

Singleton в C#

В этом посте мы рассмотрим следующие вещи :

  • Что такое паттерн проектирования Singleton (одиночка) ?
  • Как запрограммировать "одиночку" в C# ?
  • Эффекты многопоточности на паттерне Singleton
  • Реализация двойной блокировки при программировании "одиночки"
  • Раннее создание экземпляра "одиночки"
  • Полноценное "ленивое" создание "одиночки"
  • Программирование "одиночки" с применением обобщений С#
  • Создание одиночки через Lazy<T>
  • Пути взлома "одиночки"
  • Использование "одиночки"
  • Singleton как антипаттерн
  • Различия между статическим классом и паттерном Singleton
  • Заключение

Паттерн проектирования Singleton:

Шаблон (паттерн) проектирования Singleton гарантирует единственность экземпляра класса и предоставляет глобальный доступ к его свойставм и методам.

Стандартная реализация шаблона Singleton

Чтобы создать ровно один экземпляр "одиночки", мы делаем конструктор приватным. Теперь мы можем инстанцировать класс только внутри самого класса.
Мы создаем статическое поле, которое будет содержать созданный экземпляр класса.
Затем мы создаем статический метод, который представляет экземпляр класса для внешнего доступа. В методе проверяется существует ли экземпляр класса, и если нет, то создает и возвращает его, а в противном случае просто возвращает созданный ранее объект.

 private static Singleton _instance;
 
 private Singleton() {}
 
 public static Singleton GetInstance()
 {

  if(_instance == null)
                {
   _instance = new Singleton();
  }
  return _instance;

 }

Проблемы со стандартной реализацией

Стандартная реализация прекрасно работает в однопотоковой среде. В многопотоковой среде метод GetInstance() даст сбой :

public static Singleton GetInstance()
{

   if(_instance == null)
   {
      _instance = new Singleton();

   }
  return _instance;
}
Если два потока вызовут условие в операторе "if" в один и тот же момент времени, то будет создано два экземпляра класса Singleton.

Решение проблемы многопоточности в стандартной реализации одиночки

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

  public sealed class Singleton
    {
        private static Singleton _instance = null;
        private static readonly object _lock = new object();
 
        private Singleton() {}
 
        public static Singleton GetInstance
        {
            get
            {
                lock (_lock)
                {
                    if (_instance == null)
                    {
                        _instance = new Singleton();
                    }
                    return _instance;
                }
            }
        }
}
Такой код решает проблему. Но это замедляет работу, потому что все прочие потоки ожидают завершения блокировки. Для этого мы используем следующие подходы :

1 : Двойная проверка при блокировке

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

 public sealed class Singleton
    {
        private static Singleton _instance = null;
        private static readonly object _lock = new object();
 
        private Singleton() {}
 
        public static Singleton GetInstance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_lock)
                    {
                        if (_instance == null)
                        {
                            _instance = new Singleton();
                        }
 
                    }
                }
                return _instance;
            }
        }
   }
Примечание. Подобный вопрос попался мне на экзамене по С# на сертификат Microsoft.

2 : Заранее созданный экземпляр

Здесь мы создаем экземпляр "одиночки" во время загрузки класса. Потокобезопасность осуществляется в этом случае без блокирования кода оператором lock.

public class Singleton 
{
 //создание экземпляра немедленно
 private static Singleton _instance = new Singleton();
 
 private Singleton() {}
 
 public static Singleton Default
 {
     get { return _instance; } //просто возвращаем экземпляр
 }

}

3 : Полноценное отложенное создание экземпляра

В этом примере инстанцирование происходит при первом обращении к статическому свойству Default класса Singleton.

 public sealed class Singleton
  {
    private Singleton()
    {
    }
 
    public static Singleton Default { get { return GetInstance._instance; } }
        
    private class GetInstance
    {
        // Явный статический конструктор сообщит компилятору
        // не инициализировать поле заранее
        // и поле будет инициализировано при первом к нему обращении 
        static GetInstance()
        {
        }
 
        internal static readonly Singleton _instance = new Singleton();
    }
 }
 

4 : Реализация паттерна Singleton с применением обобщений

Здесь будет создан единственный экземпляр и загрузка эффективно будет отложена, потому что конструктор не вызовется пока не будет вызван Build().

 public class Singleton<T> where T : new()
    {
        private static T _instance = new T();
 
        public T Default()
        {
            get { return _instance; }
        }
    }
 
...
    Singleton<SimpleType> instance = new Singleton<SimpleType>();
    SimpleType simple = instance.Default;

5 : Применение Lazy<T> type

Передавая в конструктор Lazy делегат (или, что проще, лямбда-выражение), в котором вызывается конструктор одиночки, мы получаем отложенное создание экземпляра.

public sealed class Singleton
{
    private static readonly Lazy<Singleton> _lazy =
        new Lazy<Singleton>(() => new Singleton());
    
    public static Singleton Default{ get { return _lazy.Value; } }
 
    private Singleton()
    {
    }
}

Обходные пути для взлома Singleton - вопросы, которые задают на собеседовании :

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

– клонирование
– рефлексия
– использование класса singleton в качестве подкласса

Как избежать создания экземпляра Singleton через клонирование объекта ?

Через метод clone() можно создать копию объекта.

Чтобы предотвратить создание клонов экземпляра "одиночки", необходимо сделать следующее :

– Реализовать метод MethodwiseClone()
– Переопределить метод clone() и возбудить оттуда исключение CloneNotSupportedException.
 protected object MemberwiseClone()
    {
        throw new Exception("Cloning a singleton object is not allowed");
    }

 

Как предотвратить создание экземпляра Singleton через рефлексию ?

Чтобы предотвратить создание экземпляра через рефлексию, надо возбудить исключение из конструктора, если экземпляр уже имеется.

 private Singleton()
    {
        if (_instance != null)
        {
            throw new Exception("Cannot create singleton instance through reflection");
        }
    }

 

Как предотвратить создания экземпляра "одиночки" в качестве подкласса ?

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

Для чего используется паттерн проектиорвания Singleton :

Логирование :

Шаблон Singleton хорошо подходит для реализации протоколирование, если мы планируем единственный файл лога для всех сообщений.

Кеширование:

Шаблон Singleton может быть использован в реализации механизма кеширования для предоставления глобального доступа к кешу.

Почему "одиночка" считается антипаттерном?

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

Отличия "одиночки" от статического класса :

– В C# в статическом классе нельзя реализовать интерфейс. В случае, если "одиночка" должен реализовать интерфейс, например для использования в IoC, вы можете не использовать статический класс
– Singleton можно клонировать, объект статического класса клонировать нельзя
– Объект Singleton хранится в куче, статический - в стеке
– Можно реализовать отложенную или асинхронную инициализацию "одиночки", а статический класс инициализируется при первой загрузке.

Заключение:

Итак, мы обусдили:
  • Различные способы программирования синглтона в C#
  • Использование паттерна синглтона
  • Почему синглтон является антипаттерном?
  • Различия между статическим классовм и синглтоном

воскресенье, 3 апреля 2016 г.

Распределение ролей в команде разработчиков SharePoint

При работе над проектами SharePoint, в команде разработчиков целесообразно распределить следующие роли:
  1. Руководитель проекта: Ведет проект до успешного внедрения посредством зарекомендовавших себя методологий.
  2. Архитектор: Технический специалист, который отвечает за концептуальную разработку решения в целом, взаимодействуя с остальными командными игроками.
  3. Инфраструктурщик и/или
  4. Разработчик: Расширяет возможности SharePoint посредством кодирования или встраиванием скриптов / кода наподобие кастомизации (JavaScript, XML, и т.п)
  5. Конфигураст: Его главной задачей является конфигурирование SharePoint и выкладывание страниц без написания кода. Разумеется, вы как разработчик или инфраструктурщик можете все это сделать и сами, но стоит ли это средств, потраченных на оплату вашего труда?
  6. Дизайнер/Информационный архитектор: На больших проектах могут быть заняты занимаются два разных специалиста. На средних проектах один человек может успешно справляться с двумя ролями, тем более после надлежащего обучения. Дизайнер UX представляет решение с точки зрения пользователя, разбираясь в том, как пользователи будут работать с сайтом, а затем проектируя навигацию, переходы между страницами, разметку страниц. Информационный архитектор анализирует суть информации, которую вы собираетесь хранить на данный момент или в будущем, а также предлагает к рассмотрению информацию, которую вы не планировали пока хранить, а затем разрабатывает такие вещи, как типы контента, метаданные, поля, значения, контролы и т.п. для структурирования и управления такой информацией.
  7. Производитель контента: Известные последние слова множества неудачных проектов на SharePoint звучат примерно так: “Не беспокойтесь, контент находится под контролем.” Хороший проект на SharePoint требует, чтобы кто-то отвечал за то, что полезный контент был своевременно создан и добавлен. Заметьте: лучшие решения требуют исполнения этой роли от многих людей.

суббота, 2 апреля 2016 г.

Структура решения SharePoint в Visual Studio

Одним из способов успешного создания решения SharePoint является разбиение решения на отдельные зависимые проекты:

  • Branding
    В данном проекте находятся все файлы, относящиеся к UI - пользовательскому интерфейсу, в первую очередь изображения и стили css. С этим проектом в основном имеет дело разработчик UI/UX. Как правило в проекте не должно содержаться ни одной строчки кода .NET, что означает отсутствие сборок для развертывания.
  • Presentation
    В этом проекте должны находиться веб-части, мастер-страницы, страницы приложения (application pages), элементы управления, и т.п. Это в первую очередь компоненты, которые могут быть исплозованы в разных частях решения. Проект не должен явно зависеть от проекта Branding.
  • Definition
    Этот проект содержит типы, столбцы сайта, типы полей, определения списков (list definitions) и сайтов (site definitions), а также шаблоны web. Проект имеет зависимость от проектов Presentation и Branding
  • Core
    Этот проект содержит, как правило, единственную сборку, которая представляет главную функциональность, используемую другими проектами. Это можно воспринимать, как бибилиотеку утилит или класс бизнес-логики. В большинстве случаев, проект не должен содержать никаких артефактов SharePoint. Исключение могут составлять файлы JavaScript, которые выступают как утилиты.
  • Resources
    Здесь содержатся все файлы языковых ресурсов, используемых остальными проектами. Оформив их в виде отдельного проекта, вы упростите перевод и организацию ресурсов для всего решения.
  • Others
    Прочие проекты, в зависимости от типа решения. Предположим, проект для рабочих процессов (workflows).

Как вы возможно заметили, при разработке решения в виде подобной структуры, могут возникнуть проблемы среди разработчиков, работающих на одном проекте. В первую очередь это касается добавления и удаления файлов, потому что при этом происходит обновления файла проекта csproj. Здесь на помощь придет система контроля версий TFS и т.п., что поможет наладить необходимый мерджинг.

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

Создание нескольких WSP для одного решения также потребует некоторой стандартизации развертывания, позволяющей проконтролировать пререквизиты при установке решения. Имеется множество скриптов для развертывания, доступных на CodePlex. Если вам интересно, гляньте проект "SharePoint Solution Deployer" по ссылке http://spsd.codeplex.com

среда, 17 февраля 2016 г.

Что исчезнет в .NET Core


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

  • System.Data. Хоть поддержка на базовом уровне и есть в .NET Core, такая как модель провайдеров и SQL client, кое-что сейчас недоступно, например поддержка схем и типов DataTable/DataSet.
  • System.DirectoryServices. Сейчас в .NET Core нет поддержки для LDAP или Active Directory.
  • System.Drawing. Хотя, строго говоря, System.Drawing является клиентским API, многие разработчики используют этот API на серверах для генерации иконок или водяных знаков. Но теперь это отсутствует в .NET Core.
  • System.Transactions. Мы знаем, что ADO.NET поддерживает транзакции, однако в Core нет поддержки распределенных транзакций.
  • System.Xml.Xsl и System.Xml.Schema. В .NET Core поддерживается XmlDocument и Linq-To-XML (XDocument), включая XPath. Тем не менее, отсутствует поддержка для XSD (XmlSchema) или XSLT (XslTransform).
  • System.Net.Mail. Не имеется поддержка отправки электронной почты из .NET Core с применением данных API.
  • System.IO.Ports. Поддержка работы с серийным портом отсутствует в .NET Core.
  • System.Workflow. Windows Workflow Foundation (WF) не доступен в .NET Core.
  • System.Xaml. Создавая приложения UWP, используют API WinRT XAML. Однако, в текущий вариант .NET Core не включен управляемый каркас XAML, у которого есть возможность обрабатывать документы XAML и создавать графы описанных в них объектов.

понедельник, 8 февраля 2016 г.

SPList Items и GetItems

Продолжаем исследовать исходный код некоторых компонент SharePoint. В этот раз посмотрим различия между свойством SPList.Items и методом SPList.GetItems()
// Microsoft.SharePoint.SPList
public SPListItemCollection GetItems(params string[] fields)
{
 if (fields == null)
 {
  throw new System.ArgumentNullException("fields");
 }
 System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder();
 for (int i = 0; i < fields.Length; i++)
 {
  string text = fields[i];
  if (text != null)
  {
   stringBuilder.Append("");
  }
 }
 return this.GetItems(new SPQuery
 {
  ViewAttributes = "Scope=\"Recursive\"",
  ViewFields = stringBuilder.ToString()
 });
}

// Microsoft.SharePoint.SPList
/// Returns a collection of items from the list based on the specified query.
/// An  object that represents the items.
/// An  object that contains the query.
[ClientCallable]
public SPListItemCollection GetItems([ClientCallableConstraint(Type = ClientCallableConstraintType.NotNull)] SPQuery query)
{
 return new SPListItemCollection(this, query);
}

// Microsoft.SharePoint.SPList
/// Gets the number of items in the list.
/// A 32-bit integer that indicates the number of items.
[ClientCallable, ClientCallableConstraint(Type = ClientCallableConstraintType.Custom, FixedId = "a", Value = "It MUST be a greater than or equal to zero.")]
public int ItemCount
{
 get
 {
  this.EnsurePropsFresh();
  return (int)this.m_arrListProps[(int)((System.UIntPtr)20), (int)((System.UIntPtr)this.m_iRow)];
 }
}

воскресенье, 7 февраля 2016 г.

Корректное получение элементов списка в Task.Run


Итак, нам надо осуществить загрузку элементов списка в другой нити, для чего можно воспользоваться методом Task.Run. Однако, "в лоб" воспользоваться этим методом не удастся, так как потокобезовасность экземпляров SPWeb, как и многих других объектов не гарантируется.

Thread safety

Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

Поэтому следующий код приведет к исключению SPException с внутренним кодом 0x80010102 - ошибка потоков.
private SPWeb _web;

..............................

SPListItemCollection items;

var task1 = Task.Run(() =>
  {
    items = web.GetList(@"Lists\List1").GetItems.OfType();
  })

Решением будет являться создание нового экземпляра SPWeb внутри потока:

private SPWeb _web;

...........................

SPListItemCollection items;

var task1 = Task.Run(() =>
  {
    using (var web = _web.Site.OpenWeb())
    {
       items = web.GetList(@"Lists\List1").GetItems.OfType();
    }
  });

суббота, 6 февраля 2016 г.

Что делают SPWeb.Dispose() и Close()?


Вскрытый код метода Microsoft.SharePoint.SPWeb.Dispose();
// Microsoft.SharePoint.SPWeb
public void Close()
{
 if (this.m_closed)
 {
  return;
 }
 if (this.m_bFirstUniqueAncestorWebInited && this.m_FirstUniqueAncestorWeb != null && !object.ReferenceEquals(this.m_FirstUniqueAncestorWeb, this))
 {
  this.m_FirstUniqueAncestorWeb.Dispose();
  this.m_bFirstUniqueAncestorWebInited = false;
  this.m_FirstUniqueAncestorWeb = null;
 }
 if (this.m_Site != null)
 {
  if (this == this.m_Site.m_rootWeb)
  {
   this.m_Site.m_rootWeb = null;
  }
  this.m_Site.InvalidateWeb(this);
 }
 if (this.m_ctsAll != null)
 {
  foreach (SPContentType sPContentType in this.m_ctsAll)
  {
   sPContentType.CloseWebAsNecessary();
  }
 }
 this.Invalidate(SPContentTypeClass.Field);
 this.Invalidate(SPContentTypeClass.Type);
 if (this.m_Site != null)
 {
  this.m_Site.RemoveFromOpenedWebs(this);
 }
 this.Invalidate();
 this.m_closed = true;
}

[SharePointPermission(SecurityAction.Demand, ObjectModel=true)]
public void Dispose()
{
  this.Close();
}

До кучи System.IO.StreamWriter.Dispose():

// System.IO.TextWriter
/// Освобождает все ресурсы, используемые объектом .
public void Dispose()
{
 this.Dispose(true);
 GC.SuppressFinalize(this);
}
// System.IO.StreamWriter
/// Закрывает текущий объект StreamWriter и базовый поток.
/// Текущая кодировка не поддерживает отображение половины суррогатной пары Юникода.
/// 1
public override void Close()
{
 this.Dispose(true);
 GC.SuppressFinalize(this);
}
// System.IO.StreamWriter
/// Освобождает неуправляемые ресурсы, используемые  (при необходимости освобождает и управляемые ресурсы).
/// 
///           Значение true позволяет освободить управляемые и неуправляемые ресурсы; значение false позволяет освободить только неуправляемые ресурсы. 
/// Текущая кодировка не поддерживает отображение половины суррогатной пары Юникода.
protected override void Dispose(bool disposing)
{
 try
 {
  if (this.stream != null && (disposing || (!this.Closable && this.stream is __ConsoleStream)))
  {
   this.Flush(true, true);
   if (this.mdaHelper != null)
   {
    GC.SuppressFinalize(this.mdaHelper);
   }
  }
 }
 finally
 {
  if (this.Closable && this.stream != null)
  {
   try
   {
    if (disposing)
    {
     this.stream.Close();
    }
   }
   finally
   {
    this.stream = null;
    this.byteBuffer = null;
    this.charBuffer = null;
    this.encoding = null;
    this.encoder = null;
    this.charLen = 0;
    base.Dispose(disposing);
   }
  }
 }
}
И еще до кучи (System.Data.SQLClient.SQLConnection.Dispose()):
protected override void Dispose(bool disposing)
{
  if (disposing)
  {
      this._userConnectionOptions = null;
      this._poolGroup = null;
      this.Close();
  }
  this.DisposeMe(disposing);
  base.Dispose(disposing);
}

понедельник, 1 февраля 2016 г.

Новые возможности C# 6.0

Перевод статьи New Features of C# 6.0

В этой статье мы познакомимся с новыми ключевыми фишками и усовершенствованиями, появившимися в C# 6.0.

Автоматически реализуемые свойства:

Эта штука позволяет нам задать значения свойств на этапе их объявления.
Previously, we use constructor to initialize the auto properties to non-default value but with this new feature in C# 6.0, it doesn’t require to initialize these properties with a constructor aкак показано ниже:

class Customer
{
    public string Firstname{get; set;} = "Csharpstar";
    public string Lastname{get; set;} = "Admin";
    public int Age{get;} = 20;
    public DateTime BirthDate { get; set; }
}
We can use this property with getter/setter and getter only.Using getter only help achieve immutability.

Фильтры исключений:

Microsoft introduced this CLR feature in C# with version 6.0 but it was already available in Visual Basic and F#. To use Exception Filters in C#, we have to declare the filter condition in the same line as that of the catch block and the catch block will execute only if the condition is successfully met as shown below:

try
{
throw new CustomException("Test Exception")
}
catch(CustomException ex) if (ex.Message=="Not Test")
{
//Control will not come here because exception name is not test
}
catch(CustomException ex) if (ex.Message=="Test Exception")
{
//Control will come here because exception name is Test Exception
}
 
Remember that Exception Filter is a debugging feature rather than a coding feature. We will discuss more on this in next post

Await в блоках catch и finally block:

We frequently log exceptions to a log file or a database. Such operations are resource extensive and lengthy as we would need more time to perform I/O. In such circumstances, it would be great if we can make asynchronous calls inside our exception blocks. We may additionally need to perform some cleanup operations in finally block which may also be resource extensive.

try
{
// code that might throw exception
}
catch(Exception ex)
{
await LogExceptionAsync(ex);
}

Getter-only Auto Properties

When you use auto implemented properties in C# 5 and lower, you must provide a get and set. If you want the property value to be immutable, you can use the private accessor on the setter. With C# 6, you can now omit the set accessor to achieve true readonly auto implemented properties:

 
public DateTime BirthDate { get; }

Интерполяция строк

String.Format was used till today to generate the FullName value in the Customer class.A new feature named string interpolation provides a large improvement in this area. Rather than filling placeholders with indexes provide an array of values to be slotted in at runtime, you provide the source of the parameter value instead, and the string.Format call is replaced by a single $ sign. This is how the FullName property declaration looks when using string interpolation in the expression body instead:

 
public string FullName => $"{FirstName} {LastName}";
Apart from this saving a number of key strokes, it should also minimise (if not remove) the possibility of FormatExceptions being generated from inadvertently supplying too few values to the argument list.

Инициализация словарей:

В C# 6.0 появился более прозрачный путь для инициализации словарей (см. ниже):

 
var BookDictionary = new Dictionary<int,string>
{
[1] = "ASP.net",
[2] = "C#.net",
[3] = "ASP.net Razor",
[4] = "ASP.net MVC5"
}
А раньше было так:
Dictionary<int,string> BookDictionary = new Dictionary<int,string>()
{
{1, "ASP.net"},
{2, "C#.net"}
{3, = "ASP.net Razor"},
{4, = "ASP.net MVC5"}}
};

Null – условный оператор:

Null-условный оператор “?” использует одинарный знак вопроса. It can be used to reduce the no. of lines in code file and provide an easy access to check for Null and return the result. The null conditional operator tests for null before accessing a member of an instance. Пример:

using System;

class Program
{ static void Test(string name)
    {
 // Use null-conditional operator.
 // ... If name is not null, check its Length property.
 if (name?.Length >= 3)
 {
     Console.WriteLine(true);
 }
    }
    static void Main()
    {
 Console.WriteLine(1);
 Test(null);
 Console.WriteLine(2);
 Test("Csharpstar"); // True.
 Test("x");
 Test("ExamIron"); // True.
    }
}
Выводится:1
2
True
True

воскресенье, 31 января 2016 г.

Даинамическое отображение полей при вставке записей в список (без SharePoint Designer)

Итак, имеются два вида пользователей - физическое лицо, юридическое лицо. Это хранится в поле выбора "Форма оплаты". В зависимости от выбора формы оплаты из выпадающего списка, нужно отображать соответствующие данные: ФИО, паспорт для физлица и реквизиты для юрлица. Для этого:

1) Заходим в список, выбираем в ленте "Список" -> "Веб-части формы" ->Форма создания по умолчанию".

2) Загрузится форма. Затем нажимаем в верхней панели "Добавить веб-часть" и из группы "Среди и контент" выбираем веб-часть "Редактор контента".

3) Далее нажимаем кнопку "Добавить" и кликаем на поле редактора в самой веб-части.

4) В ленте появятся иконки редактирования контента. Где-то справа в конце будет иконка "HTML".

5) Жмем на иконку в в окно вводим следующий скрипт (см. ниже);

6) Сохраняем изменения для веб-части

7) Всё.

 
Особенность: Ввиду того что поиск производится по атрибуту Title, по началу строки, от атрибут Title одного поля не должен полностью входить в атрибут Title другого поля.
Дополнительно

Открытие диалогового окна на вставку записи из навигационной панели.

Открытие диалогового окна на вставку из навигационной панели.

1) Действия сайта -> Параметры сайта -> Внешний вид и функции ->Верхняя панель ссылок или адрес  http://myserver/_layouts/topnav.aspx

2) Далее жмем "Создать ссылку для перехода"

3) В поле "Введите описание" пишем название в горизонтальном пункта меню.

4) В поле "Введите веб-адрес" пишем адрес следующий java-скрипт одной строкой:

JavaScript:var options=SP.UI.$create_DialogOptions();options.url='http://myserver/Lists/List1/NewForm.aspx';options.height = 800;void(SP.UI.ModalDialog.showModalDialog(options))
Вместо адреса http://myserver/Lists/List1/ указываем свой адрес списка

суббота, 30 января 2016 г.

10 частых вопросов на собеседовании по C# на числа


  1. Как поменять два числа местами в C# без использования временной переменной (Решение)

  2. Write a C# program to determine total ways stairs can be climbed (Решение)

  3. Напишите программу на C# для вычисления факториала без рекурсии (Решение)

  4. Напишите на C# программу вывода n-го числа чисел Фибоначчи (Решение)

  5. Напишите на C# программу получения остатка от деления 2-х целых (Решение)

  6. Напишите на C# программу, проверяющую является ли введенное число числом Армстронга (Решение)

  7. Напишите на C# программу поиска НОК и НОД для 2-х заданных чисел. (Решение)

  8. Напишите на C# программу, проверяющую является ли введенное число простым (Решение)

  9. Напишите на C# программу, проверяющую является ли введенное число Палиндромом (Решение)

  10. Напишите на C# программу, решающую проблему FizzBuzz (Решение)

понедельник, 3 августа 2015 г.

Напоминалочка про BeforeProperties и AfterProperties в EventReceiver

Создавая новое события для списка, я постоянно натыкаюсь на то, что свойства BeforeProperties либо AfterProperties порой не заполнены. Обязательно почитайте этот пост – http://www.synergyonline.com/blog/blog-moss/Lists/Posts/Post.aspx?ID=25 А вот пара шпаргалок, заимствованных оттуда:

Для событий списка:

List BeforeProperties AfterProperties properties.ListItem
ItemAdding No Value No Value Null
ItemAdded No Value No Value New Value
ItemUpdating Original Value Changed Value Original Value
ItemUpdated Original Value Changed Value Changed Value
ItemDeleting No Value No Value Original Value
ItemDeleted No Value No Value Null


Для событий библиотеки:

Library BeforeProperties AfterProperties properties.ListItem
ItemAdding No Value No Value Null
ItemAdded No Value No Value New Value
ItemUpdating Original Value Changed Value Original Value
ItemUpdated Original Value Changed Value Changed Value
ItemDeleting No Value No Value Original Value
ItemDeleted No Value No Value Null