<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>MadDevelop</title><generator>teletype.in</generator><description><![CDATA[Блог отдела разработки MadDevelop. Здесь мы делимся своими статьями. Telegram - https://t.me/maddevelop Instagram - Https://instagram.com/maddevelop]]></description><image><url>https://teletype.in/files/b4/b4fce29d-30da-41f7-bb53-6348260a8639.jpeg</url><title>MadDevelop</title><link>https://teletype.in/@maddevelop</link></image><link>https://teletype.in/@maddevelop?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=maddevelop</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/maddevelop?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/maddevelop?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Thu, 30 Apr 2026 07:43:34 GMT</pubDate><lastBuildDate>Thu, 30 Apr 2026 07:43:34 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@maddevelop/BJn6Aiy5V</guid><link>https://teletype.in/@maddevelop/BJn6Aiy5V?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=maddevelop</link><comments>https://teletype.in/@maddevelop/BJn6Aiy5V?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=maddevelop#comments</comments><dc:creator>maddevelop</dc:creator><title>Беседа об алгоритмах</title><pubDate>Sun, 14 Apr 2019 20:39:09 GMT</pubDate><description><![CDATA[Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. ]]></description><content:encoded><![CDATA[
  <hr />
  <h2>!!НАШ БЛОГ ПЕРЕЕХАЛ!!</h2>
  <p>Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. </p>
  <h3>Наш новый сайт <a href="https://maddevelop.ru/" target="_blank">maddevelop.ru</a></h3>
  <hr />
  <h2>Первая задача</h2>
  <p><strong>Условие</strong></p>
  <p>Дан массив целых неповторяющихся положительных чисел. Числа, соседние по числовой оси (при этом они могут находиться в разных частях массива), следует заключить в диапазон типа string, в котором указываются через тире начальное число диапазона и конечное. Необходимо получить строку, где в порядке возрастания указаны все диапазоны и/или отдельные цифры, в них не входящие.</p>
  <p><strong>Пример</strong></p>
  <p>Входной массив: <em>{ 3, 7, 5, 9, 4, 10 }</em></p>
  <p>Ответ: <em>&quot;3 - 5, 7, 9 - 10&quot;</em></p>
  <p><strong>Решение</strong></p>
  <pre>// Массив преобразуется в строку &quot;на лету&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(&quot;&quot;);
    else if (length == 1)
        sb.Append(array[0].ToString());
    else
    {
        bool defis = false;

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

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

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

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

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 }.
</pre>
  <hr />
  <p><strong>Решение</strong></p>
  <p>То, что следует использовать хеш-таблицу я придумал почти сразу. Ещё время ушло на догадку о второй хеш-таблицы.</p>
  <pre>public static int[] GetPairs(int[] first, int[] second)
{
    int length = first.Length;
    if (length == 0)
        return null;
    int[] output = new int[length];            
    HashSet&lt;int&gt; firstHash = new HashSet&lt;int&gt;();
    HashSet&lt;int&gt; secondHash = new HashSet&lt;int&gt;();

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

    for (int i = 1; i &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;
}
</pre>
  <p>К сожалению, снова не смог подумать о таких случаях, как пара массивов { 1, 2 } - { 1, 2 } и { 1, 2 } - { 2, 1 }. Поэтому итоговый ответ получил после пары подсказок.</p>
  <p><strong>Вывод</strong></p>
  <p>Для успешного прохождения собеседований с алгоритмами достаточно набить руку на сайтах типа Leetcode и Codewars. Тогда при взгляде на задачу будет видеть��я верное решение <strong>с учётом</strong> всех граничных ситуаций. Я, правда, прорешал их небольшое количество из-за того, что посвятил своё время освоению некоторых технологий.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@maddevelop/BkaMIXat4</guid><link>https://teletype.in/@maddevelop/BkaMIXat4?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=maddevelop</link><comments>https://teletype.in/@maddevelop/BkaMIXat4?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=maddevelop#comments</comments><dc:creator>maddevelop</dc:creator><title>Проект &quot;Отпуск сотрудников&quot;. Часть 3, интерфейс пользователя.</title><pubDate>Sat, 13 Apr 2019 21:45:04 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/72/7255a2a1-2057-429e-a42a-e318094c1002.png"></media:content><category>WPF</category><description><![CDATA[<img src="https://teletype.in/files/72/7255a2a1-2057-429e-a42a-e318094c1002.png"></img>Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. ]]></description><content:encoded><![CDATA[
  <hr />
  <h2>!!НАШ БЛОГ ПЕРЕЕХАЛ!!</h2>
  <p>Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. </p>
  <h3>Наш новый сайт <a href="https://maddevelop.ru/" target="_blank">maddevelop.ru</a></h3>
  <hr />
  <p>Клиентскую часть реализуем с помощью технологии WPF, воспользовавшись паттерном MVVM. Модели будут те же, что и в серверной части. Просто скопируем их в созданную папку Models. Создадим класс MainViewModel, в котором опишем взаимодействие с API контроллером и с интерфейсом пользователя. Большинство свойств класса модели представления реализуют метод OnPropertyChanged(), поэтому изменение свойства приводит к изменению элемента управления, к которому оно привязано.</p>
  <pre>public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string prop = &quot;&quot;)
{
    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(prop));
}  
</pre>
  <hr />
  <p>Окно пользователя будет выглядеть следующим образом:</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/72/7255a2a1-2057-429e-a42a-e318094c1002.png" width="1462" />
  </figure>
  <p>Цифрами обозначены элементы управления для удобства дальнейшего описания. В таблице ниже удобно посмотреть, как организована привязка свойства класса MainViewModel к свойству элемента управления пользовательского интерфейса.</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/53/535e7ede-aec1-47fc-981e-fb7b4fc7e660.png" width="757" />
  </figure>
  <hr />
  <p>К свойству Command кнопок следует привязать свойство класса RelayCommand в модели представления.</p>
  <pre>public class RelayCommand : ICommand
{
    private Action&lt;object&gt; execute;
    private Func&lt;object, bool&gt; canExecute;

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

    public RelayCommand(Action&lt;object&gt; execute, Func&lt;object, bool&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);
    }
}
</pre>
  <hr />
  <p>Вторая таблица в пользовательском интерфейсе состоит из четырёх: по одной для каждого квартала года. Для того, чтобы к ним можно было привязывать свойства типа двумерных массивов, следует установить пакет NuGet &quot;Gu.Wpf.DataGrid2D&quot;. Эти матрицы составим из объектов типа Cell.</p>
  <pre>public Cell[,] FirstQuarter { get; set; }
public class Cell
{
    public Color Color { get; }

    public Cell(Color color)
    {            
        Color = color;
    }
}
</pre>
  <p>Выделение отпусков сотрудников цветом возможно, если к фону каждой ячейки привязать через конвертер значений свойство Color класса Cell.</p>
  <pre>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();
    }
}
</pre>
  <hr />
  <p>Запишем для ясности определение всех полей и свойств класса MainViewModel.</p>
  <pre>public class MainViewModel : INotifyPropertyChanged
{
    private static IEnumerable&lt;Employee&gt; employees;        
    private static HttpClient client = new HttpClient();                           
    private StringBuilder errorSB = new StringBuilder();

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

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

    // Также для элемента управления №2
    private Color empColor;
    public Color EmpColor
    {
        get =&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 =&gt; currentEmployee;
        set
        {
            currentEmployee = value;
            OnPropertyChanged(nameof(CurrentEmployee));
        }
    }

    // Привязываемое свойство к свйоству также элемента №5
    private Vacation currentVacation;
    public Vacation CurrentVacation
    {
        get =&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 =&gt; start;
        set
        {
            start = value;
            OnPropertyChanged(nameof(Start));
        }
    }

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

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

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

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

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


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


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

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@maddevelop/r1vy4votN</guid><link>https://teletype.in/@maddevelop/r1vy4votN?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=maddevelop</link><comments>https://teletype.in/@maddevelop/r1vy4votN?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=maddevelop#comments</comments><dc:creator>maddevelop</dc:creator><title>Проект &quot;Отпуск сотрудников&quot;. Часть 2, хранилище и контроллер.</title><pubDate>Wed, 10 Apr 2019 12:40:06 GMT</pubDate><category>Asp.Net</category><description><![CDATA[<img src="https://teletype.in/files/c8/c83873ef-e614-41b3-a019-88280805ad99.png"></img>Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. ]]></description><content:encoded><![CDATA[
  <hr />
  <h2>!!НАШ БЛОГ ПЕРЕЕХАЛ!!</h2>
  <p>Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. </p>
  <h3>Наш новый сайт <a href="https://maddevelop.ru/" target="_blank">maddevelop.ru</a></h3>
  <hr />
  <p>Создадим интерфейс <strong>IEmployeeRepository</strong>, в котором будут свойства с названиями таблиц из базы данных, а также методы для добавления и удаления элементов в таблицы.</p>
  <pre>public interface IEmployeeRepository
{
    IEnumerable&lt;Employee&gt; Employees { get; }
    IEnumerable&lt;Vacation&gt; Vacations { get; }
    IEnumerable&lt;Color&gt; Colors { get; }

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

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

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

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

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

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

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

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

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

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

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

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

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

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@maddevelop/Hy60Et7YE</guid><link>https://teletype.in/@maddevelop/Hy60Et7YE?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=maddevelop</link><comments>https://teletype.in/@maddevelop/Hy60Et7YE?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=maddevelop#comments</comments><dc:creator>maddevelop</dc:creator><title>Проект &quot;Отпуск сотрудников&quot;. Часть 1, база данных.</title><pubDate>Fri, 05 Apr 2019 19:15:31 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/23/23dc365c-3538-469f-b974-2b20b4c4432e.jpeg"></media:content><category>Asp.Net</category><description><![CDATA[<img src="https://teletype.in/files/23/23dc365c-3538-469f-b974-2b20b4c4432e.jpeg"></img>Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. ]]></description><content:encoded><![CDATA[
  <hr />
  <h2>!!НАШ БЛОГ ПЕРЕЕХАЛ!!</h2>
  <p>Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. </p>
  <h3>Наш новый сайт <a href="https://maddevelop.ru/" target="_blank">maddevelop.ru</a></h3>
  <hr />
  <p>Сначала сделаем серверную часть проекта. В решении создадим новый проект <strong>&quot;Веб-приложение ASP.NET Core&quot;</strong>. Далее выберем шаблон <strong>&quot;API&quot;</strong>.</p>
  <h2>Создание моделей</h2>
  <p>У нас будет три таблицы: <strong>&quot;Сотрудники&quot;</strong>, <strong>&quot;Цвета&quot;</strong> и <strong>&quot;Отпуски&quot;</strong>. У сотрудника (<em>employee</em>) есть идентификатор, имя и идентификатор цвета, которым будет окрашиваться его отпуски в сводной таблице. У цвета (<em>color</em>) имеются идентификатор и числовой номер, по которому можно будет воссоздать структуру <em>Color</em>, предназначенную для раскраски чего-либо. Специально не стал хранить название цвета в столбце типа string, ведь использование int позволяет занимать таблице меньше места в памяти. Таблицы &quot;Цвета&quot; и &quot;Сотрудники&quot; связаны отношением <em>&quot;один-ко-многим&quot;</em>. У отпуска (<em>vacation</em>) есть обязательный идентификатор, дата начала отпуска, его продолжительность и ключ работника, которому &quot;принадлежит&quot; отпуск. Сотрудники и отпуски связаны отношением &quot;один-ко-многим&quot;. Нулевым может быть только столбец с именем человека, остальные обязательны к заполнению.</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/23/23dc365c-3538-469f-b974-2b20b4c4432e.jpeg" width="1001" />
  </figure>
  <p>Для создания базы данных и таблиц в ней будем использовать <em>Entity Framework Core</em> (далее просто <em>EF</em>). Подход выберем <em>&quot;code first&quot;</em>, то есть по нашим классам, объявленным в <em>C#</em>, <em>EF</em> создаст базу данных вместе с необходимыми таблицами и связями. Для моделей сделаем отдельную папку <em>Models</em>.</p>
  <p>Класс сотрудника:</p>
  <pre>public class Employee
{
    public int EmployeeId { get; set; }
    public string Name { get; set; }        
    public List&lt;Vacation&gt; Vacations { get; set; }
    public int ColorId { get; set; }
    [JsonIgnore]
    public Color Color { get; set; }
}
</pre>
  <p>Класс цвета:</p>
  <pre>public class Color
{
    public int ColorId { get; set; }
    public int ColorNumber { get; set; }
    public List&lt;Employee&gt; Employees { get; set; } 
}
</pre>
  <p>Класс отпуска:</p>
  <pre>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; }
}
</pre>
  <p>Использование свойства <em>Vacations</em> в классе <strong>Employee</strong> и свойств <em>EmployeeId</em> и <em>Employee</em> в классе <strong>Vacation</strong> сообщает <em>EF</em>, что между соответствующими таблицами необходимо создать отношение <em>&quot;один-ко-многим&quot;</em>. Аналогичным образом создаётся и связь между <em>Color</em> и <em>Employee</em>.</p>
  <hr />
  <p>Для создания самих таблиц используется класс контекста, где они формируются на основании свойств:</p>
  <pre>public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions&lt;ApplicationDbContext&gt; options)
        : base(options) { }

    public DbSet&lt;Employee&gt; Employees { get; set; }
    public DbSet&lt;Vacation&gt; Vacations { get; set; }
    public DbSet&lt;Color&gt; Colors { get; set; }
}
</pre>
  <hr />
  <p>Строку подключения базы данных следует указать в файле <em>appsettings.json</em>. Если такого файла не в проекте, то его можно создать вручную.</p>
  <pre>{  
  &quot;ConnectionStrings&quot;: {
    &quot;vacationConnection&quot;: &quot;Server=(localdb)\\mssqllocaldb;Database=vacationsstore;
      Trusted_Connection=True&quot;
  }
} 
</pre>
  <p>Здесь <em>&quot;vacationsstore&quot;</em> - имя базы данных, которое можно задать любым.</p>
  <hr />
  <p>Создание класса контекста с нужной строкой подключения следует зарегистрировать в провайдере сервисов в методе <em>ConfigureServices()</em> класса <strong>Startup</strong>. Сам метод выглядит следующим образом:</p>
  <pre>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(&quot;vacationConnection&quot;);
        services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt;
            options.UseSqlServer(connection));
        services.AddSingleton&lt;IEmployeeRepository, EFEmployeeRepository&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);
    }
}  
</pre>
  <hr />
  <p>Также создадим класс с методом, который будет сразу заполнять нашу базу данных начальными значениями.</p>
  <pre>public static class SeedData
{
    public static void EnsurePopulated(IApplicationBuilder app)
    {
        ApplicationDbContext context = 
            app.ApplicationServices.GetRequiredService&lt;ApplicationDbContext&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 = &quot;Pyotr&quot;, 
                    Color = context.Colors.Skip(0).FirstOrDefault() },
                new Employee { Name = &quot;Sergey&quot;, 
                    Color = context.Colors.Skip(1).FirstOrDefault() },
                new Employee { Name = &quot;Vasiliy&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();
        }
    }
}
</pre>
  <p>В первой строчке метода мы получаем класс контекста из провайдера сервисов. Затем производим все необходимые миграции. Если база данных с именем из строки подключения отсутствует, то она создаётся. При пустой таблице Employees мы заполняем все таблицы данными. Метод <em>EnsurePopulated()</em> следует вызывать в методе <em>Configure()</em> класса <strong>Startup</strong>. В результате данные таблиц будут такими:</p>
  <figure class="m_custom">
    <img src="https://teletype.in/files/33/33da81f4-3d26-426f-a17c-29860d5b5a71.png" width="297" />
  </figure>
  <figure class="m_custom">
    <img src="https://teletype.in/files/ad/ad3c2904-4282-4ebf-9bc3-653e39ef05ed.png" width="399" />
  </figure>
  <figure class="m_custom">
    <img src="https://teletype.in/files/a8/a8e83776-76bb-49cc-87cc-424b408414e3.png" width="198" />
  </figure>
  <hr />
  <p>И, наконец, в методе <em>CreateWebHostBuilder()</em> класса <strong>Program</strong> сконфигурируем провайдер сервисов:</p>
  <pre>public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

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

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@maddevelop/B1ru3VbtN</guid><link>https://teletype.in/@maddevelop/B1ru3VbtN?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=maddevelop</link><comments>https://teletype.in/@maddevelop/B1ru3VbtN?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=maddevelop#comments</comments><dc:creator>maddevelop</dc:creator><title>Немного о REST Web API</title><pubDate>Wed, 03 Apr 2019 17:38:06 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/98/981f50d3-1f39-4bcd-8619-e679932cc0e7.png"></media:content><description><![CDATA[<img src="https://teletype.in/files/98/981f50d3-1f39-4bcd-8619-e679932cc0e7.png"></img>Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. ]]></description><content:encoded><![CDATA[
  <hr />
  <h2>!!НАШ БЛОГ ПЕРЕЕХАЛ!!</h2>
  <p>Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. </p>
  <h3>Наш новый сайт <a href="https://maddevelop.ru/" target="_blank">maddevelop.ru</a></h3>
  <hr />
  <p><strong>API</strong> (англ. <em>application programming interface</em> - <strong>&quot;программный интерфейс приложения&quot;</strong>) - описание способов (набор классов, процедур, функций, структур или констант), которыми одна компьютерная программа может взаимодействовать с другой программой. API определяет функциональность, которую предоставляет программа (модуль, библиотека). При этом API позволяет абстрагироваться от того, как именно эта функциональность реализована. Если программный интерфейс приложения написан для веб-сервера или веб-браузера, то мы имеем дело с <strong>Web API</strong>.</p>
  <p><strong>Веб-служба</strong>, веб-сервис - идентифицируемая уникальным веб-адресом программная система со стандартизованными интерфейсами. Веб-службы могут взаимодействовать друг с другом и со сторонним приложениями посредством сообщений, основанных на определённых протоколах или соглашениях (REST). Веб-служба является единицей модульности при использовании <em>сервис-ориентированной архитектуры</em> приложения. Использование подобного построения обеспечивает комбинирование и многократное использование компонентов для создания сложных распределённых программных комплексов, обеспечивая независимость от используемых платформ и инструментов разработки, способствуя <em>масштабируемости</em> и <em>управляемости</em> создаваемых систем.</p>
  <figure class="m_custom">
    <img src="https://teletype.in/files/98/981f50d3-1f39-4bcd-8619-e679932cc0e7.png" width="813" />
  </figure>
  <p><strong>REST</strong> (англ. representational state transfer - <strong>&quot;передача состояния представления&quot;</strong>) - это стиль архитектуры программного обеспечения для распределённых систем, таких как World Wide Web, который часто используется для построения веб-служб. В сети Интернет вызов удалённой процедуры может представлять собой обычный HTTP-запрос (обычно «GET» или «POST»; такой запрос называют <em>«REST-запрос»</em>), а необходимые данные передаются в качестве параметров запроса. Для веб-служб, построенных с учётом REST (то есть не нарушающих накладываемых им ограничений), применяют термин «RESTful».</p>
  <p>В отличие от веб-сервисов на основе SOAP, не существует «официального» стандарта для RESTful веб-API. Дело в том, что REST является архитектурным стилем, в то время как SOAP является протоколом. Несмотря на то, что REST не является стандартом сам по себе, большинство RESTful-реализаций используют стандарты, такие как HTTP, URL, JSON и XML.</p>
  <p>Требования (<a href="https://ru.wikipedia.org/wiki/REST" target="_blank">здесь</a> подробнее) к архитектуре REST:</p>
  <ul>
    <li>модель клиент-сервер;</li>
    <li>отсутствие состояния;</li>
    <li>кэширование;</li>
    <li>единообразие интерфейса;</li>
    <li>слои;</li>
    <li>код по требованию.</li>
  </ul>
  <p>P.S. Информация почти целиком взята из Википедии, данная заметка создана для понимания REST Web API без перехода по нескольким ссылкам.</p>
  <hr />
  <p><strong><em>Ещё больше интересной информации на нашем <a href="https://t.me/maddevelop" target="_blank">Telegram</a>-канале.</em></strong></p>

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

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@maddevelop/Hk7-T-Ed4</guid><link>https://teletype.in/@maddevelop/Hk7-T-Ed4?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=maddevelop</link><comments>https://teletype.in/@maddevelop/Hk7-T-Ed4?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=maddevelop#comments</comments><dc:creator>maddevelop</dc:creator><title>История о том, как мы создавали чат бот для Telegram.</title><pubDate>Tue, 26 Mar 2019 15:30:24 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/bf/bf982ad5-cb9e-4ca7-9a12-0d8e58a37a65.jpeg"></media:content><description><![CDATA[<img src="https://teletype.in/files/bf/bf982ad5-cb9e-4ca7-9a12-0d8e58a37a65.jpeg"></img>Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. ]]></description><content:encoded><![CDATA[
  <hr />
  <h2>!!НАШ БЛОГ ПЕРЕЕХАЛ!!</h2>
  <p>Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. </p>
  <h3>Наш новый сайт <a href="https://maddevelop.ru/" target="_blank">maddevelop.ru</a></h3>
  <hr />
  <p>В общем, все началось с того, что появилась идея создать бот, чтобы Вы, наши подписчики, смогли протестировать свои знания по языкам программирования. Проще говоря, бот должен содержать в себе различного рода тренировочные задания. Но мы столкнулись с некоторыми сложностями.</p>
  <figure class="m_custom">
    <img src="https://teletype.in/files/bf/bf982ad5-cb9e-4ca7-9a12-0d8e58a37a65.jpeg" width="1920" />
  </figure>
  <p>Первый вариант статьи должен был содержать описание погодного бота, созданного на основе ASP.Net Core Web API, но мы столкнулись с тем, что бот просто так не запустился... В связи с тем, что у нас в стране полным ходом (нет) идет блокировка сервиса Telegram, достучаться до него получится только с помощью VPN. И поэтому приняли решение описать все с самого начала. Постараемся по максимуму рассказать о тех подводных камнях, которые можно встретить во время разработки бот</p>
  <p>Информацию искали в самых различных источниках, очень помог официальный <a href="https://telegrambots.github.io/book/1/quickstart.html" target="_blank">мануал</a> от команды Telegram.</p>
  <p>Начнем с самого начала, а именно с регистрации нового бота. Поехали!</p>
  <h2>Регистрация нового бота.</h2>
  <p>Создаем бота через другого бота (вот такая вот загогулина :D ). Для этого находим в поиске <strong><em>@BotFather</em></strong> и добавляем его к себе в чаты.</p>
  <figure class="m_custom">
    <img src="https://teletype.in/files/47/475aaddf-fc4b-4239-a9d9-2e8f38945fb0.jpeg" width="243.5" />
  </figure>
  <p>Далее отправляем <em>/start</em>, он выдает весь список возможных команд. Нам нужна команда <em>/newbot</em>. Он предложит нам выбрать название бота и id, с помощью которого можно будет его найти.</p>
  <p>После выбора этих параметров BotFather отправит сообщение, в котором будет поздравление с созданием нового бота и <em>(самое главное)</em> <strong>токен</strong> нашего бота, его то мы и будем использовать.</p>
  <figure class="m_custom">
    <img src="https://teletype.in/files/cb/cb9a1bcf-8053-459b-8dcb-c439cecdbe9e.jpeg" width="305" />
  </figure>
  <p>После можно найти своего бота по id и произвести дополнительную настройку, добавить описание и аватарку.</p>
  <blockquote>Все программы будут создаваться на C# и иногда будем применять технологию .Net Core. В качестве среды разработки бы выбран VS Code (Просто у нас несколько ноутбуков, один из них на Ubuntu и там стоит Code. В целом очень даже хороший редактор кода. Обязательно сделаем статью по некоторым основам работы с этой программой)</blockquote>
  <h2>Проверяем работоспособность на простейшей программе.</h2>
  <p>Создадим папку (Например Example 1) и откроем ее в VS Code. Через терминал введем команду</p>
  <pre>dotnet new console
</pre>
  <p>тем самым у нас создастся новый консольный проект, который содержит только файл Program.cs.</p>
  <p>Через диспетчер NiGet установим в наш проект пакет Telegram.Bot. В файле Program.cs добавим с помощью директивы #using</p>
  <pre>using Telegram.Bot;
using Telegram.Bot.Args;
</pre>
  <p>И в классе Programm напишем такую программу.</p>
  <pre>class Program {
    static ITelegramBotClient botClient;

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

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

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

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@maddevelop/ryrJ1hFU4</guid><link>https://teletype.in/@maddevelop/ryrJ1hFU4?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=maddevelop</link><comments>https://teletype.in/@maddevelop/ryrJ1hFU4?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=maddevelop#comments</comments><dc:creator>maddevelop</dc:creator><title>Отзыв о митапе №2 GetIT Community, часть первая</title><pubDate>Sun, 03 Mar 2019 19:45:33 GMT</pubDate><media:content medium="image" url="https://avatars.mds.yandex.net/get-zen_doc/59126/pub_5c7bef29aa86a600b2b138c3_5c7bf4c77cf2bf00b2fc634a/orig"></media:content><description><![CDATA[<img src="https://avatars.mds.yandex.net/get-zen_doc/59126/pub_5c7bef29aa86a600b2b138c3_5c7bf4c77cf2bf00b2fc634a/orig"></img>Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. ]]></description><content:encoded><![CDATA[
  <hr />
  <h2>!!НАШ БЛОГ ПЕРЕЕХАЛ!!</h2>
  <p>Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. </p>
  <h3>Наш новый сайт <a href="https://maddevelop.ru/" target="_blank">maddevelop.ru</a></h3>
  <hr />
  <p>Два дня назад, в пятницу, 1 марта, состоялся второй митап сообщества разработчиков России GetIT. Встреча происходила в Москве, в офисе &quot;<strong>Мегафон</strong>&quot; на Садовой-Каретной. Здание, которое &quot;Мегафон&quot; делит пополам со &quot;Сбербанком&quot;, значительно повлияло на общий вид центральной части Москвы, поэтому интересно было оказаться там. К сожалению, в самую верхнюю часть, где располагается вроде бы смотровая площадка, не было возможности попасть. Но об этом позже.</p>
  <p>Мероприятие должно было начаться в 18-40, в здании я был в 18-25. Удалось десять минут перекинуться с товарищем, который в &quot;Мегафоне&quot; же и работает. В 18-40 я вбежал в зал, ожидая, что попадаю на самое начало митапа, но его отложили минут на 15, поэтому можно было спокойно поесть пирогов. Помещение для митапа было раза в два больше, чем в <em>tutu.ru</em>, где проходила первая встреча. Количество слушателей увеличилось соответственно. Мне показалось, что в зале находилось человек сто.</p>
  <figure class="m_column">
    <img src="https://avatars.mds.yandex.net/get-zen_doc/59126/pub_5c7bef29aa86a600b2b138c3_5c7bf4c77cf2bf00b2fc634a/orig" width="2284" />
  </figure>
  <p>Первым выступал <strong>Евгений Сугробов</strong> (HR-директор Banki.ru) с докладом <em>&quot;Как оценивать софт скилы при найме людей в команду&quot;</em>. Выступление было лаконичным, но сама тема с софт скилами достаточно избита, и что-то новое почерпнуть из него было сложно. Когда пришло время вопросов зрителей, то стало веселее, так как видно, что Евгений - человек с большим опытом и ему есть что рассказать. Для себя безрадостно отметил его слова о том, что при найме начинающих разработчиков больше внимания стоит обращаться на имеющиеся у них навыки программирования, чем на черты характера. Софт скилы начинают выходить на первый план после достижения программистом уровня уверенного мидла.</p>
  <p>Второй рассказчик - <strong>Дарья Афанасьева</strong> из &quot;Skyeng&quot; с темой <em>&quot;Как выстроить работу с удалённой командой&quot;</em>. В её коллективе из десяти человек восемь работают удалённо. Она описала, как проходит их рабочий день, какие программы используют для организации рабочего процесса и поддержания связи. Для контроля проделанной работы каждый отписывается о своих успехах и планах боту. Общение между ними происходит в чатах нескольких мессенджеров. Если с каким-то мессенджером начинаются проблемы, то они легко переходят на использование другого. Для укрепления командного духа её сотрудники иногда встречаются и проводят вместе отпуск.</p>
  <p>Автором третьей презентации <em>&quot;Тим-лид как Заказчик, как строить работу с HR и давать фидбеки по подбору специалистов в команду&quot;</em> были две барышни из &quot;Мегафона&quot;: <strong>Мария Трофимова</strong> (Руководитель по архитектуре и интеграционным решениям корпоративного хранилища данных) и <strong>Светлана Стаканова</strong> (HR). Доклад начался с любопытных слайдов о корпоративном хранилище данных (КХД) &quot;Мегафона&quot;.</p>
  <figure class="m_column">
    <img src="https://avatars.mds.yandex.net/get-zen_doc/1329105/pub_5c7bef29aa86a600b2b138c3_5c7c019564fb5100b3167ac8/orig" width="1921" />
  </figure>
  <figure class="m_column">
    <img src="https://avatars.mds.yandex.net/get-zen_doc/108872/pub_5c7bef29aa86a600b2b138c3_5c7c01a82248a400aff21892/orig" width="1991" />
  </figure>
  <figure class="m_column">
    <img src="https://avatars.mds.yandex.net/get-zen_doc/1222384/pub_5c7bef29aa86a600b2b138c3_5c7c01b57dccd000afeadedf/orig" width="1970" />
  </figure>
  <p>После краткого изложения технических данных КХД Мария рассказала о задачах, стоящих перед её подразделением.</p>
  <figure class="m_column">
    <img src="https://avatars.mds.yandex.net/get-zen_doc/1538903/pub_5c7bef29aa86a600b2b138c3_5c7c0396ae21cb00d49db463/orig" width="2019" />
  </figure>
  <p>Далее в форме &quot;вопрос - ответ&quot; был организован диалог между Марией и Светланой. Признаться, вопросы были общего характера и несодержательны. Из них интересного я только узнал, что собеседование проходит в два этапа: разговор с HR и техническое, с Марией. Техническая часть заключается в размышлениях кандидата о том, как бы он решил поставленную задачу, как реализовывал собственные проекты. Никаких справочных вопросов не задаётся. Либо сразу, либо на следующий день соискателю сообщается результат собеседования.</p>
  <figure class="m_column">
    <img src="https://avatars.mds.yandex.net/get-zen_doc/153162/pub_5c7bef29aa86a600b2b138c3_5c7c058f0a096c00b36329e3/orig" width="2012" />
  </figure>
  <p>Необычный вопрос в конце доклада задали про соискателей, ранее судимых! Светлана логично на него ответила, что если преступление было не жуткое, то проблем у человека с наймом быть не должно.</p>
  <p><em>Продолжение следует...</em></p>
  <hr />
  <p><strong><em>Ещё больше интересной информации на нашем <a href="https://t.me/maddevelop" target="_blank">Telegram</a>-канале.</em></strong></p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@maddevelop/BJe0LyUHE</guid><link>https://teletype.in/@maddevelop/BJe0LyUHE?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=maddevelop</link><comments>https://teletype.in/@maddevelop/BJe0LyUHE?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=maddevelop#comments</comments><dc:creator>maddevelop</dc:creator><title>ASP.NET MVC 5. Создание сайта-блога. Часть 3.</title><pubDate>Thu, 28 Feb 2019 10:31:15 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/a4/a4d744ca-d4a0-4e4b-b333-91f1cb464faf.png"></media:content><category>Asp.Net</category><description><![CDATA[<img src="https://teletype.in/files/a4/a4d744ca-d4a0-4e4b-b333-91f1cb464faf.png"></img>Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. ]]></description><content:encoded><![CDATA[
  <hr />
  <h2>!!НАШ БЛОГ ПЕРЕЕХАЛ!!</h2>
  <p>Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда. </p>
  <h3>Наш новый сайт <a href="https://maddevelop.ru/" target="_blank">maddevelop.ru</a></h3>
  <hr />
  <h2>Страница представления постов</h2>
  <p>Страница, на которой расположены посты, состоит из нескольких частей: из мастер-страниц и страниц содержимого. Мастер-страница представляет собой обрамление, шаблон из неменяющейся или слабо меняющейся информации. В нашем случае - это шапка страницы, нижняя часть (футер) и, в правой части, место для блоков с названиями возможных категорий и тегов для фильтрации. Сами блоки, категории и теги отображаются с помощью страниц содержимого.</p>
  <p>Код основной мастер-страницы выглядит следующим образом:</p>
  <pre>&lt;!DOCTYPE html&gt;
&lt;html&gt;

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

&lt;body&gt;

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

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

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

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

]]></content:encoded></item></channel></rss>