четверг, 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