<?xml version="1.0" encoding="utf-8" ?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:tt="http://teletype.in/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"><title>MadDevelop</title><subtitle>Блог отдела разработки MadDevelop. Здесь мы делимся своими статьями. Telegram - https://t.me/maddevelop Instagram - Https://instagram.com/maddevelop</subtitle><author><name>MadDevelop</name></author><id>https://teletype.in/atom/maddevelop</id><link rel="self" type="application/atom+xml" href="https://teletype.in/atom/maddevelop?offset=0"></link><link rel="alternate" type="text/html" href="https://teletype.in/@maddevelop?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=maddevelop"></link><link rel="next" type="application/rss+xml" href="https://teletype.in/atom/maddevelop?offset=10"></link><link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></link><updated>2026-04-30T07:43:35.084Z</updated><entry><id>maddevelop:BJn6Aiy5V</id><link rel="alternate" type="text/html" href="https://teletype.in/@maddevelop/BJn6Aiy5V?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=maddevelop"></link><title>Беседа об алгоритмах</title><published>2019-04-14T20:39:09.546Z</published><updated>2019-11-13T14:23:48.398Z</updated><summary type="html">Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. </summary><content type="html">
  &lt;hr /&gt;
  &lt;h2&gt;!!НАШ БЛОГ ПЕРЕЕХАЛ!!&lt;/h2&gt;
  &lt;p&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. &lt;/p&gt;
  &lt;h3&gt;Наш новый сайт &lt;a href=&quot;https://maddevelop.ru/&quot; target=&quot;_blank&quot;&gt;maddevelop.ru&lt;/a&gt;&lt;/h3&gt;
  &lt;hr /&gt;
  &lt;h2&gt;Первая задача&lt;/h2&gt;
  &lt;p&gt;&lt;strong&gt;Условие&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;Дан массив целых неповторяющихся положительных чисел. Числа, соседние по числовой оси (при этом они могут находиться в разных частях массива), следует заключить в диапазон типа string, в котором указываются через тире начальное число диапазона и конечное. Необходимо получить строку, где в порядке возрастания указаны все диапазоны и/или отдельные цифры, в них не входящие.&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Пример&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;Входной массив: &lt;em&gt;{ 3, 7, 5, 9, 4, 10 }&lt;/em&gt;&lt;/p&gt;
  &lt;p&gt;Ответ: &lt;em&gt;&amp;quot;3 - 5, 7, 9 - 10&amp;quot;&lt;/em&gt;&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Решение&lt;/strong&gt;&lt;/p&gt;
  &lt;pre&gt;// Массив преобразуется в строку &amp;quot;на лету&amp;quot;. Сложность - O(n).
private static string ArrayToString(int[] array)
{
    Array.Sort(array);
    StringBuilder sb = new StringBuilder();
    int length = array.Length;
    if (length == 0)
        sb.Append(&amp;quot;&amp;quot;);
    else if (length == 1)
        sb.Append(array[0].ToString());
    else
    {
        bool defis = false;

        for (int i = 1; i &amp;lt; length; i++)
        {
            if (array[i] - array[i - 1] == 1)
            {
                // Если дефис не поставлен, а числа идут подряд, то пишу число и дефис.
                if (!defis)                                         
                {
                    sb.Append($&amp;quot;{array[i - 1].ToString()} - &amp;quot;);
                    defis = true;
                }
                continue;
            }

            // Если число идёт не подряд, то пишу предыдущее число и ставлю запятую.
            sb.Append($&amp;quot;{array[i - 1].ToString()}, &amp;quot;);      
        
            defis = false;
        }

        // Добавляю последнее число.
        sb.Append(array[length - 1].ToString());                    
    }
    return sb.ToString();
}
&lt;/pre&gt;
  &lt;p&gt;Конечно, сначала массив следует упорядочить и только потом пытаться получать выходную строку. &lt;/p&gt;
  &lt;p&gt;Решение следовало продемонстрировать на бумаге. А для этого необходимо сразу держать в голове все граничные случаи, такие как идущие подряд числа, все числа массива не лежат в диапазонах, последнее значение - отдельное и прочие. Учитывая их, я запутался и не смог решить задачу. &lt;/p&gt;
  &lt;p&gt;Товарищ предложил мне попробовать её сделать не &amp;quot;на лету&amp;quot;, а через списки диапазонов. Такой способ действительно проще для реализации, хоть и увеличатся пространственная и временные сложности.&lt;/p&gt;
  &lt;pre&gt;// Более длинный способ, сложность в худшем случае O(n*n).
private static string ArrayToStringLong(int[] array)
{
    Array.Sort(array);
    StringBuilder sb = new StringBuilder();
    int length = array.Length;
    if (length == 0)
        return &amp;quot;&amp;quot;;
    else if (length == 1)
        return array[0].ToString();
    else
    {
        IList&amp;lt;List&amp;lt;int&amp;gt;&amp;gt; list = new List&amp;lt;List&amp;lt;int&amp;gt;&amp;gt;();          // Создаю список списков
        
        // Текущий список, описывающий диапазон.
        List&amp;lt;int&amp;gt; current = new List&amp;lt;int&amp;gt;() { array[0] };       
        for (int i = 1; i &amp;lt; length; i++)
        {                    
            if (array[i] - array[i - 1] == 1)
            {
                current.Add(array[i]);                        
                continue;
            }
            else
            {
                // Обязательно делать ToList(), так как List - ссылочный тип. 
                // Метод ToList() позволит передать список по значению.
                list.Add(current.ToList());                     
                current.Clear();
                current.Add(array[i]);
            }
        }
        list.Add(current.ToList());       // Последний диапазон добавляем в список списков.

        // Преобразую список диапазонов в выходную строку.
        for (int i = 0; i &amp;lt; list.Count; i++)
        {
            if (list[i].Count == 1)
                sb.Append($&amp;quot;{list[i].ElementAt(0).ToString()}, &amp;quot;);
            else
                sb.Append($&amp;quot;{list[i].ElementAt(0).ToString()} - 
                    {list[i].ElementAt(list[i].Count - 1).ToString()}, &amp;quot;);
        }

        string output = sb.ToString();
        return output.Remove(output.Length - 2, 2);
    }            
}
&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;h2&gt;Вторая задача&lt;/h2&gt;
  &lt;p&gt;&lt;strong&gt;Условие&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;Даны два массива целых неповторяющихся положительных чисел одинаковой длины. Создать третий массив такой, что его i-е значение, равняется количеству пар одинаковых чисел из значений от 0 до i первых двух массивов.&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Пример&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;Первый массив: &lt;em&gt;{ 1, 2, 42, 8, 15 }&lt;/em&gt;&lt;/p&gt;
  &lt;p&gt;Второй массив: &lt;em&gt;{ 2, 5, 8, 7, 1 }&lt;/em&gt;&lt;/p&gt;
  &lt;p&gt;Выходной массив: &lt;em&gt;{ 0, 1, 1, 2, 3 }&lt;/em&gt;&lt;/p&gt;
  &lt;pre&gt;Пояснение к получению выходного массива

0-й элемент
Первый массив: { 1 }
Второй массив: { 2 }
Числа различаются, поэтому выходной массив { 0 }.

1-й элемент
Первый массив: { 1, 2 }
Второй массив: { 2, 5 }
И в первом массиве, и во втором есть цифра 2, поэтому выходной массив { 0, 1 }.

2-й элемент
Первый массив: { 1, 2, 42 }
Второй массив: { 2, 5, 8 }
В массивах по-прежнему одинаковая цифра - 2, поэтому выходной массив { 0, 1, 1 }.

3-й элемент
Первый массив: { 1, 2, 42, 8 }
Второй массив: { 2, 5, 8, 7 }
Появляется ещё пара цифр 8, поэтому выходной массив { 0, 1, 1, 2 }.

4-й элемент
Первый массив: { 1, 2, 42, 8, 15 }
Второй массив: { 2, 5, 8, 7, 1 }
Цифра 1 есть также в обоих массивах, поэтому выходной массив { 0, 1, 1, 2, 3 }.
&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;p&gt;&lt;strong&gt;Решение&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;То, что следует использовать хеш-таблицу я придумал почти сразу. Ещё время ушло на догадку о второй хеш-таблицы.&lt;/p&gt;
  &lt;pre&gt;public static int[] GetPairs(int[] first, int[] second)
{
    int length = first.Length;
    if (length == 0)
        return null;
    int[] output = new int[length];            
    HashSet&amp;lt;int&amp;gt; firstHash = new HashSet&amp;lt;int&amp;gt;();
    HashSet&amp;lt;int&amp;gt; secondHash = new HashSet&amp;lt;int&amp;gt;();

    if (first[0] == second[0])
        output[0] = 1;
    firstHash.Add(first[0]);
    secondHash.Add(second[0]);

    for (int i = 1; i &amp;lt; length; i++)
    {
        output[i] = output[i - 1];
        firstHash.Add(first[i]);
        secondHash.Add(second[i]);
    
        if (first[i] == second[i])
        {
            output[i] = ++output[i];
            continue;
        }

        if (secondHash.Contains(first[i]))
            output[i] = ++output[i];
        if (firstHash.Contains(second[i]))
            output[i] = ++output[i];
    }
    return output;
}
&lt;/pre&gt;
  &lt;p&gt;К сожалению, снова не смог подумать о таких случаях, как пара массивов { 1, 2 } - { 1, 2 } и { 1, 2 } - { 2, 1 }. Поэтому итоговый ответ получил после пары подсказок.&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Вывод&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;Для успешного прохождения собеседований с алгоритмами достаточно набить руку на сайтах типа Leetcode и Codewars. Тогда при взгляде на задачу будет видеть��я верное решение &lt;strong&gt;с учётом&lt;/strong&gt; всех граничных ситуаций. Я, правда, прорешал их небольшое количество из-за того, что посвятил своё время освоению некоторых технологий.&lt;/p&gt;

</content></entry><entry><id>maddevelop:BkaMIXat4</id><link rel="alternate" type="text/html" href="https://teletype.in/@maddevelop/BkaMIXat4?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=maddevelop"></link><title>Проект &quot;Отпуск сотрудников&quot;. Часть 3, интерфейс пользователя.</title><published>2019-04-13T21:45:04.641Z</published><updated>2019-11-13T14:23:25.520Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://teletype.in/files/72/7255a2a1-2057-429e-a42a-e318094c1002.png"></media:thumbnail><category term="wpf" label="WPF"></category><summary type="html">&lt;img src=&quot;https://teletype.in/files/72/7255a2a1-2057-429e-a42a-e318094c1002.png&quot;&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. </summary><content type="html">
  &lt;hr /&gt;
  &lt;h2&gt;!!НАШ БЛОГ ПЕРЕЕХАЛ!!&lt;/h2&gt;
  &lt;p&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. &lt;/p&gt;
  &lt;h3&gt;Наш новый сайт &lt;a href=&quot;https://maddevelop.ru/&quot; target=&quot;_blank&quot;&gt;maddevelop.ru&lt;/a&gt;&lt;/h3&gt;
  &lt;hr /&gt;
  &lt;p&gt;Клиентскую часть реализуем с помощью технологии WPF, воспользовавшись паттерном MVVM. Модели будут те же, что и в серверной части. Просто скопируем их в созданную папку Models. Создадим класс MainViewModel, в котором опишем взаимодействие с API контроллером и с интерфейсом пользователя. Большинство свойств класса модели представления реализуют метод OnPropertyChanged(), поэтому изменение свойства приводит к изменению элемента управления, к которому оно привязано.&lt;/p&gt;
  &lt;pre&gt;public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string prop = &amp;quot;&amp;quot;)
{
    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(prop));
}  
&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;p&gt;Окно пользователя будет выглядеть следующим образом:&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/72/7255a2a1-2057-429e-a42a-e318094c1002.png&quot; width=&quot;1462&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Цифрами обозначены элементы управления для удобства дальнейшего описания. В таблице ниже удобно посмотреть, как организована привязка свойства класса MainViewModel к свойству элемента управления пользовательского интерфейса.&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/53/535e7ede-aec1-47fc-981e-fb7b4fc7e660.png&quot; width=&quot;757&quot; /&gt;
  &lt;/figure&gt;
  &lt;hr /&gt;
  &lt;p&gt;К свойству Command кнопок следует привязать свойство класса RelayCommand в модели представления.&lt;/p&gt;
  &lt;pre&gt;public class RelayCommand : ICommand
{
    private Action&amp;lt;object&amp;gt; execute;
    private Func&amp;lt;object, bool&amp;gt; canExecute;

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public RelayCommand(Action&amp;lt;object&amp;gt; execute, Func&amp;lt;object, bool&amp;gt; canExecute = null)
    {
        this.execute = execute;
        this.canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return this.canExecute == null || this.canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        this.execute(parameter);
    }
}
&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;p&gt;Вторая таблица в пользовательском интерфейсе состоит из четырёх: по одной для каждого квартала года. Для того, чтобы к ним можно было привязывать свойства типа двумерных массивов, следует установить пакет NuGet &amp;quot;Gu.Wpf.DataGrid2D&amp;quot;. Эти матрицы составим из объектов типа Cell.&lt;/p&gt;
  &lt;pre&gt;public Cell[,] FirstQuarter { get; set; }
public class Cell
{
    public Color Color { get; }

    public Cell(Color color)
    {            
        Color = color;
    }
}
&lt;/pre&gt;
  &lt;p&gt;Выделение отпусков сотрудников цветом возможно, если к фону каждой ячейки привязать через конвертер значений свойство Color класса Cell.&lt;/p&gt;
  &lt;pre&gt;public class ConvMycolorColor : IValueConverter
{
    private static System.Drawing.Color color;

    public object Convert(object value, Type targetType, object parameter, 
        CultureInfo culture)
    {
        color = System.Drawing.Color.FromArgb(((Models.Color)value).ColorNumber);
        return new SolidColorBrush(System.Windows.Media.Color
            .FromArgb(color.A, color.R, color.G, color.B));
    }

    public object ConvertBack(object value, Type targetType, object parameter, 
        CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;p&gt;Запишем для ясности определение всех полей и свойств класса MainViewModel.&lt;/p&gt;
  &lt;pre&gt;public class MainViewModel : INotifyPropertyChanged
{
    private static IEnumerable&amp;lt;Employee&amp;gt; employees;        
    private static HttpClient client = new HttpClient();                           
    private StringBuilder errorSB = new StringBuilder();

    // Привязанное свойство №1 из таблицы выше
    private string name;
    public string Name
    {
        get =&amp;gt; name;
        set
        {
            name = value;
            OnPropertyChanged(nameof(Name));
        }
    }

    // №2
    public IEnumerable&amp;lt;Color&amp;gt; Colors { get; set; }

    // Также для элемента управления №2
    private Color empColor;
    public Color EmpColor
    {
        get =&amp;gt; empColor;
        set
        {
            empColor = value;
            OnPropertyChanged(nameof(empColor));
        }
    }

    // №3
    public RelayCommand AddEmployee { get; set; }
    
    // №4    
    public RelayCommand DeleteEmployee { get; set; }

    // №8
    public RelayCommand CommandAddVacation { get; set; }

    // №9
    public RelayCommand CommandDeleteVacation { get; set; }  

    // №15
    public RelayCommand CommandRefresh { get; set; }
    
    // №5
    private Employee currentEmployee;
    public Employee CurrentEmployee
    {
        get =&amp;gt; currentEmployee;
        set
        {
            currentEmployee = value;
            OnPropertyChanged(nameof(CurrentEmployee));
        }
    }

    // Привязываемое свойство к свйоству также элемента №5
    private Vacation currentVacation;
    public Vacation CurrentVacation
    {
        get =&amp;gt; currentVacation;
        set
        {
            currentVacation = value;
            OnPropertyChanged(nameof(CurrentVacation));
            if (currentVacation != null)
            {
                Start = currentVacation.Start;
                Duration = currentVacation.Duration.ToString();
            }                
        }
    }

    // №6
    private DateTime start = DateTime.Now;
    public DateTime Start
    {
        get =&amp;gt; start;
        set
        {
            start = value;
            OnPropertyChanged(nameof(Start));
        }
    }

    // №7
    private string duration;
    public string Duration
    {
        get =&amp;gt; duration;
        set
        {
            duration = value;
            OnPropertyChanged(nameof(Duration));
        }
    }

    // №10
    private DataView table;
    public DataView Table
    {
        get =&amp;gt; table;
        set
        {
            table = value;                             
            OnPropertyChanged(nameof(Table));
            EmployeeNames = employees.Select(x =&amp;gt; x.Name).ToList();                
        }
    }

    // №10
    private DataRowView currentRow;
    public DataRowView CurrentRow
    {
        get =&amp;gt; currentRow;
        set
        {
            currentRow = value;
            OnPropertyChanged(nameof(CurrentRow));
            if (currentRow != null)
            {
                CurrentEmployee = employees
                    .FirstOrDefault(e =&amp;gt; e.Name 
                        == currentRow.Row.ItemArray.ElementAt(0) as string);
                Name = CurrentEmployee.Name;
                EmpColor = Colors.FirstOrDefault(c =&amp;gt; CurrentEmployee.ColorId == c.ColorId);
            }                    
        }
    }

    // №11
    private IEnumerable&amp;lt;string&amp;gt; employeeNames;
    public IEnumerable&amp;lt;string&amp;gt; EmployeeNames
    {
        get =&amp;gt; employeeNames;
        set
        {
            employeeNames = value;
            OnPropertyChanged(nameof(EmployeeNames));
        }
    }
    
    // №11    
    public Cell[,] FirstQuarter { get; set; }

    // №12
    public Cell[,] SecondQuarter { get; set; }

    // №13
    public Cell[,] ThirdQuarter { get; set; }

    // №14
    public Cell[,] FourthQuarter { get; set; }

    // №16
    private string error;
    // Свойство для записывания ошибок
    public string Error
    {
        get =&amp;gt; error;
        set
        {
            error = value;
            OnPropertyChanged(nameof(Error));
        }
    }       
&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;p&gt;Напоследок приведём XAML-разметку окна программы:&lt;/p&gt;
  &lt;pre&gt;&amp;lt;Window x:Class=&amp;quot;FrontendWPF.MainWindow&amp;quot;
        xmlns=&amp;quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&amp;quot;
        xmlns:x=&amp;quot;http://schemas.microsoft.com/winfx/2006/xaml&amp;quot;
        xmlns:d=&amp;quot;http://schemas.microsoft.com/expression/blend/2008&amp;quot;
        xmlns:mc=&amp;quot;http://schemas.openxmlformats.org/markup-compatibility/2006&amp;quot;
        xmlns:dataGrid2D=&amp;quot;http://gu.se/DataGrid2D&amp;quot; 
        xmlns:local=&amp;quot;clr-namespace:FrontendWPF&amp;quot;
        mc:Ignorable=&amp;quot;d&amp;quot;
        Title=&amp;quot;MainWindow&amp;quot; SizeToContent=&amp;quot;WidthAndHeight&amp;quot;&amp;gt;
    &amp;lt;Window.DataContext&amp;gt;
        &amp;lt;local:MainViewModel/&amp;gt;
    &amp;lt;/Window.DataContext&amp;gt;
    &amp;lt;Window.Resources&amp;gt;
        &amp;lt;local:ConvMyColorString x:Key=&amp;quot;myColorConverter&amp;quot;/&amp;gt;
        &amp;lt;local:ConvMycolorColor x:Key=&amp;quot;colorToTrueColor&amp;quot;/&amp;gt;
    &amp;lt;/Window.Resources&amp;gt;
    &amp;lt;Grid&amp;gt;
        &amp;lt;Grid.RowDefinitions&amp;gt;
            &amp;lt;RowDefinition/&amp;gt;
            &amp;lt;RowDefinition/&amp;gt;
            &amp;lt;RowDefinition/&amp;gt;
            &amp;lt;RowDefinition/&amp;gt;
            &amp;lt;RowDefinition/&amp;gt;
        &amp;lt;/Grid.RowDefinitions&amp;gt;
        &amp;lt;Grid&amp;gt;            
            &amp;lt;Grid.ColumnDefinitions&amp;gt;
                &amp;lt;ColumnDefinition Width=&amp;quot;Auto&amp;quot;/&amp;gt;
                &amp;lt;ColumnDefinition/&amp;gt;
            &amp;lt;/Grid.ColumnDefinitions&amp;gt;
            &amp;lt;Grid&amp;gt;
                &amp;lt;Grid.RowDefinitions&amp;gt;
                    &amp;lt;RowDefinition/&amp;gt;
                    &amp;lt;RowDefinition/&amp;gt;
                &amp;lt;/Grid.RowDefinitions&amp;gt;
                &amp;lt;Grid.ColumnDefinitions&amp;gt;
                    &amp;lt;ColumnDefinition Width=&amp;quot;50&amp;quot;/&amp;gt;
                    &amp;lt;ColumnDefinition Width=&amp;quot;200&amp;quot;/&amp;gt;
                    &amp;lt;ColumnDefinition Width=&amp;quot;85&amp;quot;/&amp;gt;
                    &amp;lt;ColumnDefinition Width=&amp;quot;25&amp;quot;/&amp;gt;
                    &amp;lt;ColumnDefinition Width=&amp;quot;90&amp;quot;/&amp;gt;
                    &amp;lt;ColumnDefinition Width=&amp;quot;90&amp;quot;/&amp;gt;
                    &amp;lt;ColumnDefinition Width=&amp;quot;110&amp;quot;/&amp;gt;
                    &amp;lt;ColumnDefinition Width=&amp;quot;85&amp;quot;/&amp;gt;
                &amp;lt;/Grid.ColumnDefinitions&amp;gt;
                &amp;lt;TextBlock Text=&amp;quot;Отпуски&amp;quot; Grid.Column=&amp;quot;4&amp;quot; Margin=&amp;quot;5&amp;quot; 
                    HorizontalAlignment=&amp;quot;Center&amp;quot; VerticalAlignment=&amp;quot;Bottom&amp;quot;/&amp;gt;
                &amp;lt;ComboBox Grid.Column=&amp;quot;4&amp;quot; Grid.Row=&amp;quot;1&amp;quot; Margin=&amp;quot;5&amp;quot; Width=&amp;quot;80&amp;quot; 
                    HorizontalAlignment=&amp;quot;Left&amp;quot; SelectedItem=&amp;quot;{Binding CurrentVacation}&amp;quot; 
                    ItemStringFormat=&amp;quot;d&amp;quot; ItemsSource=&amp;quot;{Binding CurrentEmployee.Vacations}&amp;quot; 
                    DisplayMemberPath=&amp;quot;Start&amp;quot;/&amp;gt;
                &amp;lt;TextBlock Text=&amp;quot;Дата начала:&amp;quot; Margin=&amp;quot;5&amp;quot; Grid.Column=&amp;quot;5&amp;quot; 
                    HorizontalAlignment=&amp;quot;Right&amp;quot;/&amp;gt;
                &amp;lt;TextBlock Text=&amp;quot;Длительность:&amp;quot; Margin=&amp;quot;5&amp;quot; Grid.Column=&amp;quot;5&amp;quot; Grid.Row=&amp;quot;1&amp;quot; 
                    HorizontalAlignment=&amp;quot;Right&amp;quot;/&amp;gt;
                &amp;lt;DatePicker Grid.Column=&amp;quot;6&amp;quot; Margin=&amp;quot;5&amp;quot; SelectedDateFormat=&amp;quot;Short&amp;quot; 
                    SelectedDate=&amp;quot;{Binding Start}&amp;quot; DisplayDateStart=&amp;quot;2019/01/01&amp;quot; 
                    DisplayDateEnd=&amp;quot;2019/12/31&amp;quot;/&amp;gt;
                &amp;lt;TextBox Grid.Column=&amp;quot;6&amp;quot; Grid.Row=&amp;quot;1&amp;quot; Margin=&amp;quot;5&amp;quot; Width=&amp;quot;22&amp;quot; 
                    HorizontalAlignment=&amp;quot;Left&amp;quot; 
                    Text=&amp;quot;{Binding Duration, UpdateSourceTrigger=PropertyChanged}&amp;quot;/&amp;gt;
                &amp;lt;Button Content=&amp;quot;Добавить&amp;quot; Grid.Column=&amp;quot;7&amp;quot; Width=&amp;quot;75&amp;quot; Margin=&amp;quot;5&amp;quot; 
                    Command=&amp;quot;{Binding CommandAddVacation}&amp;quot;/&amp;gt;
                &amp;lt;Button Content=&amp;quot;Удалить&amp;quot; Grid.Column=&amp;quot;7&amp;quot; Grid.Row=&amp;quot;1&amp;quot; Width=&amp;quot;75&amp;quot; Margin=&amp;quot;5&amp;quot; 
                    Command=&amp;quot;{Binding CommandDeleteVacation}&amp;quot;/&amp;gt;
                &amp;lt;TextBlock Text=&amp;quot;ФИО:&amp;quot; Margin=&amp;quot;5&amp;quot; HorizontalAlignment=&amp;quot;Right&amp;quot;/&amp;gt;
                &amp;lt;TextBox Grid.Column=&amp;quot;1&amp;quot; Margin=&amp;quot;5&amp;quot; 
                    Text=&amp;quot;{Binding Name, UpdateSourceTrigger=PropertyChanged}&amp;quot;/&amp;gt;
                &amp;lt;TextBlock Text=&amp;quot;Цвет:&amp;quot; Grid.Row=&amp;quot;1&amp;quot; Margin=&amp;quot;5&amp;quot; HorizontalAlignment=&amp;quot;Right&amp;quot;/&amp;gt;
                &amp;lt;ComboBox Grid.Column=&amp;quot;1&amp;quot; Grid.Row=&amp;quot;1&amp;quot; Margin=&amp;quot;5&amp;quot; Width=&amp;quot;80&amp;quot; 
                    HorizontalAlignment=&amp;quot;Left&amp;quot; SelectedItem=&amp;quot;{Binding EmpColor}&amp;quot;
                    ItemsSource=&amp;quot;{Binding Colors}&amp;quot;&amp;gt;
                    &amp;lt;ComboBox.ItemTemplate&amp;gt;
                        &amp;lt;DataTemplate&amp;gt;
                            &amp;lt;TextBlock Text=&amp;quot;{Binding Converter={StaticResource 
                                myColorConverter}}&amp;quot;/&amp;gt;
                        &amp;lt;/DataTemplate&amp;gt;
                    &amp;lt;/ComboBox.ItemTemplate&amp;gt;
                &amp;lt;/ComboBox&amp;gt;
                &amp;lt;Button Content=&amp;quot;Добавить&amp;quot; Grid.Column=&amp;quot;2&amp;quot; Width=&amp;quot;75&amp;quot; Margin=&amp;quot;5&amp;quot; 
                    Command=&amp;quot;{Binding AddEmployee}&amp;quot;/&amp;gt;
                &amp;lt;Button Content=&amp;quot;Удалить&amp;quot; Grid.Column=&amp;quot;2&amp;quot; Grid.Row=&amp;quot;1&amp;quot; Width=&amp;quot;75&amp;quot; Margin=&amp;quot;5&amp;quot; 
                    Command=&amp;quot;{Binding DeleteEmployee}&amp;quot;/&amp;gt;
            &amp;lt;/Grid&amp;gt;            
        &amp;lt;/Grid&amp;gt;

        &amp;lt;DataGrid ItemsSource=&amp;quot;{Binding Table}&amp;quot; CanUserAddRows=&amp;quot;False&amp;quot; 
            HorizontalAlignment=&amp;quot;Left&amp;quot; Grid.Row=&amp;quot;1&amp;quot; SelectedItem=&amp;quot;{Binding CurrentRow}&amp;quot;/&amp;gt;
        
        &amp;lt;TextBlock Grid.Row=&amp;quot;2&amp;quot; Text=&amp;quot;График отпусков на 2019 год&amp;quot; FontWeight=&amp;quot;Bold&amp;quot; 
            Margin=&amp;quot;5,20,5,5&amp;quot;/&amp;gt; 
        
        &amp;lt;StackPanel Grid.Row=&amp;quot;3&amp;quot; Orientation=&amp;quot;Horizontal&amp;quot; Margin=&amp;quot;5&amp;quot;&amp;gt;
            &amp;lt;StackPanel&amp;gt;
                &amp;lt;TextBlock Text=&amp;quot;1 квартал&amp;quot; HorizontalAlignment=&amp;quot;Center&amp;quot;/&amp;gt;
                &amp;lt;DataGrid SelectionUnit=&amp;quot;Cell&amp;quot; ColumnWidth=&amp;quot;3&amp;quot;
                    dataGrid2D:ItemsSource.Array2D=&amp;quot;{Binding FirstQuarter, 
                    UpdateSourceTrigger=PropertyChanged}&amp;quot; HeadersVisibility=&amp;quot;Row&amp;quot; 
                    MinColumnWidth=&amp;quot;2&amp;quot; GridLinesVisibility=&amp;quot;Horizontal&amp;quot; 
                    HorizontalAlignment=&amp;quot;Left&amp;quot; dataGrid2D:ItemsSource.RowHeadersSource=
                    &amp;quot;{Binding EmployeeNames, UpdateSourceTrigger=PropertyChanged}&amp;quot;&amp;gt;
                    &amp;lt;DataGrid.Resources&amp;gt;
                        &amp;lt;Style TargetType=&amp;quot;DataGridCell&amp;quot;&amp;gt;
                            &amp;lt;Setter Property=&amp;quot;BorderThickness&amp;quot; Value=&amp;quot;0&amp;quot;/&amp;gt;
                        &amp;lt;/Style&amp;gt;
                    &amp;lt;/DataGrid.Resources&amp;gt;
                    &amp;lt;dataGrid2D:Cell.Template&amp;gt;
                        &amp;lt;DataTemplate&amp;gt;
                            &amp;lt;Grid Background=&amp;quot;{Binding Color, Converter={StaticResource 
                                colorToTrueColor}}&amp;quot; Width=&amp;quot;3&amp;quot;/&amp;gt;
                        &amp;lt;/DataTemplate&amp;gt;
                    &amp;lt;/dataGrid2D:Cell.Template&amp;gt;
                &amp;lt;/DataGrid&amp;gt;
            &amp;lt;/StackPanel&amp;gt;
            
            &amp;lt;StackPanel&amp;gt;
                &amp;lt;TextBlock Text=&amp;quot;2 квартал&amp;quot; HorizontalAlignment=&amp;quot;Center&amp;quot;/&amp;gt;
                &amp;lt;DataGrid SelectionUnit=&amp;quot;Cell&amp;quot; dataGrid2D:ItemsSource.Array2D=
                    &amp;quot;{Binding SecondQuarter, UpdateSourceTrigger=PropertyChanged}&amp;quot;  
                    ColumnWidth=&amp;quot;3&amp;quot; HeadersVisibility=&amp;quot;None&amp;quot;  MinColumnWidth=&amp;quot;2&amp;quot; 
                    GridLinesVisibility=&amp;quot;Horizontal&amp;quot; HorizontalAlignment=&amp;quot;Left&amp;quot; 
                    RowHeight=&amp;quot;22&amp;quot;&amp;gt;
                    &amp;lt;DataGrid.Resources&amp;gt;
                        &amp;lt;Style TargetType=&amp;quot;DataGridCell&amp;quot;&amp;gt;
                            &amp;lt;Setter Property=&amp;quot;BorderThickness&amp;quot; Value=&amp;quot;0&amp;quot;/&amp;gt;
                        &amp;lt;/Style&amp;gt;
                    &amp;lt;/DataGrid.Resources&amp;gt;
                    &amp;lt;dataGrid2D:Cell.Template&amp;gt;
                        &amp;lt;DataTemplate&amp;gt;
                            &amp;lt;Grid Background=&amp;quot;{Binding Color, Converter={StaticResource 
                                colorToTrueColor}}&amp;quot; Width=&amp;quot;3&amp;quot;/&amp;gt;
                        &amp;lt;/DataTemplate&amp;gt;
                    &amp;lt;/dataGrid2D:Cell.Template&amp;gt;
                &amp;lt;/DataGrid&amp;gt;
            &amp;lt;/StackPanel&amp;gt;


            &amp;lt;StackPanel&amp;gt;
                &amp;lt;TextBlock Text=&amp;quot;3 квартал&amp;quot; HorizontalAlignment=&amp;quot;Center&amp;quot;/&amp;gt;
                &amp;lt;DataGrid SelectionUnit=&amp;quot;Cell&amp;quot; dataGrid2D:ItemsSource.Array2D=&amp;quot;{Binding 
                    ThirdQuarter, UpdateSourceTrigger=PropertyChanged}&amp;quot; ColumnWidth=&amp;quot;3&amp;quot; 
                    HeadersVisibility=&amp;quot;None&amp;quot;  MinColumnWidth=&amp;quot;2&amp;quot; RowHeight=&amp;quot;22&amp;quot;
                    GridLinesVisibility=&amp;quot;Horizontal&amp;quot; РorizontalAlignment=&amp;quot;Left&amp;quot;&amp;gt;
                    &amp;lt;DataGrid.Resources&amp;gt;
                        &amp;lt;Style TargetType=&amp;quot;DataGridCell&amp;quot;&amp;gt;
                            &amp;lt;Setter Property=&amp;quot;BorderThickness&amp;quot; Value=&amp;quot;0&amp;quot;/&amp;gt;
                        &amp;lt;/Style&amp;gt;
                    &amp;lt;/DataGrid.Resources&amp;gt;
                    &amp;lt;dataGrid2D:Cell.Template&amp;gt;
                        &amp;lt;DataTemplate&amp;gt;
                            &amp;lt;Grid Background=&amp;quot;{Binding Color, Converter={StaticResource 
                                colorToTrueColor}}&amp;quot; Width=&amp;quot;3&amp;quot;/&amp;gt;
                        &amp;lt;/DataTemplate&amp;gt;
                    &amp;lt;/dataGrid2D:Cell.Template&amp;gt;
                &amp;lt;/DataGrid&amp;gt;
            &amp;lt;/StackPanel&amp;gt;


            &amp;lt;StackPanel&amp;gt;
                &amp;lt;TextBlock Text=&amp;quot;4 квартал&amp;quot; HorizontalAlignment=&amp;quot;Center&amp;quot;/&amp;gt;
                &amp;lt;DataGrid SelectionUnit=&amp;quot;Cell&amp;quot; dataGrid2D:ItemsSource.Array2D=&amp;quot;{Binding 
                    FourthQuarter, UpdateSourceTrigger=PropertyChanged}&amp;quot; ColumnWidth=&amp;quot;3&amp;quot; 
                    HeadersVisibility=&amp;quot;None&amp;quot;  MinColumnWidth=&amp;quot;2&amp;quot; RowHeight=&amp;quot;22&amp;quot;
                    GridLinesVisibility=&amp;quot;Horizontal&amp;quot; HorizontalAlignment=&amp;quot;Left&amp;quot;&amp;gt;
                    &amp;lt;DataGrid.Resources&amp;gt;
                        &amp;lt;Style TargetType=&amp;quot;DataGridCell&amp;quot;&amp;gt;
                            &amp;lt;Setter Property=&amp;quot;BorderThickness&amp;quot; Value=&amp;quot;0&amp;quot;/&amp;gt;
                        &amp;lt;/Style&amp;gt;
                    &amp;lt;/DataGrid.Resources&amp;gt;
                    &amp;lt;dataGrid2D:Cell.Template&amp;gt;
                        &amp;lt;DataTemplate&amp;gt;
                            &amp;lt;Grid Background=&amp;quot;{Binding Color, Converter={StaticResource 
                                colorToTrueColor}}&amp;quot; Width=&amp;quot;3&amp;quot;/&amp;gt;
                        &amp;lt;/DataTemplate&amp;gt;
                    &amp;lt;/dataGrid2D:Cell.Template&amp;gt;
                &amp;lt;/DataGrid&amp;gt;
            &amp;lt;/StackPanel&amp;gt;
        &amp;lt;/StackPanel&amp;gt;          
 
        &amp;lt;StackPanel Grid.Row=&amp;quot;4&amp;quot; Orientation=&amp;quot;Horizontal&amp;quot;&amp;gt;
                &amp;lt;Button Content=&amp;quot;Обновить&amp;quot; Width=&amp;quot;80&amp;quot; Command=&amp;quot;{Binding CommandRefresh}&amp;quot; 
                    HorizontalAlignment=&amp;quot;Right&amp;quot; Margin=&amp;quot;5,0,20,5&amp;quot; 
                    VerticalAlignment=&amp;quot;Center&amp;quot;/&amp;gt;
                &amp;lt;TextBlock Text=&amp;quot;Ошибки:&amp;quot; Margin=&amp;quot;5,0,0,5&amp;quot; VerticalAlignment=&amp;quot;Center&amp;quot;/&amp;gt;
                &amp;lt;TextBox Width=&amp;quot;300&amp;quot; Margin=&amp;quot;5,0,5,5&amp;quot; Text=&amp;quot;{Binding Error}&amp;quot;/&amp;gt;
        &amp;lt;/StackPanel&amp;gt;           
    &amp;lt;/Grid&amp;gt;
&amp;lt;/Window&amp;gt;
&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;p&gt;В следующей части рассмотрим, каким образом происходит заполнение двух таблиц в интерфейсе пользователя.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;p&gt;&lt;strong&gt;&lt;em&gt;Ещё больше интересной информации на нашем &lt;a href=&quot;https://t.me/maddevelop&quot; target=&quot;_blank&quot;&gt;Telegram&lt;/a&gt;-канале.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;&lt;em&gt;&amp;lt;&amp;lt; &lt;a href=&quot;https://blog.maddevelop.ru/r1vy4votN&quot; target=&quot;_blank&quot;&gt;К части 2&lt;/a&gt; &amp;lt;&amp;lt; ......... &lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</content></entry><entry><id>maddevelop:r1vy4votN</id><link rel="alternate" type="text/html" href="https://teletype.in/@maddevelop/r1vy4votN?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=maddevelop"></link><title>Проект &quot;Отпуск сотрудников&quot;. Часть 2, хранилище и контроллер.</title><published>2019-04-10T12:40:06.336Z</published><updated>2019-11-13T14:23:11.535Z</updated><category term="topic2958" label="Asp.Net"></category><summary type="html">&lt;img src=&quot;https://teletype.in/files/c8/c83873ef-e614-41b3-a019-88280805ad99.png&quot;&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. </summary><content type="html">
  &lt;hr /&gt;
  &lt;h2&gt;!!НАШ БЛОГ ПЕРЕЕХАЛ!!&lt;/h2&gt;
  &lt;p&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. &lt;/p&gt;
  &lt;h3&gt;Наш новый сайт &lt;a href=&quot;https://maddevelop.ru/&quot; target=&quot;_blank&quot;&gt;maddevelop.ru&lt;/a&gt;&lt;/h3&gt;
  &lt;hr /&gt;
  &lt;p&gt;Создадим интерфейс &lt;strong&gt;IEmployeeRepository&lt;/strong&gt;, в котором будут свойства с названиями таблиц из базы данных, а также методы для добавления и удаления элементов в таблицы.&lt;/p&gt;
  &lt;pre&gt;public interface IEmployeeRepository
{
    IEnumerable&amp;lt;Employee&amp;gt; Employees { get; }
    IEnumerable&amp;lt;Vacation&amp;gt; Vacations { get; }
    IEnumerable&amp;lt;Color&amp;gt; Colors { get; }

    Employee AddEmployee(Employee employee);
    void DeleteEmployee(int id);

    void AddVacation(int employeeId, Vacation vacation);
    void DeleteVacation(int employeeId, int vacationId);
}
&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;p&gt;Определим класс &lt;strong&gt;EFEmployeeRepository&lt;/strong&gt;, реализующий интерфейс &lt;em&gt;IEmployeeRepository&lt;/em&gt;.&lt;/p&gt;
  &lt;pre&gt;public class EFEmployeeRepository : IEmployeeRepository
{
    private ApplicationDbContext context;

    public EFEmployeeRepository(ApplicationDbContext ctx)
    {
        context = ctx;
    }

    public IEnumerable&amp;lt;Color&amp;gt; Colors =&amp;gt; context.Colors;
    public IEnumerable&amp;lt;Employee&amp;gt; Employees =&amp;gt; context.Employees.Include(v =&amp;gt; v.Vacations);
    public IEnumerable&amp;lt;Vacation&amp;gt; Vacations =&amp;gt; context.Vacations; 

    .....
}
&lt;/pre&gt;
  &lt;p&gt;Так как между таблицами базы данных &lt;em&gt;Employees&lt;/em&gt; и &lt;em&gt;Vacations&lt;/em&gt; имеется связь &amp;quot;&lt;em&gt;один-ко-многим&lt;/em&gt;&amp;quot;, то вместе со списком сотрудников требуется подгружать список отпусков, используя метод &lt;em&gt;Include()&lt;/em&gt;.&lt;/p&gt;
  &lt;pre&gt;public IEnumerable&amp;lt;Employee&amp;gt; Employees =&amp;gt; context.Employees.Include(v =&amp;gt; v.Vacations);
&lt;/pre&gt;
  &lt;p&gt;Нам не требуется смотреть список сотрудников, цвета которых в сводной таблице окрашиваются одним цветом, поэтому просто получаем список цветов, без&lt;em&gt; Include()&lt;/em&gt;.&lt;/p&gt;
  &lt;pre&gt;public IEnumerable&amp;lt;Color&amp;gt; Colors =&amp;gt; context.Colors;
&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;p&gt;Методы добавления и удаления сущностей опишем так:&lt;/p&gt;
  &lt;pre&gt;public Employee AddEmployee(Employee employee)
{
    context.Employees.Add(employee);
    context.SaveChanges();
    return employee;
}

public void DeleteEmployee(int id)
{
    Employee deletedEmployee = context.Employees.FirstOrDefault(e =&amp;gt; e.EmployeeId == id);
    if (deletedEmployee != null)
    {
        context.Employees.Remove(deletedEmployee);
        context.SaveChanges();
    }
}

public void AddVacation(int employeeId, Vacation vacation)
{
    Employee changedEmployee = context.Employees
        .FirstOrDefault(e =&amp;gt; e.EmployeeId == employeeId);
    if (changedEmployee != null)
    {
        changedEmployee.Vacations.Add(vacation);
        context.SaveChanges();
    }            
}

public void DeleteVacation(int employeeId, int vacationId)
{
    Employee changedEmployee = context.Employees
        .FirstOrDefault(e =&amp;gt; e.EmployeeId == employeeId);
    if (changedEmployee != null)
    {
        Vacation deletedVacation = changedEmployee.Vacations
            .FirstOrDefault(v =&amp;gt; v.VacationId == vacationId);
        if (deletedVacation != null)
        {
            changedEmployee.Vacations.Remove(deletedVacation);
            context.SaveChanges();
        }
    }           
}
&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;p&gt;Теперь реализуем &lt;strong&gt;API-контроллер&lt;/strong&gt;.&lt;/p&gt;
  &lt;pre&gt;[Route(&amp;quot;api/[controller]&amp;quot;)]
[ApiController]
public class ValuesController : ControllerBase
{
    private IEmployeeRepository employeeRepository;

    public ValuesController(IEmployeeRepository repo)
    {
        employeeRepository = repo;
    }        

    public IEnumerable&amp;lt;Employee&amp;gt; GetEmployees()
    {
        return employeeRepository.Employees;
    }

    [HttpGet(&amp;quot;colors&amp;quot;)]
    public IEnumerable&amp;lt;Color&amp;gt; GetColors()
    {
        return employeeRepository.Colors;
    }

    [HttpPost]
    public Employee PostEmployee([FromBody]Employee employee)
    {
        return employeeRepository.AddEmployee(employee);
    }

    [HttpDelete(&amp;quot;{id}&amp;quot;)]
    public void DeleteEmployee(int id)
    {
        employeeRepository.DeleteEmployee(id);
    }

    [HttpPost(&amp;quot;vacation&amp;quot;)]
    public void AddVacation([FromBody]VacationViewModel vacationVM)
    {
        employeeRepository.AddVacation(vacationVM.EmployeeId, vacationVM.Vacation);
    }

    [HttpDelete(&amp;quot;{idE}/{idV}&amp;quot;)]
    public void DeleteVacation(int idE, int idV)
    {
        employeeRepository.DeleteVacation(idE, idV);
    }
}
&lt;/pre&gt;
  &lt;p&gt;Для пояснения работы контроллера приведём таблицу, в которой для каждого маршрута приведён метод запроса и метод, который этим запросом вызывается.&lt;/p&gt;
  &lt;figure class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/c8/c83873ef-e614-41b3-a019-88280805ad99.png&quot; width=&quot;694&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Стоит отметить, что для добавления нового отпуска у сотрудника требуется передавать в контроллер идентификатор сотрудника и класс нового отпуска. Для передачи двух объектов используется вспомогательный класс.&lt;/p&gt;
  &lt;pre&gt;public class VacationViewModel
{
    public int EmployeeId { get; set; }
    public Vacation Vacation { get; set; }
}
&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;p&gt;На этом разработка серверной части закончена. В следующих частях будет описан интерфейс пользователя.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;p&gt;&lt;strong&gt;&lt;em&gt;Ещё больше интересной информации на нашем &lt;a href=&quot;https://t.me/maddevelop&quot; target=&quot;_blank&quot;&gt;Telegram&lt;/a&gt;-канале.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;&lt;em&gt;&amp;lt;&amp;lt; &lt;a href=&quot;https://blog.maddevelop.ru/Hy60Et7YE&quot; target=&quot;_blank&quot;&gt;К части 1&lt;/a&gt; &amp;lt;&amp;lt; ......... &amp;gt;&amp;gt; &lt;a href=&quot;https://blog.maddevelop.ru/BkaMIXat4&quot; target=&quot;_blank&quot;&gt;К части 3&lt;/a&gt; &amp;gt;&amp;gt;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</content></entry><entry><id>maddevelop:Hy60Et7YE</id><link rel="alternate" type="text/html" href="https://teletype.in/@maddevelop/Hy60Et7YE?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=maddevelop"></link><title>Проект &quot;Отпуск сотрудников&quot;. Часть 1, база данных.</title><published>2019-04-05T19:15:31.303Z</published><updated>2019-11-13T14:22:55.594Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://teletype.in/files/23/23dc365c-3538-469f-b974-2b20b4c4432e.jpeg"></media:thumbnail><category term="topic2958" label="Asp.Net"></category><summary type="html">&lt;img src=&quot;https://teletype.in/files/23/23dc365c-3538-469f-b974-2b20b4c4432e.jpeg&quot;&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. </summary><content type="html">
  &lt;hr /&gt;
  &lt;h2&gt;!!НАШ БЛОГ ПЕРЕЕХАЛ!!&lt;/h2&gt;
  &lt;p&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. &lt;/p&gt;
  &lt;h3&gt;Наш новый сайт &lt;a href=&quot;https://maddevelop.ru/&quot; target=&quot;_blank&quot;&gt;maddevelop.ru&lt;/a&gt;&lt;/h3&gt;
  &lt;hr /&gt;
  &lt;p&gt;Сначала сделаем серверную часть проекта. В решении создадим новый проект &lt;strong&gt;&amp;quot;Веб-приложение ASP.NET Core&amp;quot;&lt;/strong&gt;. Далее выберем шаблон &lt;strong&gt;&amp;quot;API&amp;quot;&lt;/strong&gt;.&lt;/p&gt;
  &lt;h2&gt;Создание моделей&lt;/h2&gt;
  &lt;p&gt;У нас будет три таблицы: &lt;strong&gt;&amp;quot;Сотрудники&amp;quot;&lt;/strong&gt;, &lt;strong&gt;&amp;quot;Цвета&amp;quot;&lt;/strong&gt; и &lt;strong&gt;&amp;quot;Отпуски&amp;quot;&lt;/strong&gt;. У сотрудника (&lt;em&gt;employee&lt;/em&gt;) есть идентификатор, имя и идентификатор цвета, которым будет окрашиваться его отпуски в сводной таблице. У цвета (&lt;em&gt;color&lt;/em&gt;) имеются идентификатор и числовой номер, по которому можно будет воссоздать структуру &lt;em&gt;Color&lt;/em&gt;, предназначенную для раскраски чего-либо. Специально не стал хранить название цвета в столбце типа string, ведь использование int позволяет занимать таблице меньше места в памяти. Таблицы &amp;quot;Цвета&amp;quot; и &amp;quot;Сотрудники&amp;quot; связаны отношением &lt;em&gt;&amp;quot;один-ко-многим&amp;quot;&lt;/em&gt;. У отпуска (&lt;em&gt;vacation&lt;/em&gt;) есть обязательный идентификатор, дата начала отпуска, его продолжительность и ключ работника, которому &amp;quot;принадлежит&amp;quot; отпуск. Сотрудники и отпуски связаны отношением &amp;quot;один-ко-многим&amp;quot;. Нулевым может быть только столбец с именем человека, остальные обязательны к заполнению.&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/23/23dc365c-3538-469f-b974-2b20b4c4432e.jpeg&quot; width=&quot;1001&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Для создания базы данных и таблиц в ней будем использовать &lt;em&gt;Entity Framework Core&lt;/em&gt; (далее просто &lt;em&gt;EF&lt;/em&gt;). Подход выберем &lt;em&gt;&amp;quot;code first&amp;quot;&lt;/em&gt;, то есть по нашим классам, объявленным в &lt;em&gt;C#&lt;/em&gt;, &lt;em&gt;EF&lt;/em&gt; создаст базу данных вместе с необходимыми таблицами и связями. Для моделей сделаем отдельную папку &lt;em&gt;Models&lt;/em&gt;.&lt;/p&gt;
  &lt;p&gt;Класс сотрудника:&lt;/p&gt;
  &lt;pre&gt;public class Employee
{
    public int EmployeeId { get; set; }
    public string Name { get; set; }        
    public List&amp;lt;Vacation&amp;gt; Vacations { get; set; }
    public int ColorId { get; set; }
    [JsonIgnore]
    public Color Color { get; set; }
}
&lt;/pre&gt;
  &lt;p&gt;Класс цвета:&lt;/p&gt;
  &lt;pre&gt;public class Color
{
    public int ColorId { get; set; }
    public int ColorNumber { get; set; }
    public List&amp;lt;Employee&amp;gt; Employees { get; set; } 
}
&lt;/pre&gt;
  &lt;p&gt;Класс отпуска:&lt;/p&gt;
  &lt;pre&gt;public class Vacation
{
    public int VacationId { get; set; }
    public DateTime Start { get; set; }
    public int Duration { get; set; }        
    public int EmployeeId { get; set; }
    [JsonIgnore]
    public Employee Employee { get; set; }
}
&lt;/pre&gt;
  &lt;p&gt;Использование свойства &lt;em&gt;Vacations&lt;/em&gt; в классе &lt;strong&gt;Employee&lt;/strong&gt; и свойств &lt;em&gt;EmployeeId&lt;/em&gt; и &lt;em&gt;Employee&lt;/em&gt; в классе &lt;strong&gt;Vacation&lt;/strong&gt; сообщает &lt;em&gt;EF&lt;/em&gt;, что между соответствующими таблицами необходимо создать отношение &lt;em&gt;&amp;quot;один-ко-многим&amp;quot;&lt;/em&gt;. Аналогичным образом создаётся и связь между &lt;em&gt;Color&lt;/em&gt; и &lt;em&gt;Employee&lt;/em&gt;.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;p&gt;Для создания самих таблиц используется класс контекста, где они формируются на основании свойств:&lt;/p&gt;
  &lt;pre&gt;public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)
        : base(options) { }

    public DbSet&amp;lt;Employee&amp;gt; Employees { get; set; }
    public DbSet&amp;lt;Vacation&amp;gt; Vacations { get; set; }
    public DbSet&amp;lt;Color&amp;gt; Colors { get; set; }
}
&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;p&gt;Строку подключения базы данных следует указать в файле &lt;em&gt;appsettings.json&lt;/em&gt;. Если такого файла не в проекте, то его можно создать вручную.&lt;/p&gt;
  &lt;pre&gt;{  
  &amp;quot;ConnectionStrings&amp;quot;: {
    &amp;quot;vacationConnection&amp;quot;: &amp;quot;Server=(localdb)\\mssqllocaldb;Database=vacationsstore;
      Trusted_Connection=True&amp;quot;
  }
} 
&lt;/pre&gt;
  &lt;p&gt;Здесь &lt;em&gt;&amp;quot;vacationsstore&amp;quot;&lt;/em&gt; - имя базы данных, которое можно задать любым.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;p&gt;Создание класса контекста с нужной строкой подключения следует зарегистрировать в провайдере сервисов в методе &lt;em&gt;ConfigureServices()&lt;/em&gt; класса &lt;strong&gt;Startup&lt;/strong&gt;. Сам метод выглядит следующим образом:&lt;/p&gt;
  &lt;pre&gt;public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. 
    // Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        string connection = Configuration.GetConnectionString(&amp;quot;vacationConnection&amp;quot;);
        services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;
            options.UseSqlServer(connection));
        services.AddSingleton&amp;lt;IEmployeeRepository, EFEmployeeRepository&amp;gt;();
        services.AddMvc();            
    }

    // This method gets called by the runtime. 
    // Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseStatusCodePages();
        app.UseDeveloperExceptionPage();
        app.UseStaticFiles();
        app.UseMvcWithDefaultRoute();

        SeedData.EnsurePopulated(app);
    }
}  
&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;p&gt;Также создадим класс с методом, который будет сразу заполнять нашу базу данных начальными значениями.&lt;/p&gt;
  &lt;pre&gt;public static class SeedData
{
    public static void EnsurePopulated(IApplicationBuilder app)
    {
        ApplicationDbContext context = 
            app.ApplicationServices.GetRequiredService&amp;lt;ApplicationDbContext&amp;gt;();
        context.Database.Migrate();
        if (!context.Employees.Any())
        {
            context.Colors.AddRange(
                new Color { ColorNumber = -16777216 },  // чёрный
                new Color { ColorNumber = -16744448 },  // зелёный
                new Color { ColorNumber = -16776961 },  // синий
                new Color { ColorNumber = -65536 },     // красный
                new Color { ColorNumber = -256 },       // жёлтый
                new Color { ColorNumber = -8388480 },   // фиолетовый
                new Color { ColorNumber = -23296 },     // оранжевый
                new Color { ColorNumber = -8355712 },   // серый
                new Color { ColorNumber = -16711936 },  // лайм
                new Color { ColorNumber = -16181 }      // розовый
                );
            context.SaveChanges();

            context.Employees.AddRange(
                new Employee { Name = &amp;quot;Pyotr&amp;quot;, 
                    Color = context.Colors.Skip(0).FirstOrDefault() },
                new Employee { Name = &amp;quot;Sergey&amp;quot;, 
                    Color = context.Colors.Skip(1).FirstOrDefault() },
                new Employee { Name = &amp;quot;Vasiliy&amp;quot;, 
                    Color = context.Colors.Skip(2).FirstOrDefault() }
                );
            context.SaveChanges();

            context.Vacations.AddRange(
                new Vacation { Start = new DateTime(2019, 5, 6), Duration = 7, 
                    Employee = context.Employees.Skip(0).FirstOrDefault() },
                new Vacation { Start = new DateTime(2019, 3, 11), Duration = 7, 
                    Employee = context.Employees.Skip(0).FirstOrDefault() },
                new Vacation { Start = new DateTime(2019, 5, 1), Duration = 28, 
                    Employee = context.Employees.Skip(1).FirstOrDefault() },
                new Vacation { Start = new DateTime(2019, 4, 30), Duration = 7, 
                    Employee = context.Employees.Skip(2).FirstOrDefault() },
                new Vacation { Start = new DateTime(2019, 5, 12), Duration = 7, 
                    Employee = context.Employees.Skip(2).FirstOrDefault() },
                new Vacation { Start = new DateTime(2019, 5, 25), Duration = 7, 
                    Employee = context.Employees.Skip(2).FirstOrDefault() }
                );
            context.SaveChanges();
        }
    }
}
&lt;/pre&gt;
  &lt;p&gt;В первой строчке метода мы получаем класс контекста из провайдера сервисов. Затем производим все необходимые миграции. Если база данных с именем из строки подключения отсутствует, то она создаётся. При пустой таблице Employees мы заполняем все таблицы данными. Метод &lt;em&gt;EnsurePopulated()&lt;/em&gt; следует вызывать в методе &lt;em&gt;Configure()&lt;/em&gt; класса &lt;strong&gt;Startup&lt;/strong&gt;. В результате данные таблиц будут такими:&lt;/p&gt;
  &lt;figure class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/33/33da81f4-3d26-426f-a17c-29860d5b5a71.png&quot; width=&quot;297&quot; /&gt;
  &lt;/figure&gt;
  &lt;figure class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/ad/ad3c2904-4282-4ebf-9bc3-653e39ef05ed.png&quot; width=&quot;399&quot; /&gt;
  &lt;/figure&gt;
  &lt;figure class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/a8/a8e83776-76bb-49cc-87cc-424b408414e3.png&quot; width=&quot;198&quot; /&gt;
  &lt;/figure&gt;
  &lt;hr /&gt;
  &lt;p&gt;И, наконец, в методе &lt;em&gt;CreateWebHostBuilder()&lt;/em&gt; класса &lt;strong&gt;Program&lt;/strong&gt; сконфигурируем провайдер сервисов:&lt;/p&gt;
  &lt;pre&gt;public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =&amp;gt;
        WebHost.CreateDefaultBuilder(args)
            .UseStartup&amp;lt;Startup&amp;gt;()
            .UseDefaultServiceProvider(options =&amp;gt; options.ValidateScopes = false);
}
&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;p&gt;Таким образом, с помощью &lt;em&gt;Entity Framework Core&lt;/em&gt; мы создали базу данных и заполнили её таблицы минимальной информацией. В следующей части определим класс репозитория и класс контроллера.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;p&gt;&lt;strong&gt;&lt;em&gt;Ещё больше интересной информации на нашем &lt;a href=&quot;https://t.me/maddevelop&quot; target=&quot;_blank&quot;&gt;Telegram&lt;/a&gt;-канале.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt; &lt;strong&gt;&lt;em&gt;......... &amp;gt;&amp;gt; &lt;a href=&quot;https://blog.maddevelop.ru/r1vy4votN&quot; target=&quot;_blank&quot;&gt;К части 2&lt;/a&gt; &amp;gt;&amp;gt;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</content></entry><entry><id>maddevelop:B1ru3VbtN</id><link rel="alternate" type="text/html" href="https://teletype.in/@maddevelop/B1ru3VbtN?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=maddevelop"></link><title>Немного о REST Web API</title><published>2019-04-03T17:38:06.535Z</published><updated>2019-11-13T14:22:41.013Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://teletype.in/files/98/981f50d3-1f39-4bcd-8619-e679932cc0e7.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://teletype.in/files/98/981f50d3-1f39-4bcd-8619-e679932cc0e7.png&quot;&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. </summary><content type="html">
  &lt;hr /&gt;
  &lt;h2&gt;!!НАШ БЛОГ ПЕРЕЕХАЛ!!&lt;/h2&gt;
  &lt;p&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. &lt;/p&gt;
  &lt;h3&gt;Наш новый сайт &lt;a href=&quot;https://maddevelop.ru/&quot; target=&quot;_blank&quot;&gt;maddevelop.ru&lt;/a&gt;&lt;/h3&gt;
  &lt;hr /&gt;
  &lt;p&gt;&lt;strong&gt;API&lt;/strong&gt; (англ. &lt;em&gt;application programming interface&lt;/em&gt; - &lt;strong&gt;&amp;quot;программный интерфейс приложения&amp;quot;&lt;/strong&gt;) - описание способов (набор классов, процедур, функций, структур или констант), которыми одна компьютерная программа может взаимодействовать с другой программой. API определяет функциональность, которую предоставляет программа (модуль, библиотека). При этом API позволяет абстрагироваться от того, как именно эта функциональность реализована. Если программный интерфейс приложения написан для веб-сервера или веб-браузера, то мы имеем дело с &lt;strong&gt;Web API&lt;/strong&gt;.&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Веб-служба&lt;/strong&gt;, веб-сервис - идентифицируемая уникальным веб-адресом программная система со стандартизованными интерфейсами. Веб-службы могут взаимодействовать друг с другом и со сторонним приложениями посредством сообщений, основанных на определённых протоколах или соглашениях (REST). Веб-служба является единицей модульности при использовании &lt;em&gt;сервис-ориентированной архитектуры&lt;/em&gt; приложения. Использование подобного построения обеспечивает комбинирование и многократное использование компонентов для создания сложных распределённых программных комплексов, обеспечивая независимость от используемых платформ и инструментов разработки, способствуя &lt;em&gt;масштабируемости&lt;/em&gt; и &lt;em&gt;управляемости&lt;/em&gt; создаваемых систем.&lt;/p&gt;
  &lt;figure class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/98/981f50d3-1f39-4bcd-8619-e679932cc0e7.png&quot; width=&quot;813&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;&lt;strong&gt;REST&lt;/strong&gt; (англ. representational state transfer - &lt;strong&gt;&amp;quot;передача состояния представления&amp;quot;&lt;/strong&gt;) - это стиль архитектуры программного обеспечения для распределённых систем, таких как World Wide Web, который часто используется для построения веб-служб. В сети Интернет вызов удалённой процедуры может представлять собой обычный HTTP-запрос (обычно «GET» или «POST»; такой запрос называют &lt;em&gt;«REST-запрос»&lt;/em&gt;), а необходимые данные передаются в качестве параметров запроса. Для веб-служб, построенных с учётом REST (то есть не нарушающих накладываемых им ограничений), применяют термин «RESTful».&lt;/p&gt;
  &lt;p&gt;В отличие от веб-сервисов на основе SOAP, не существует «официального» стандарта для RESTful веб-API. Дело в том, что REST является архитектурным стилем, в то время как SOAP является протоколом. Несмотря на то, что REST не является стандартом сам по себе, большинство RESTful-реализаций используют стандарты, такие как HTTP, URL, JSON и XML.&lt;/p&gt;
  &lt;p&gt;Требования (&lt;a href=&quot;https://ru.wikipedia.org/wiki/REST&quot; target=&quot;_blank&quot;&gt;здесь&lt;/a&gt; подробнее) к архитектуре REST:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;модель клиент-сервер;&lt;/li&gt;
    &lt;li&gt;отсутствие состояния;&lt;/li&gt;
    &lt;li&gt;кэширование;&lt;/li&gt;
    &lt;li&gt;единообразие интерфейса;&lt;/li&gt;
    &lt;li&gt;слои;&lt;/li&gt;
    &lt;li&gt;код по требованию.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p&gt;P.S. Информация почти целиком взята из Википедии, данная заметка создана для понимания REST Web API без перехода по нескольким ссылкам.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;p&gt;&lt;strong&gt;&lt;em&gt;Ещё больше интересной информации на нашем &lt;a href=&quot;https://t.me/maddevelop&quot; target=&quot;_blank&quot;&gt;Telegram&lt;/a&gt;-канале.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</content></entry><entry><id>maddevelop:H1uQgos_E</id><link rel="alternate" type="text/html" href="https://teletype.in/@maddevelop/H1uQgos_E?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=maddevelop"></link><title>Способы запуска Telegam бота через Proxy. Продолжаем создавать чат-бот на .Net Core</title><published>2019-03-29T18:11:41.349Z</published><updated>2019-11-13T14:22:10.491Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://teletype.in/files/2d/2d3af411-148b-46fc-94b3-da4aa0a11927.jpeg"></media:thumbnail><summary type="html">&lt;img src=&quot;https://teletype.in/files/2d/2d3af411-148b-46fc-94b3-da4aa0a11927.jpeg&quot;&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. </summary><content type="html">
  &lt;hr /&gt;
  &lt;h2&gt;!!НАШ БЛОГ ПЕРЕЕХАЛ!!&lt;/h2&gt;
  &lt;p&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. &lt;/p&gt;
  &lt;h3&gt;Наш новый сайт &lt;a href=&quot;https://maddevelop.ru/&quot; target=&quot;_blank&quot;&gt;maddevelop.ru&lt;/a&gt;&lt;/h3&gt;
  &lt;hr /&gt;
  &lt;p&gt;В продолжении &lt;a href=&quot;https://blog.maddevelop.ru/Hk7-T-Ed4&quot; target=&quot;_blank&quot;&gt;статьи&lt;/a&gt; рассмотри варианты запуска бота через &lt;strong&gt;Proxy&lt;/strong&gt; сервера.&lt;/p&gt;
  &lt;h2&gt;Вариант 1. Использование Tor браузера.&lt;/h2&gt;
  &lt;p&gt;Этот способ был найден в &lt;a href=&quot;https://telegrambots.github.io/book/1/example-bot.html&quot; target=&quot;_blank&quot;&gt;описании&lt;/a&gt; к библиотеке Telegram.Bot. Он подходит скорее для отладки и для тестирования нежели, чем для релиза. Неудобство состоит в том, что во время работы бота должен быть всегда открыт браузер Tor.&lt;/p&gt;
  &lt;p&gt;При создании бота при помощи пакета Telegram.Bot нам нужно создавать объект клиента&lt;/p&gt;
  &lt;pre&gt;TelegramBotClient client = new TelegramBotClient(token, webProxy)
&lt;/pre&gt;
  &lt;p&gt;Конструктор объекта первым параметром принимаем токен бота, а вторым объект класса &lt;strong&gt;HttpToSocks5Proxy&lt;/strong&gt;. Этот класс находится в другой библиотеке (он создан одним из членов команды Telegram.Bot), распространяется она так же в формате NuGet пакета. Чтобы ее скачать заходим в пакетный менеджер, находим &lt;strong&gt;HttpToSocks5Proxy &lt;/strong&gt;и добавляем к себе в проект.&lt;/p&gt;
  &lt;p&gt;Еще нам нужно настроить наш Tor браузер. Для этого заходим по пути:&lt;/p&gt;
  &lt;pre&gt;...(Путь до папки с браузером)...\Browser\TorBrowser\Data\Tor\
&lt;/pre&gt;
  &lt;p&gt;Там мы находим файл torrc, его нужно открыть в обычном текстовом редакторе и добавить строки&lt;/p&gt;
  &lt;pre&gt;EntryNodes {NL}
ExitNodes {NL}
StrictNodes 1
SocksPort 127.0.0.1:9050
&lt;/pre&gt;
  &lt;p&gt;После этого мы получаем возможность создавать объект клиента бота с новыми параметрами, а именно&lt;/p&gt;
  &lt;pre&gt;TelegramBotClient client = new TelegramBotClient(&amp;lt;token&amp;gt;, new HttpToSocks5Proxy(&amp;quot;127.0.0.1&amp;quot;, 9050))
&lt;/pre&gt;
  &lt;p&gt;Теперь весь трафик от бота будет перенаправляться через Tor, и ошибок с подключением не возникнет.&lt;/p&gt;
  &lt;p&gt;Способ проверенный, рабочий, сами им пользуемся.&lt;/p&gt;
  &lt;h2&gt;Вариант 2. Настройка VPN (L2TP/IPsec) на OC.&lt;/h2&gt;
  &lt;p&gt;Данный вариант это первое что приходит на ум, когда нужно перенаправить весь трафик через VPN на компьютере. Описание нашлось на &lt;a href=&quot;https://www.comss.ru/page.php?id=4077&quot; target=&quot;_blank&quot;&gt;просторах интернета&lt;/a&gt; и тоже оказалось рабочим.&lt;/p&gt;
  &lt;p&gt;Рассмотрим на примере ОС Windows 7. Заходим в &amp;quot;Центр управления сетями и общим доступом&amp;quot;&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/2d/2d3af411-148b-46fc-94b3-da4aa0a11927.jpeg&quot; width=&quot;1200&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Нажимаем &amp;quot;Настройки нового подключения или сети&amp;quot;&lt;/p&gt;
  &lt;figure class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/0a/0ad7b70d-ab1b-49e2-8daf-531d160ef6ff.jpeg&quot; width=&quot;625&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Выбираем &amp;quot;Подключение к рабочему месту&amp;quot;&lt;/p&gt;
  &lt;figure class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/23/237e6327-56d8-4e08-a771-756a1107e4ec.jpeg&quot; width=&quot;628&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;&amp;quot;Использовать мое подключение к Интернету (VPN)&amp;quot;&lt;/p&gt;
  &lt;figure class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/39/39d08637-35ec-499e-a838-2f6743140b6a.jpeg&quot; width=&quot;628&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Здесь вводим DDNS hostname, который находим на сайте &lt;a href=&quot;https://www.vpngate.net/en/&quot; target=&quot;_blank&quot;&gt;VpnGate&lt;/a&gt;. Главное выбрать тот сервер у которого есть поддержка L2TP/IPsec. Т.е. в соответствующей колонке должна стоять галочка.&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/5a/5ac7a907-2b04-4366-81cc-fa4fed53b1f0.png&quot; width=&quot;1276&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Далее, после введения адреса система предлагает ввести имя пользователя и пароль. В обе эти графы вводим &lt;strong&gt;vpn &lt;/strong&gt;и нажимаем далее (Ставим галочку запомнить этот пароль).&lt;/p&gt;
  &lt;figure class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/01/01181b19-3562-46e6-a94d-1aa760a0d5bc.jpeg&quot; width=&quot;624&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;На этом моменте мастер настройки создаем новое подключение которое уже находим в разделе &amp;quot;Изменение параметров адаптера&amp;quot; (Сетевые подключения). Открываем свойства нового vpn подключения и заходим на вкладку &amp;quot;безопасность&amp;quot;.&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/11/11671f94-0f04-4ee4-aa09-4604bbfdaf8b.jpeg&quot; width=&quot;1200&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Здесь выбираем параметры как указано на рисунке, и в подменю &amp;quot;Дополнительные параметры&amp;quot;.&lt;/p&gt;
  &lt;figure class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/88/88dc848b-95fd-4da2-8067-5c18679720f6.jpeg&quot; width=&quot;376&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;В поле &amp;quot;ключ&amp;quot; вводим vpn.&lt;/p&gt;
  &lt;figure class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/c7/c759e39d-9f3b-4e50-81a1-5f1e7e10855c.jpeg&quot; width=&quot;414&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Все, мы полностью настроили новое подключение. Теперь прост его можно запустить и трафик пойдет через внешние сервера.&lt;/p&gt;
  &lt;p&gt;Этот способ тоже проверенный и рабочий. Но если у Вас не будет подключаться, то стоит попробовать другие варианты DDNS адреса.&lt;/p&gt;
  &lt;h2&gt;Вариант 3. Использование сервиса Ngrok.&lt;/h2&gt;
  &lt;p&gt;Он подходит для тех ботов, которые обновляют свое состояние через WebHook. Вообще данный вариант обновления более предпочтителен, нежели простой опрос сервера по таймеру. Таким образом снижается нагрузка на сервера Telegram.&lt;/p&gt;
  &lt;p&gt;Но опять же есть сложности. Сам сервис Ngrok тоже запрещен в России, поэтому его можно использовать в совокупности со вторым вариантом.&lt;/p&gt;
  &lt;p&gt;Чтобы воспользоваться Ngrok нужно зарегистрироваться на &lt;a href=&quot;https://ngrok.com/&quot; target=&quot;_blank&quot;&gt;сайте&lt;/a&gt; и получить персональный ключ. Далее устанавливаем ngrok.exe, заходим в командную строку и вводим&lt;/p&gt;
  &lt;pre&gt;ngrok authtoken 
&lt;/pre&gt;
  &lt;p&gt;ключ Ngrok. Тем самым сгененируется файл, который будет содержать наш сгенерированный ключ, это процедуру нужно выполнить один раз.&lt;/p&gt;
  &lt;p&gt;А дальше для трансляции нашего localhols сервиса в мировую паутину выполняем команду&lt;/p&gt;
  &lt;pre&gt;ngrok http портсервиса
&lt;/pre&gt;
  &lt;p&gt;В поле же адреса, к которому будет привязан WebHook вводим сгененированный программой https адрес&lt;/p&gt;
  &lt;figure class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/fd/fd51f996-4dce-48ec-8ace-1538aaa9ffdd.png&quot; width=&quot;603&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;(фото взято из &lt;a href=&quot;https://habr.com/ru/company/alfa/blog/343846/&quot; target=&quot;_blank&quot;&gt;статьи&lt;/a&gt;, там тоже вкратце писан процесс подключения)&lt;/p&gt;
  &lt;p&gt;Мы этот вариант более подробно рассмотрим в следующих статьях на конкретном примере, когда будем создавать бота, с применением такого метода обновления.&lt;/p&gt;
  &lt;p&gt;Ну вот все, надеемся, что статья была полезной. Следите за обновлениями, ваша команда MadDevelopTeam.&lt;/p&gt;
  &lt;p&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;hr /&gt;
  &lt;p&gt;&lt;strong&gt;&lt;em&gt;Ещё больше интересной информации на нашем &lt;a href=&quot;https://t.me/maddevelop&quot; target=&quot;_blank&quot;&gt;Telegram&lt;/a&gt;-канале.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</content></entry><entry><id>maddevelop:Hk7-T-Ed4</id><link rel="alternate" type="text/html" href="https://teletype.in/@maddevelop/Hk7-T-Ed4?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=maddevelop"></link><title>История о том, как мы создавали чат бот для Telegram.</title><published>2019-03-26T15:30:24.507Z</published><updated>2019-11-13T14:21:54.895Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://teletype.in/files/bf/bf982ad5-cb9e-4ca7-9a12-0d8e58a37a65.jpeg"></media:thumbnail><summary type="html">&lt;img src=&quot;https://teletype.in/files/bf/bf982ad5-cb9e-4ca7-9a12-0d8e58a37a65.jpeg&quot;&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. </summary><content type="html">
  &lt;hr /&gt;
  &lt;h2&gt;!!НАШ БЛОГ ПЕРЕЕХАЛ!!&lt;/h2&gt;
  &lt;p&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. &lt;/p&gt;
  &lt;h3&gt;Наш новый сайт &lt;a href=&quot;https://maddevelop.ru/&quot; target=&quot;_blank&quot;&gt;maddevelop.ru&lt;/a&gt;&lt;/h3&gt;
  &lt;hr /&gt;
  &lt;p&gt;В общем, все началось с того, что появилась идея создать бот, чтобы Вы, наши подписчики, смогли протестировать свои знания по языкам программирования. Проще говоря, бот должен содержать в себе различного рода тренировочные задания. Но мы столкнулись с некоторыми сложностями.&lt;/p&gt;
  &lt;figure class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/bf/bf982ad5-cb9e-4ca7-9a12-0d8e58a37a65.jpeg&quot; width=&quot;1920&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Первый вариант статьи должен был содержать описание погодного бота, созданного на основе ASP.Net Core Web API, но мы столкнулись с тем, что бот просто так не запустился... В связи с тем, что у нас в стране полным ходом (нет) идет блокировка сервиса Telegram, достучаться до него получится только с помощью VPN. И поэтому приняли решение описать все с самого начала. Постараемся по максимуму рассказать о тех подводных камнях, которые можно встретить во время разработки бот&lt;/p&gt;
  &lt;p&gt;Информацию искали в самых различных источниках, очень помог официальный &lt;a href=&quot;https://telegrambots.github.io/book/1/quickstart.html&quot; target=&quot;_blank&quot;&gt;мануал&lt;/a&gt; от команды Telegram.&lt;/p&gt;
  &lt;p&gt;Начнем с самого начала, а именно с регистрации нового бота. Поехали!&lt;/p&gt;
  &lt;h2&gt;Регистрация нового бота.&lt;/h2&gt;
  &lt;p&gt;Создаем бота через другого бота (вот такая вот загогулина :D ). Для этого находим в поиске &lt;strong&gt;&lt;em&gt;@BotFather&lt;/em&gt;&lt;/strong&gt; и добавляем его к себе в чаты.&lt;/p&gt;
  &lt;figure class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/47/475aaddf-fc4b-4239-a9d9-2e8f38945fb0.jpeg&quot; width=&quot;243.5&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Далее отправляем &lt;em&gt;/start&lt;/em&gt;, он выдает весь список возможных команд. Нам нужна команда &lt;em&gt;/newbot&lt;/em&gt;. Он предложит нам выбрать название бота и id, с помощью которого можно будет его найти.&lt;/p&gt;
  &lt;p&gt;После выбора этих параметров BotFather отправит сообщение, в котором будет поздравление с созданием нового бота и &lt;em&gt;(самое главное)&lt;/em&gt; &lt;strong&gt;токен&lt;/strong&gt; нашего бота, его то мы и будем использовать.&lt;/p&gt;
  &lt;figure class=&quot;m_custom&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/cb/cb9a1bcf-8053-459b-8dcb-c439cecdbe9e.jpeg&quot; width=&quot;305&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;После можно найти своего бота по id и произвести дополнительную настройку, добавить описание и аватарку.&lt;/p&gt;
  &lt;blockquote&gt;Все программы будут создаваться на C# и иногда будем применять технологию .Net Core. В качестве среды разработки бы выбран VS Code (Просто у нас несколько ноутбуков, один из них на Ubuntu и там стоит Code. В целом очень даже хороший редактор кода. Обязательно сделаем статью по некоторым основам работы с этой программой)&lt;/blockquote&gt;
  &lt;h2&gt;Проверяем работоспособность на простейшей программе.&lt;/h2&gt;
  &lt;p&gt;Создадим папку (Например Example 1) и откроем ее в VS Code. Через терминал введем команду&lt;/p&gt;
  &lt;pre&gt;dotnet new console
&lt;/pre&gt;
  &lt;p&gt;тем самым у нас создастся новый консольный проект, который содержит только файл Program.cs.&lt;/p&gt;
  &lt;p&gt;Через диспетчер NiGet установим в наш проект пакет Telegram.Bot. В файле Program.cs добавим с помощью директивы #using&lt;/p&gt;
  &lt;pre&gt;using Telegram.Bot;
using Telegram.Bot.Args;
&lt;/pre&gt;
  &lt;p&gt;И в классе Programm напишем такую программу.&lt;/p&gt;
  &lt;pre&gt;class Program {
    static ITelegramBotClient botClient;

    static void Main() {
      botClient = new TelegramBotClient(&amp;quot;&amp;lt;Token&amp;gt;&amp;quot;);

      var me = botClient.GetMeAsync().Result;
      Console.WriteLine(
        $&amp;quot;Привет всем! Я пользователь {me.Id} и меня зовут {me.FirstName}.&amp;quot;
      );
    }
  }
&lt;/pre&gt;
  &lt;p&gt;Программа просто создает объект интерфейса ITelegramBotClient и с помощью метода&lt;/p&gt;
  &lt;pre&gt;botClient.GetMeAsync()
&lt;/pre&gt;
  &lt;p&gt;Получаем свойство Result, которое содержит всякого рода информацию о нашем боте.&lt;/p&gt;
  &lt;p&gt;Но возможно, даже на этом этапе у Вас выскочит Exception. Он скажет о том, что клиент не смог получить доступ к удаленному серверу за обозримое время, а это значит, что результатов Вы не получите. Для решения этой проблемы можно использовать VPN.&lt;/p&gt;
  &lt;p&gt;О том, как это все сделать мы расскажем в следующей статье из этой серии. Приведем несколько разных примеров, как это реализовать, а Вы выберете самый удобный для себя.&lt;/p&gt;
  &lt;p&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;hr /&gt;
  &lt;p&gt;&lt;strong&gt;&lt;em&gt;Ещё больше интересной информации на нашем &lt;a href=&quot;https://t.me/maddevelop&quot; target=&quot;_blank&quot;&gt;Telegram&lt;/a&gt;-канале.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</content></entry><entry><id>maddevelop:H1cQnnq84</id><link rel="alternate" type="text/html" href="https://teletype.in/@maddevelop/H1cQnnq84?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=maddevelop"></link><title>Отзыв о митапе №2 GetIT Community, часть вторая</title><published>2019-03-05T17:40:27.109Z</published><updated>2019-11-13T14:21:37.853Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://teletype.in/files/9b/9bd212ce-deb1-46f2-813e-307ce0cacec5.jpeg"></media:thumbnail><summary type="html">&lt;img src=&quot;https://teletype.in/files/9b/9bd212ce-deb1-46f2-813e-307ce0cacec5.jpeg&quot;&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. </summary><content type="html">
  &lt;hr /&gt;
  &lt;h2&gt;!!НАШ БЛОГ ПЕРЕЕХАЛ!!&lt;/h2&gt;
  &lt;p&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. &lt;/p&gt;
  &lt;h3&gt;Наш новый сайт &lt;a href=&quot;https://maddevelop.ru/&quot; target=&quot;_blank&quot;&gt;maddevelop.ru&lt;/a&gt;&lt;/h3&gt;
  &lt;hr /&gt;
  &lt;p&gt;После &lt;a href=&quot;https://blog.maddevelop.ru/ryrJ1hFU4&quot; target=&quot;_blank&quot;&gt;трёх докладов&lt;/a&gt; был объявлен перерыв, во время которого можно было пообщаться, перекусить пирогами или отправиться на экскурсию по офису &amp;quot;Мегафона&amp;quot;. Я выбрал последнее, и вот несколько фотографий оттуда.&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/9b/9bd212ce-deb1-46f2-813e-307ce0cacec5.jpeg&quot; width=&quot;2441&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Каждый этаж имеет название одного из городов России и оформлен соответствующе.&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/ee/ee2b2910-f41e-4d2d-b2ae-fb9969316ae8.jpeg&quot; width=&quot;2153&quot; /&gt;
  &lt;/figure&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/38/38354a4c-546e-4a3d-acc1-9555ac75982d.jpeg&quot; width=&quot;5120&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Вкусный, как говорят, комплексный обед стоит 210 рублей.&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/ef/efb43018-ec0e-4491-afa1-fe4a4fe62a86.jpeg&quot; width=&quot;5120&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Если сотрудник задерживается до 23-00, то такси домой для него оплачивает компания.&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/d0/d07b77d9-3d5b-492d-aabf-2919b07e1619.jpeg&quot; width=&quot;5120&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;После возвращения началось четвёртое и снова сдвоенное выступление &lt;em&gt;&amp;quot;Зачем разработчику персональный бренд&amp;quot;&lt;/em&gt;. Докладчиками были &lt;strong&gt;Антон Пальгунов&lt;/strong&gt; (Senior Frontend Engineer, говорил по скайпу) и &lt;strong&gt;Юлия Великанова&lt;/strong&gt; (Employer Brand Manager), оба из банка &lt;strong&gt;&amp;quot;Revolut&amp;quot;&lt;/strong&gt;.&lt;/p&gt;
  &lt;p&gt;Рассказ начался с примеров сильных персональных брендов: Стив Джобс (который прочно ассоциируется с &amp;quot;Apple&amp;quot;), Билл Гейтс (&amp;quot;Microsoft&amp;quot;), Марк Цукерберг (&amp;quot;Facebook&amp;quot;), Дэн Абрамов (redux/react frontend &amp;quot;Facebook&amp;quot;) и Андрей Бреслав (&amp;quot;Kotlin&amp;quot;).&lt;/p&gt;
  &lt;p&gt;Увеличивать собственное влияние можно двумя способами: в сообществе и в компании. Если ты активно публикуешься, к примеру, на хабре (habr.com), твои статьи собирают множество лайков, а под ними оставляют множество комментариев, то твоя популярность растёт и вскоре тебя могут приглашать выступать на различных мероприятиях. Подобного можно достичь, создавая значительные проекты в крупных и известных компаниях. Почему в крупных? Потому что достижения в таких фирмах на виду, об их авторах быстро начинают говорить.&lt;/p&gt;
  &lt;p&gt;Так как для поддержания личного бренда необходимо говорить своим почитателям о злободневных вещах, то тебе следует всегда быть &amp;quot;на гребне волны&amp;quot;. Это приводит к росту твоих технических навыков и личностному росту, что способствует продвижению по карьерной лестнице. Таким образом происходит нематериальная отдача от развития своего бренда.&lt;/p&gt;
  &lt;p&gt;Что же сильно мешает заниматься своим продвижением? Несколько заблуждений. Первое - &amp;quot;всё написали за меня&amp;quot;. Да, в интернете море разнообразной информации, но она не всеобъемлюща. Есть вещи, до описания которых у многих людей не дошли руки, а что-то новое постоянно появляется с развитием IT-индустрии. Нужно обязательно пробовать искать эти незанятые ниши. Второе заблуждение заключается в том, не надо писать о личном. Все люди разные, что-то ими забывается, поэтому не нужно ждать каких-то чудес в своей жизни, а по-тихоньку начинать о ней рассказывать. И только тогда жизнь преобразится.&lt;/p&gt;
  &lt;p&gt;Приступая к созданию или продвижению личного бренда, следует определиться с целью этого процесса. Цель будет хорошим мотиватором.&lt;/p&gt;
  &lt;p&gt;Также рекомендуется знать ответы на следующие вопросы, чтобы добиться успеха:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;В чём я эксперт?&lt;/li&gt;
    &lt;li&gt;О чём я могу рассказывать?&lt;/li&gt;
    &lt;li&gt;Кто ещё об этом рассказывает?&lt;/li&gt;
    &lt;li&gt;Где и как они это делают?&lt;/li&gt;
    &lt;li&gt;В чём их сильные и слабые стороны?&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p&gt;Ответы на эти вопросы помогут определить аудиторию и площадку, где люди смогут получать твой контент. Таких мест существует множество: социальные сети, мессенджеры, блоги, влоги, СМИ и прочие. Кроме того, для каждого канала следует собирать и анализировать статистику. Это значительно поможет скорректировать свои усилия по продвижению персонального бренда.&lt;/p&gt;
  &lt;p&gt;Собственное продвижение - это серьёзная работа. Не следует отделять себя от своего бренда, так как эта фальшь будет чувствоваться. В самом начале нужно понимать: зачем тебе это, готов ли ты работать над личным брендом, какой первый шаг можешь сделать.&lt;/p&gt;
  &lt;p&gt;И закрывался митап выступлением &lt;strong&gt;Виталия Шароватова&lt;/strong&gt; (Ex Head of Front-end Badoo) &lt;em&gt;«Как тим-лиду выстроить эффективную коммуникацию в команде».&lt;/em&gt; Признаться понять его было не просто, так как для понимания его доклада необходимо знать устройство компании для разработки какого-либо продукта. Ясно было, что коммуникацию между разными сотрудниками можно формализовать и представить в виде загадочной таблицы RACI.&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/81/81b86a12-3c7d-428d-bd74-ddf68a4f8e84.jpeg&quot; width=&quot;1179&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Также он напирал на то, чтобы каждый понимал, стоящие перед ним задачи. А для этого подойдёт &amp;quot;продажа&amp;quot; предложения в обратном направлении. То есть человек, от которого требуют выполнение какой-либо работы, пересказывает другому её содержание.&lt;/p&gt;
  &lt;h2&gt;Заключение&lt;/h2&gt;
  &lt;p&gt;Митап вышел очень насыщенным - целых пять докладчиков. Каждый из которых рассказывал о совершенно разных темах. Особенно, конечно, понравилось выступление о создании бренда, ведь это то, что мы, &lt;strong&gt;MadDevelop&lt;/strong&gt;, пытаемся создать. Стоит также отметить и отличную организацию мероприятия. Прекрасно, что встречи происходит в интересных офисах известных компаний.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;p&gt;&lt;em&gt;Для тех, кто пропустил &lt;a href=&quot;https://blog.maddevelop.ru/ryrJ1hFU4&quot; target=&quot;_blank&quot;&gt;первую&lt;/a&gt; часть.&lt;/em&gt;&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;&lt;em&gt;Ещё больше интересной информации на нашем &lt;a href=&quot;https://t.me/maddevelop&quot; target=&quot;_blank&quot;&gt;Telegram&lt;/a&gt;-канале.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</content></entry><entry><id>maddevelop:ryrJ1hFU4</id><link rel="alternate" type="text/html" href="https://teletype.in/@maddevelop/ryrJ1hFU4?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=maddevelop"></link><title>Отзыв о митапе №2 GetIT Community, часть первая</title><published>2019-03-03T19:45:33.205Z</published><updated>2019-11-13T14:21:22.376Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://avatars.mds.yandex.net/get-zen_doc/59126/pub_5c7bef29aa86a600b2b138c3_5c7bf4c77cf2bf00b2fc634a/orig"></media:thumbnail><summary type="html">&lt;img src=&quot;https://avatars.mds.yandex.net/get-zen_doc/59126/pub_5c7bef29aa86a600b2b138c3_5c7bf4c77cf2bf00b2fc634a/orig&quot;&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. </summary><content type="html">
  &lt;hr /&gt;
  &lt;h2&gt;!!НАШ БЛОГ ПЕРЕЕХАЛ!!&lt;/h2&gt;
  &lt;p&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. &lt;/p&gt;
  &lt;h3&gt;Наш новый сайт &lt;a href=&quot;https://maddevelop.ru/&quot; target=&quot;_blank&quot;&gt;maddevelop.ru&lt;/a&gt;&lt;/h3&gt;
  &lt;hr /&gt;
  &lt;p&gt;Два дня назад, в пятницу, 1 марта, состоялся второй митап сообщества разработчиков России GetIT. Встреча происходила в Москве, в офисе &amp;quot;&lt;strong&gt;Мегафон&lt;/strong&gt;&amp;quot; на Садовой-Каретной. Здание, которое &amp;quot;Мегафон&amp;quot; делит пополам со &amp;quot;Сбербанком&amp;quot;, значительно повлияло на общий вид центральной части Москвы, поэтому интересно было оказаться там. К сожалению, в самую верхнюю часть, где располагается вроде бы смотровая площадка, не было возможности попасть. Но об этом позже.&lt;/p&gt;
  &lt;p&gt;Мероприятие должно было начаться в 18-40, в здании я был в 18-25. Удалось десять минут перекинуться с товарищем, который в &amp;quot;Мегафоне&amp;quot; же и работает. В 18-40 я вбежал в зал, ожидая, что попадаю на самое начало митапа, но его отложили минут на 15, поэтому можно было спокойно поесть пирогов. Помещение для митапа было раза в два больше, чем в &lt;em&gt;tutu.ru&lt;/em&gt;, где проходила первая встреча. Количество слушателей увеличилось соответственно. Мне показалось, что в зале находилось человек сто.&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://avatars.mds.yandex.net/get-zen_doc/59126/pub_5c7bef29aa86a600b2b138c3_5c7bf4c77cf2bf00b2fc634a/orig&quot; width=&quot;2284&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Первым выступал &lt;strong&gt;Евгений Сугробов&lt;/strong&gt; (HR-директор Banki.ru) с докладом &lt;em&gt;&amp;quot;Как оценивать софт скилы при найме людей в команду&amp;quot;&lt;/em&gt;. Выступление было лаконичным, но сама тема с софт скилами достаточно избита, и что-то новое почерпнуть из него было сложно. Когда пришло время вопросов зрителей, то стало веселее, так как видно, что Евгений - человек с большим опытом и ему есть что рассказать. Для себя безрадостно отметил его слова о том, что при найме начинающих разработчиков больше внимания стоит обращаться на имеющиеся у них навыки программирования, чем на черты характера. Софт скилы начинают выходить на первый план после достижения программистом уровня уверенного мидла.&lt;/p&gt;
  &lt;p&gt;Второй рассказчик - &lt;strong&gt;Дарья Афанасьева&lt;/strong&gt; из &amp;quot;Skyeng&amp;quot; с темой &lt;em&gt;&amp;quot;Как выстроить работу с удалённой командой&amp;quot;&lt;/em&gt;. В её коллективе из десяти человек восемь работают удалённо. Она описала, как проходит их рабочий день, какие программы используют для организации рабочего процесса и поддержания связи. Для контроля проделанной работы каждый отписывается о своих успехах и планах боту. Общение между ними происходит в чатах нескольких мессенджеров. Если с каким-то мессенджером начинаются проблемы, то они легко переходят на использование другого. Для укрепления командного духа её сотрудники иногда встречаются и проводят вместе отпуск.&lt;/p&gt;
  &lt;p&gt;Автором третьей презентации &lt;em&gt;&amp;quot;Тим-лид как Заказчик, как строить работу с HR и давать фидбеки по подбору специалистов в команду&amp;quot;&lt;/em&gt; были две барышни из &amp;quot;Мегафона&amp;quot;: &lt;strong&gt;Мария Трофимова&lt;/strong&gt; (Руководитель по архитектуре и интеграционным решениям корпоративного хранилища данных) и &lt;strong&gt;Светлана Стаканова&lt;/strong&gt; (HR). Доклад начался с любопытных слайдов о корпоративном хранилище данных (КХД) &amp;quot;Мегафона&amp;quot;.&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://avatars.mds.yandex.net/get-zen_doc/1329105/pub_5c7bef29aa86a600b2b138c3_5c7c019564fb5100b3167ac8/orig&quot; width=&quot;1921&quot; /&gt;
  &lt;/figure&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://avatars.mds.yandex.net/get-zen_doc/108872/pub_5c7bef29aa86a600b2b138c3_5c7c01a82248a400aff21892/orig&quot; width=&quot;1991&quot; /&gt;
  &lt;/figure&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://avatars.mds.yandex.net/get-zen_doc/1222384/pub_5c7bef29aa86a600b2b138c3_5c7c01b57dccd000afeadedf/orig&quot; width=&quot;1970&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;После краткого изложения технических данных КХД Мария рассказала о задачах, стоящих перед её подразделением.&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://avatars.mds.yandex.net/get-zen_doc/1538903/pub_5c7bef29aa86a600b2b138c3_5c7c0396ae21cb00d49db463/orig&quot; width=&quot;2019&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Далее в форме &amp;quot;вопрос - ответ&amp;quot; был организован диалог между Марией и Светланой. Признаться, вопросы были общего характера и несодержательны. Из них интересного я только узнал, что собеседование проходит в два этапа: разговор с HR и техническое, с Марией. Техническая часть заключается в размышлениях кандидата о том, как бы он решил поставленную задачу, как реализовывал собственные проекты. Никаких справочных вопросов не задаётся. Либо сразу, либо на следующий день соискателю сообщается результат собеседования.&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://avatars.mds.yandex.net/get-zen_doc/153162/pub_5c7bef29aa86a600b2b138c3_5c7c058f0a096c00b36329e3/orig&quot; width=&quot;2012&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Необычный вопрос в конце доклада задали про соискателей, ранее судимых! Светлана логично на него ответила, что если преступление было не жуткое, то проблем у человека с наймом быть не должно.&lt;/p&gt;
  &lt;p&gt;&lt;em&gt;Продолжение следует...&lt;/em&gt;&lt;/p&gt;
  &lt;hr /&gt;
  &lt;p&gt;&lt;strong&gt;&lt;em&gt;Ещё больше интересной информации на нашем &lt;a href=&quot;https://t.me/maddevelop&quot; target=&quot;_blank&quot;&gt;Telegram&lt;/a&gt;-канале.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</content></entry><entry><id>maddevelop:BJe0LyUHE</id><link rel="alternate" type="text/html" href="https://teletype.in/@maddevelop/BJe0LyUHE?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=maddevelop"></link><title>ASP.NET MVC 5. Создание сайта-блога. Часть 3.</title><published>2019-02-28T10:31:15.028Z</published><updated>2019-11-13T14:17:51.671Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://teletype.in/files/a4/a4d744ca-d4a0-4e4b-b333-91f1cb464faf.png"></media:thumbnail><category term="topic2958" label="Asp.Net"></category><summary type="html">&lt;img src=&quot;https://teletype.in/files/a4/a4d744ca-d4a0-4e4b-b333-91f1cb464faf.png&quot;&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. </summary><content type="html">
  &lt;hr /&gt;
  &lt;h2&gt;!!НАШ БЛОГ ПЕРЕЕХАЛ!!&lt;/h2&gt;
  &lt;p&gt;Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. &lt;/p&gt;
  &lt;h3&gt;Наш новый сайт &lt;a href=&quot;https://maddevelop.ru/&quot; target=&quot;_blank&quot;&gt;maddevelop.ru&lt;/a&gt;&lt;/h3&gt;
  &lt;hr /&gt;
  &lt;h2&gt;Страница представления постов&lt;/h2&gt;
  &lt;p&gt;Страница, на которой расположены посты, состоит из нескольких частей: из мастер-страниц и страниц содержимого. Мастер-страница представляет собой обрамление, шаблон из неменяющейся или слабо меняющейся информации. В нашем случае - это шапка страницы, нижняя часть (футер) и, в правой части, место для блоков с названиями возможных категорий и тегов для фильтрации. Сами блоки, категории и теги отображаются с помощью страниц содержимого.&lt;/p&gt;
  &lt;p&gt;Код основной мастер-страницы выглядит следующим образом:&lt;/p&gt;
  &lt;pre&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;

&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;/&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;@ViewBag.Title&amp;lt;/title&amp;gt;
    &amp;lt;link href=&amp;quot;~/Content/bootstrap.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; /&amp;gt;    
&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;

    &amp;lt;nav class=&amp;quot;navbar navbar-light rounded&amp;quot; style=&amp;quot;background-color: #808000;&amp;quot;&amp;gt;
        @Html.ActionLink(&amp;quot;Самый лучший сайт&amp;quot;, &amp;quot;List&amp;quot;, &amp;quot;Blog&amp;quot;, null, 
                         new { @class = &amp;quot;navbar-brand&amp;quot; })
        @Html.ActionLink(&amp;quot;Админ-панель&amp;quot;, &amp;quot;Index&amp;quot;, &amp;quot;Admin&amp;quot;, null, 
                         new { @class = &amp;quot;btn btn-outline-light&amp;quot; })        
    &amp;lt;/nav&amp;gt;
    
&amp;lt;div class=&amp;quot;row&amp;quot; style=&amp;quot;margin-top: 15px; margin-bottom: 15px&amp;quot;&amp;gt;
    
    &amp;lt;div class=&amp;quot;col-10&amp;quot;&amp;gt;
        &amp;lt;div class=&amp;quot;container-fluid&amp;quot;&amp;gt;
            @RenderBody()
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;div class=&amp;quot;col-2&amp;quot;&amp;gt;
        &amp;lt;div class=&amp;quot;container-fluid&amp;quot;&amp;gt;
            &amp;lt;div class=&amp;quot;list-group&amp;quot;&amp;gt;
                @Html.Action(&amp;quot;CatMenu&amp;quot;, &amp;quot;Nav&amp;quot;)                
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;
        &amp;lt;div class=&amp;quot;container-fluid&amp;quot;&amp;gt;
            &amp;lt;div class=&amp;quot;list-group&amp;quot; style=&amp;quot;font-style: italic&amp;quot;&amp;gt;
                @Html.Action(&amp;quot;TagMenu&amp;quot;, &amp;quot;Tag&amp;quot;)
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;        
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;h2 class=&amp;quot;row&amp;quot; id=&amp;quot;dno&amp;quot;&amp;gt;
    Здесь могла быть Ваша реклама!
&amp;lt;/h2&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
  &lt;p&gt;Ниже представлен один из вариантов получаемой страницы:&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/a4/a4d744ca-d4a0-4e4b-b333-91f1cb464faf.png&quot; width=&quot;1819&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;В начале кода мастер-страницы написана минимальная для создания страницы информация:&lt;/p&gt;
  &lt;pre&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;

&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;/&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;@ViewBag.Title&amp;lt;/title&amp;gt;
    &amp;lt;link href=&amp;quot;~/Content/bootstrap.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; /&amp;gt;    
&amp;lt;/head&amp;gt;
&lt;/pre&gt;
  &lt;p&gt;Здесь стоит обратить внимание на предпоследнюю строку с объявлением использования библиотеки шаблонов &lt;em&gt;Bootstrap&lt;/em&gt;. Для создания строки достаточно перетащить css-файл в шапку разметки страницы.&lt;/p&gt;
  &lt;p&gt;Далее идёт описания тела (&lt;em&gt;&amp;lt;body&amp;gt;&lt;/em&gt;).&lt;/p&gt;
  &lt;p&gt;Первый в теле страницы блок кода описывает навигационную панель, которая на рисунке выше отмечена цифрой 1.&lt;/p&gt;
  &lt;pre&gt;&amp;lt;nav class=&amp;quot;navbar navbar-light rounded&amp;quot; style=&amp;quot;background-color: #808000;&amp;quot;&amp;gt;
    @Html.ActionLink(&amp;quot;Самый лучший сайт&amp;quot;, &amp;quot;List&amp;quot;, &amp;quot;Blog&amp;quot;, null, 
                     new { @class = &amp;quot;navbar-brand&amp;quot; })
    @Html.ActionLink(&amp;quot;Админ-панель&amp;quot;, &amp;quot;Index&amp;quot;, &amp;quot;Admin&amp;quot;, null, 
                     new { @class = &amp;quot;btn btn-outline-light&amp;quot; })   
&amp;lt;/nav&amp;gt;
&lt;/pre&gt;
  &lt;p&gt;Навигационная панель имеет светлую тему (&lt;em&gt;navbar-light&lt;/em&gt;) с закругленными краями (&lt;em&gt;rounded&lt;/em&gt;).&lt;/p&gt;
  &lt;p&gt;В панели с помощью двух хелперов &lt;em&gt;ActionLink&lt;/em&gt; создаются гиперссылки на действия контроллера. Здесь первый аргумент - текст ссылки, при нажатии на который случится перенаправление, третий и второй - контроллер и его метод действия, который будет вызываться при нажатии, четвёртый - передаваемый методу действия параметр (в нашем случае такого нет - &lt;em&gt;null&lt;/em&gt;), а пятый - класс создаваемого html-элемента.&lt;/p&gt;
  &lt;p&gt;Поясним на примере &lt;em&gt;@Html.ActionLink(&amp;quot;Самый лучший сайт&amp;quot;, &amp;quot;List&amp;quot;, &amp;quot;Blog&amp;quot;, null, new { @class = &amp;quot;navbar-brand&amp;quot; }). &lt;/em&gt;При нажатии на текст &lt;em&gt;&amp;quot;Самый лучший сайт&amp;quot;&lt;/em&gt; будет вызван метод действия &lt;em&gt;List&lt;/em&gt; контроллера &lt;em&gt;Blog&lt;/em&gt;, который вернёт нам домашнюю страницу. При этом правила оформления гиперссылки записаны в классе &lt;em&gt;navbar-brand&lt;/em&gt; библиотеки &lt;em&gt;Bootstrap 4&lt;/em&gt;.&lt;/p&gt;
  &lt;p&gt;Далее в коде описываются части страницы 2, 3 и 4 (см. рис.).&lt;/p&gt;
  &lt;pre&gt;&amp;lt;div class=&amp;quot;row&amp;quot; style=&amp;quot;margin-top: 15px; margin-bottom: 15px&amp;quot;&amp;gt;
    
    &amp;lt;div class=&amp;quot;col-10&amp;quot;&amp;gt;
        &amp;lt;div class=&amp;quot;container-fluid&amp;quot;&amp;gt;
            @RenderBody()
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;div class=&amp;quot;col-2&amp;quot;&amp;gt;
        &amp;lt;div class=&amp;quot;container-fluid&amp;quot;&amp;gt;
            &amp;lt;div class=&amp;quot;list-group&amp;quot;&amp;gt;
                @Html.Action(&amp;quot;CatMenu&amp;quot;, &amp;quot;Nav&amp;quot;)                
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;
        &amp;lt;div class=&amp;quot;container-fluid&amp;quot;&amp;gt;
            &amp;lt;div class=&amp;quot;list-group&amp;quot; style=&amp;quot;font-style: italic&amp;quot;&amp;gt;
                @Html.Action(&amp;quot;TagMenu&amp;quot;, &amp;quot;Tag&amp;quot;)
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;        
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
  &lt;p&gt;Этот фрагмент разметки задаёт блок, в котором вложенные части располагаются горизонтально (&lt;em&gt;class=&amp;quot;row&amp;quot;&lt;/em&gt;). В строку размещаются две колонки: одна занимает 10/12 всей ширины страницы (&lt;em&gt; div class=&amp;quot;col-10&amp;quot;&lt;/em&gt;), вторая - 2/12 (&lt;em&gt;div class=&amp;quot;col-2&amp;quot;&lt;/em&gt;).&lt;/p&gt;
  &lt;p&gt;В первой колонке находится контейнер, занимающий всю ширину столбца (&lt;em&gt;div class=&amp;quot;container-fluid&amp;quot;&lt;/em&gt;), в котором происходит вызов метода &lt;em&gt;RenderBody()&lt;/em&gt;. Метод вставляет в разметку содержимое представления, указанное методом действия (в нашем случае метод действия &lt;em&gt;List&lt;/em&gt; контроллера &lt;em&gt;Blog&lt;/em&gt;).&lt;/p&gt;
  &lt;p&gt;Во второй колонке находятся два контейнера (№3 и №4 на рисунке), в которых происходит вызов частичных представлений из методов действия &lt;em&gt;CatMenu&lt;/em&gt; и &lt;em&gt;TagMenu&lt;/em&gt; контроллеров &lt;em&gt;Nav&lt;/em&gt; и &lt;em&gt;Tag &lt;/em&gt;соответственно.&lt;/p&gt;
  &lt;p&gt;Подробное описание содержимого столбцов будет приведено в следующей части.&lt;/p&gt;

</content></entry></feed>