ASP.NET Core, Angular 2, SignalR для чайников

    Всем привет! Хочу поделиться своим опытом использования ASP.Net Core и Angular 2 с использованием SignalR.

    Будучи программистом 1С, часто приходится решать задачи, которые на 1С решить сложно или невозможно. Очень помогает знание .Net. Но вот, что касается клиентской части сайтов, то здесь много тонкостей (JavaScript, CSS, JQuery итд), которые быстро забываются, если ими не пользоваться.

    Angular 2 позволяет значительно упростить создание клиентской части. Так TypeScript значительно ближе к C# (и главное позволяет использовать Руслиш), а с шаблонами несложно разобраться зная Razor и Xaml.

    Главное, что вы работаете с данными, по аналогии с WPF. При этом есть куча контролов.

    Хочу поделиться с такими же бедолагами как я, или кто только начинает изучение Angular 2, ASP.Net Core, так как потратил значительное время, на поиски материалов для изучения.

    Для тренировки на кошках был выбран мой проект 1C Messenger для отправки сообщений, файлов и обмена данными между пользователями 1С, вэб страницы, мобильными приложениями а ля Skype, WhatsApp. Исходники Здесь

    Пока не вышел. Net Core 1.2 и NetStandard 2, сейчас нет поддержки клиента для SignalR под .Net Core

    Итак, начнем. Для работы нам потребуется:

    1. ASP.NET Core + Angular 2 шаблон для Visual Studio
    2. Руководство по ASP.NET Core
    3. Руководство по Angular 2
    4. Руководство по TypeScript
    5. Компоненты от PrimeNG
    6. Компоненты Bootstrap

    ASP.NET Core + Angular 2 шаблон для Visual Studio это чудесный шаблон, котоый настраивает ваше приложения для использования ASP.Net Core и Angular 2 создавая кучу json-файлов и настаивая для использования webpack. Для примера можно почитать статью Запускаем Angular2 c Visual Studio 2015

    И самое главное мы можем изменять ts и html файлы во время отладки и видеть изменения при обновлении страницы. За это отвечает классы из Microsoft.AspNetCore.SpaServices.dll
    WebpackDevMiddlewareOptions и SpaRouteExtensions. Да да! Отлаживать мы можем в браузере. При этом Ts файлы лежат в папке (нет домена)

    Плюс с шаблоном идет куча примеров! Но все они сохранены в ASCII. Если будете использовать кириллицу, нудно пересохранить ts и html в UTF-8.

    Компоненты от PrimeNG позволяют значительно упростить создание клиентского кода. Кроме того на них можно изучить создание собственных компонент и модифицировать существующие.

    Начнем с создания хаба SignalR. Удобно использовать типизированные хабы.

    public interface IHelloHub
        {
    
            // На клиенте независимо как названы методы на сервере
            //будет использоваться Camel нотация
            // поэтому все методы начинаем с прописных букв
    
            
            Task sendEcho(string str, string Кому);
            
           // События Клиенту
            // Нужно учитывать, что Xamarin пока поддерживат передачу только 4 параметров
           Task addMessage(string Name, string str, string ConnectionId);
    
    }
    
    

    То при реализации этого интерфейса буду контролироваться не только метод sendEcho. Но и Clients.Others.addMessage и Clients.Client(Кому).addMessage.

     public class HelloHub : Hub<IHelloHub>
        {
    
            // Отправим сообщения всем пользователям, кроме отсылающего если Кому пустая строка
            // Если Кому определен, то отправим конкретному пользователю и ID Кому
            public async Task sendEcho(string str, string Кому)
            {
                var user = new User();
                if (!ПользовательЗарегистрирован(user))
                    return;
    
                if (string.IsNullOrWhiteSpace(Кому))
                 await Clients.Others.addMessage(user.Name, str, user.ConnectionId);
                else
                 await  Clients.Client(Кому).addMessage(user.Name, str, user.ConnectionId);
            }
    }
    

    Кроме того этот интерфейс удобно использовать в .Net приложении, и можно создать кодогенератор для TypeScript описания сервиса.

    Теперь перейдем к созданию клиента. Сначала создадим общий сервис Один сервис для всех компонентов

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

    Код сервиса SignalR
    /// <reference path="../models/ForHelloHub.ts"/>
    import { IHelloHub, DataInfo, ChatMessage, User}  from "../models/ForHelloHub";
    import { EventEmitter } from '@angular/core';
    import { SelectItem } from 'primeng/primeng';
    declare var $: any;
    
    export class HelloHub implements IHelloHub
    {
        // Все сообщения
        public allMessages: ChatMessage[];
        // Флаг подключения к Хабу
        public connectionExists: Boolean;
        // Пользователь зарегистрировал имя
        public isRegistered: Boolean;
        // $.connection.helloHub.server
        private server: any;
         // $.connection.helloHub.client
        private client: any;
        // $.connection.helloHub
        private chat: any;
    
        // ID подключения
        private userId: string;
        // Подключенные пользователи
        public Users: SelectItem[];
        // Событие об изменении списка пользователей
        public onChangeUser: EventEmitter<void> = new EventEmitter<void>(); 
        // Событие о получении сообщения
        public onAddMessage: EventEmitter<void> = new EventEmitter<void>();
        // Событие о подключении к хабу
        public onConnected: EventEmitter<void> = new EventEmitter<void>();
        // Событие о регистрации имент пользователя.
        public onRegistered: EventEmitter<void> = new EventEmitter<void>();
    
        constructor() {
            this.userId = "";
            // Установим начальный список с именем "Всем". При его выборе
            // сообщения будут отправлены всем пользователям, кроме текущего
            this.Users = [{ label: "Всем", value: ""}];
            this.connectionExists = false;
            this.isRegistered = false;
    
            this.chat = $.connection.helloHub;
            this.server = this.chat.server;
            this.client = this.chat.client;
    
            // Установим обработчики событий
            this.registerOnServerEvents();
            this.allMessages = new Array<ChatMessage>();
    
            // Подсоединимся к Хабу
            this.startConnection();
        }
    
    
        // Сортировка пользователей по имени. Всем должна быть первой
        private sortUsers() {
            this.Users.sort((a, b: SelectItem) => {
                if (a.label == "Всем") return -1;
    
                return a.label.toLocaleUpperCase().localeCompare(b.label.toLocaleUpperCase());
    
            });
    
        }
    
    
        //установим обработчики к событиям от сервера
        private registerOnServerEvents(): void {
    
            let self = this;
    
    
            // Событие о получении сообщения
            //Task addMessage(string Name, string str, string ConnectionId);
            this.client.addMessage = (name: string, message: string, ConnectionId: string) => {
                // Добавление сообщений на веб-страницу 
                console.log('addMessage ' + message);
                self.addMessage(name, message,ConnectionId);
            };
    
    
           // Событие о регистрации пользователя
                   //Task onConnected(string id, string userName, List < User > users);
            this.client.onConnected = function (id: string, userName: string, allUsers: User[]) {
                self.isRegistered = true;
                   self.userId = id;
                // Добавление всех пользователей
                for (let user of allUsers) {
    
                    self.addUser(user.ConnectionId, user.Name);
                   }
    
                self.sortUsers();
                // Сообщим об изменении списка пользователей
                self.onRegistered.emit();
            };
    
    
            //Task onNewUserConnected(string id, string userName);
            // Добавляем нового пользователя
            this.client.onNewUserConnected = (id: string, name: string) => {
    
                self.addUser(id, name);
            };
    
            //Task onUserDisconnected(string id, string Name);
            // Удаляем пользователя
           this.client.onUserDisconnected = (id: string, userName: string) => {
    
                let idx = self.Users.findIndex((cur: SelectItem) => {
                    return cur.value == id;
                });
    
                if (idx != -1) {
                    return self.Users.splice(idx, 1);
    
                };
    
            }       
        }
    
        // Найдем пользователя по id
        // Если не находим то создаем нового пользователя
        findUser(userName:string,id: string): SelectItem
        {
            let idx = this.Users.findIndex((cur: SelectItem) => {
                return cur.value == id;
            });
    
            if (idx != -1) {
                return this.Users[idx];
            }
            return { label: userName, value:id }
             
        }
        // Обработаем сообщение с сервера
        addMessage(name: string, message: string, ConnectionId: string): void {
    
            this.allMessages.splice(0, 0, new ChatMessage(message, new Date, this.findUser(name, ConnectionId)));
            this.onAddMessage.emit();
    
        }
    
    
        // Добавим пользователя и отсортируем по наименованию
        addUser(id: string, name: string): void {
            if (this.userId === "") return;
    
            if (this.userId !== id) {
                let usr = { label: name, value: id };
                this.Users.push(usr);
                this.sortUsers();
                this.onChangeUser.emit();
            }
        }
    
        // Подключимся к Хабу
        private startConnection(): void {
            let self = this;
            $.connection.hub.start().done((data: any) => {
                console.log('startConnection ' + data);
                self.connectionExists = true;
                self.onConnected.emit();
                console.log('Send  onConnected');
            }).fail((error) => {
                console.log('Could not connect ' + error);
    
            });
        }
    
    //======= методы и события сервера
    
        // Отошлем сообщение Всем или конкретному пользователю
        sendEcho(str: string, Кому: string)
        {
    
            this.server.sendEcho(str, Кому);
        }
    
        // Отошлем сообщение по имени
        sendByName(message: string, Кому: string)
        {
    
            this.server.sendByName(message, Кому);
        }
    
    
          // Зарегистрироваться на сервере по имени
        connect(userName: string)
        {
            this.server.connect(userName);
    
        }
    
      
    }
    


    Теперь перейдем к созданию компонента:

    Компонент для отображения полученных сообщений и отправки сообщений
    import { Component, NgZone, ViewChild, AfterViewInit } from '@angular/core';
    import { HelloHub } from '../services/HelloHubServise';
    import { ChatMessage } from '../models/ForHelloHub';
    import { SelectItem} from 'primeng/primeng';
    import { Dropdown2 } from '../Dropdown/Dropdown';
    
    
    @Component({
        selector: 'p-HelloHubComponent',
        template: require('./SignalRHelloHub.html')
    })
    
    
    export class HelloHubComponent {
        @ViewChild(Dropdown2)
        public dropdown: Dropdown2;
    
        public allMessages: ChatMessage[];
        public Users: SelectItem[];
        public selectedUser: string;
        public Message: string;
        public selectUsername: boolean=false;
        constructor(private _signalRService: HelloHub) {
            // Подключимся к событиям от сервиса
            this.subscribeToEvents();
    
          // Получим все сообщения полученные за время существования страницы
           this.allMessages = this._signalRService.allMessages;
         // Получим данные о пользователях
            this.Users = _signalRService.Users;
            
        }
    
        // Метод отправки сообщений взависимости от выбранных данных
        public sendMessage() {
    
    
            
            if (this.dropdown.value == "") // Если Выбран "Всем" отправляем  всем пользователям, кроме отправителя
            {
                    this._signalRService.sendEcho(this.Message, "");
                }
                else {
    
               // В 1С может быть несколько пользователей с одним именем     
                if (!this.selectUsername) // Если не стоит галка "По Имени" то отправляем конкретному мользователю
                        this._signalRService.sendEcho(this.Message, this.dropdown.value);
                    else  // отправляем сообщение всем пользователям с выбранным именем
                        this._signalRService.sendByName(this.Message, this.dropdown.selectedOption.label);
                }
    
                this.Message = "";
           
        }
    
        private subscribeToEvents(): void {
    
            let self = this;
        
           // Обновим данные о полученных сообщениях
            this._signalRService.onAddMessage.subscribe(() => {
                self.allMessages = this._signalRService.allMessages;
            });
    
            // Обновим данные о пользователях
            this._signalRService.onChangeUser.subscribe(() =>
            { this.Users = self._signalRService.Users; }
            );
        }
    
        
    }



    Компонент просто считывает и обновляет данные по событию из Сервиса и отправляет сообщения через методы сервиса. Ну и покажем HTML код компонента.

    HTML шаблон

     <div class="container" id="MainMessage">
            <form role="form"  (ngSubmit)="sendMessage()">
    
                <div class="form-group">
                    <textarea type="text" [(ngModel)]="Message" name="Message" class="form-control" placeholder="Сообщение"></textarea>
                </div>
    
                <div class="form-group">
                    <div class="btn-group open">
                        <button  type="submit" class="btn btn-info">Отправить</button>
                    </div>
                    <div class="btn-group" id="users">
                        <p-dropdown2 [options]="Users" [(ngModel)]="selectedUser" name="dropdownUsers"></p-dropdown2>
                    </div>
                    <div class="btn-group" id="SendByName">
                        <div class="checkbox">
                            <label>
                                <input type="checkbox" name="CheckBoxSendByName" [(ngModel)]="selectUsername"  [disabled]="dropdown.value==''"> По имени
                            </label>
                        </div>
    
    
                    </div>
                </div>
    
    
            </form>
    
    
        </div>
    
        <div class="row">
    
            <div class="col-xs-12 col-md-8" id="GetingMessage">
                <template ngFor let-item [ngForOf]="allMessages">
                    <div class='panel panel-primary'>
                        <div class='panel-heading'>
                            {{item.From.label}} от {{item.Sent.toLocaleString()}}
                        </div>
                        <div class='panel-body'>
                        {{item.Message}}
                        </div>
                    </div>
                </template>
            </div>
            <div class="col-xs-6 col-md-4">
    
            </div>
        </div>
    

    У нас есть форма для отправки сообщения, которая содержит textarea с привязкой свойства Message, dropdown с данными о подключенных пользователях, checkbox с привязкой selectUsername для отправки сообщения по имени.

    И есть блок с полученными сообщениями allMessages. Сам Html получился компактным, а весь код на очень приятном TypeScript.

    Обращу внимание на применение self внутри замыкания. Это связано с JS. Сам компилятор TS в JS генерирует:

    var _this = this;

    и заменяет

    var transport = this.chat.transport;

    на

    var transport = _this.chat.transport;

    Но для отладки удобно использовать промежуточную переменную

      let self = this;

    Исходники можно посмотреть здесь.

    Ну и до кучи статья и шаблоны Create great looking, fast, mobile apps using JavaScript, Angular 2, and Ionic 2

    Angular 2 наступает по всем фронтам
    Поделиться публикацией

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 34

    • НЛО прилетело и опубликовало эту надпись здесь
        0
        bootstrap для кучи, если самому захочется поэксперементировать, да и что бы понимать как это все устроено
        0
        Вот смотрю я на развитие Angular 2 Ionic 2 Это очень близко к WPF. Сейчас для .Net Core нет кроссплатформенного декстопного GUI. Но вот есть идея скрестить Angular 2 и .Net Core. В свое время я сделал обертку для использования классов .Net Core из неуправляемого кода Мои статьи

        В обертке реализован доступ к классам из сборок, итераторы, вывод типов для дженериков, методы расширения, поддержка событий. То есть можно писать код близкий к C# на 1С. К моему огромному сожалению 1С это оказалось не нужно. В том плане, что они не хотят модернизировать Native Api для передачи в параметрах объектов и возврате объектов из методов. Поэтому ссылки на объекты передаются виде строки и из которых создаются объекты ВК. Это очень неудобно.

        Но вот подумалось, что эту технологию можно прикрутить к браузеру через плагин. То есть можно создавать обертку на стороне браузера. При этом можно для TypeScript генерить интерфейсы (в свое время предлагал псевдоинтерфейсы для динамиков в .Net) и использовать IntelliSense на полную катушку. То есть можно как в Xamarin писать единую общую часть в Dll, а GUI писать отдельно. Я знаю, что 1С использует технологию «Создание компонент с использованием технологии Native API». и в Web клиенте через плагины. Буду благодарен за конструктивную критику, идеи и ссылки на создание плагинов.
          0
          Не знаю конечно, как для веба, все таки там и обычная связка с вторым Ангуляром отлично работает, а вот скрестить что-то похожее на Electron.js, второй Ангуляр и .NET, это было бы интересно)
            –1
            спасибо. Интересно. Сейчас стал изучать хромовский Native Client SDK

            Скачал по ссылке https://developer.chrome.com/native-client/sdk/download хромовского клиента.
            Нашел часто используемую структуру в файле ..\nacl_sdk\pepper_49\include\ppapi\c\ppVar.h

            typedef enum {
               /**
               * Represents a JavaScript object. This vartype is not currently usable
               * from modules, although it is used internally for some tasks. These objects
               * are reference counted, so AddRef() and Release() must be used properly to
               * avoid memory leaks.
               */
              PP_VARTYPE_OBJECT = 6,
            


            Эх теперь с С разбираться.
              0
              Выложил на обсуждение свое видение
              Использование в TypeScript классов .Net
              Суть в том, что используя Native Client Messaging System, Proxy и Promise vs можем вызывать методы .Net классов асинхронно через await

                 
                let HttpClient=await NetWrap.GetType("System.Net.Http.HttpClient","System.Net.Http.dll"); 
                   let HttpClientHandler = await NetWrap.GetType("System.Net.Http.HttpClientHandler","System.Net.Http.dll"); 
              
                  let client=await NetWrap.new(HttpClient); 
                  let responst= await (await client.GetStringAsync("https://msdn.microsoft.com/ru-ru/library/hh551745(v=vs.118).aspx")).Result(); 
              //Result вызываем как функцию 
              
              
              
              Для чего это нужно.

              Возьмем пример нынешнего Xamarin. Xamarin.Forms очень беден, поэтому многие делают общую логику отдельно (которая может быть сложной),
              а морду рисуют для каждой ОС отдельно.

              Сейчас для отличных от Win декстопов нет UI.
              Но например у тебя есть приложение на UWP или WPF. Можно выделить логику отдельно, а морду нарисовать на Angular 2
              Разница использования .Net кода только в том, что все методы асинхронные. При этом можно подписываться к событиям 1С,.Net Core. Динамическая компиляция класса обертки для получения событий .Net объекта в 1С. Для Вэб апи нужно городить SignalR или WebSocket.
              То есть перенести приложение достаточно легко, в отличие от web api-host.
                0

                То есть вы даже не догадались Task переводить в promise?

                  0
                  Это просто набросок. Кроме того Task можно использовать через ContinueWhith или WhenAll итд
                    0

                    А у вас уже реализована нормальная передача из js-кода в .Net-код делегатов?

                      –1
                      У меня есть только взаимодействие 1С с .Net Core. Но пока все это в стол. Хотя есть на стороне .Net поддержка и параметы массивы, ref параметры, автовывод дженерик типов, поиск методов расширений и опять же с дженериками.
                      Подержка событий через динамическую компиляцию оберток для событий. Просто так или иначе я один не то что не потяну, а нет смысла писать в стол.
                      А реализовать можно взаимодействие и с JS объектами через DynamicObject по аналогии с COM объектами http://infostart.ru/public/466196/
                      Просто нет подсчета ссылок, но можно только на время вызова держать ссылку
                        –1
                        Кроме того можно динамически скомпилировать нужный класс подписаться на событие из него получить ссылку на делегат и передать эту ссылку.
                         static Delegate ПолучитьДелегатПоТипу(IPropertyOrFieldInfo Свойcтво)
                                {
                                    var ReturnType = Свойcтво.GetPropertyType();
                        
                                    if (ReturnType == typeof(Action<string, string, object>)) return new Action<string, string, object>(ВызватьВнешнееСобытиеСОбъектом);
                        
                                    if (ReturnType == typeof(Action<string, string, string>)) return new Action<string, string, string>(AutoWrap.ВызватьВнешнееСобытие1С);
                        
                                    throw new Exception("Не не нужного типа  " + ReturnType.ToString());
                                }
                                public static void УстановитьДелегатДляВызоваВнешнегоСобытия(object объект, string ИмяДелегата)
                                {
                        
                                    var Свойcтво = НайтиСвойство(объект, ИмяДелегата);
                                    var value = ПолучитьДелегатПоТипу(Свойcтво);
                        
                                    Свойcтво.SetValue(объект, value);
                                }
                        
                        
                        
                        0
                        Но можно ввести служебное слово например async

                         let response= await client.async.GetStringAsync("https://msdn.microsoft.com/ru-ru/library/hh551745(v=vs.118).aspx")
                        
                          0

                          В этом нет смысла. Достаточно проверять возвращаемое значение на Task и отдельно его обрабатывать.

                            0
                            Смысл есть. Опять же мне нужен массив для Task.WhenAll. Кроме того это лишнее взаимодействие между нативом и Net. Не все так просто. То есть нужно в натив возвращать флаг, что это Task для того, что бы натив не вызывал событие, передавал ключ в Net а дальше для задачи вызывался await или ContinueWhith с событием которое вызывает метод натива с передачей ключа и результата.

                            Мне лично await client.async.GetStringAsync нравится.
                            Кроме того для дженерик методов где нельзя вывести тип у меня применяется in
                            например
                            C# вариант
                            var HtmlAnchorElement = document.QuerySelector<IHtmlAnchorElement>(rowSelector);
                            


                            может выглядеть так

                            let HtmlAnchorElement = avait doc.in(IHtmlAnchorElement).QuerySelector(rowSelector));
                            


                            Кроме того иногда нужно получить интерфейс (у него свои методы)

                            let obj= await obj.as("IEnumerable");
                            или
                            let obj= await obj.as(IEnumerable);
                            
                            
                              0

                              Вам не нужен Task.WhenAll потомучто есть Promise.all

                                0
                                Понятно, но это уже дикое смешение. На самом деле хоть какой то вариант заработал. Но проблема в том, что я 1С ник и занимаюсь в всободное время. .Net знаю неплохо, но вот с С++ и С очень мало практики. Кроме того привык к VS а например Debugging with Visual Studio только для VS 2010 и 2012. Под 2012 у меня не получилось скомпилировать даже пример. Но по обсуждению я так понимаю, что мало кому это нужно. А время на это тратить нужно и не мало. И больше не на реализацию, а на обучение плагинописанию.
                      0
                      Почитал, это все очень интересно. Можно подключать любую class Library с асинхронными методами?
                      Еще я нашел вот такой вот интересный проект: Edge.js, правда не знаю, как у него с производительностью и использованием памяти.
                        0

                        Нет, его решение позволяет подключить любую class Library с синхронными методами и (важно!) без событий и обратных вызовов.

                          0
                          А с другой стороны, было бы интересно иметь возможность работы как асинхронно, так и синхронно, при этом имея возможность удобно маппить объекты TS в объекты c#(пусть и в виде DTO, уже что-то), и вызывая любую функцию с любой сигнатурой, а не Func<object,Task
                          –1
                          Если это к моей библиотеке то она посзволяет все и события и обратные вызовы через динаимескую компиляцию, которая автоматически создает обертки.

                           public static object СоздатьОберткуДляСобытий(Object ОбъектССобытиями)
                                  {
                          
                                      Type тип = ОбъектССобытиями.GetType();
                                      Type genType = typeof(КомВраперДляСобытий<>);
                                      Type constructed = genType.MakeGenericType(new Type[] { тип });
                                      var ИмяСвойства = "СоздательОбертки";
                          
                                        var fi = constructed.GetField(ИмяСвойства);
                                        Delegate функция = (Delegate)fi.GetValue(null);
                                       // Получили делегат
                                       // И из него получим объект обертку для событий который передадим в 1С для хранения ссылки на него
                                      object обертка = функция.DynamicInvoke(new Action<string,string,object>(ВызватьВнешнееСобытиеСОбъектом), ОбъектССобытиями);
                                      return обертка;
                          
                                  }
                          


                          Правда пока обертка делается ко всем событиям. Но можно ограничить.
                          0
                          Огромное спасибо! Посмотрю
                          0
                          Кстати мне посоветовали cef / JavaScriptIntegration

                          Его наверное и возьму за основу
                        +2
                        Я 16 лет занимаюсь разработкой на 1С, немного меньше на C#.
                        НО! Никогда не пытаюсь смешивать английский и русский в коде. Ведь всего-то надо подумать о названии метода по английски и освоить для себя <summary/>.
                        А то, задумайтесь, как 1С-программист, как бы вы работали с модулями конфигурации, если бы часть переменных и методов были на английском, часть на французском, а все то, что касается встроенного языка (и прикладных объектов) — по-русски.
                          +1
                          Так-то и именование методов в lowerCamelCase: sendEcho, addMessage, etc. — какой-то Java-style.
                            0
                            Там суть такова, что для JSON и наименования методов практикуется Кэмел нотация. А на стороне клиента все методы будут в Кэмел. Поэтому, что бы не путаться проще задать им названия сразу в Кэмел
                              +2
                              Там же вроде как есть аттрибут для именования полей со стороны клиента(Именуем в Паскаль кейс, вешаем аттрибут для клиентского именования и вуаля).
                                0
                                Можно и так. Просто я в статье ориентировался на интерфейс который создавался на стороне Asp.Net и по нему создавался класс на TS. В дальнейшем можно кодогенератор прикрутить как для TS так и .Net клиентов
                            0
                            А у меня так повсеместно. Я использую Использование классов .Net в 1С для новичков. А там все классы на английском.
                            Кроме того и сама 1С использует Руслиш. HttpСоединение, ФабрикаXDTO, ЗаписьXML итд.
                            Просто привыкаешь самодокументироваться на русском.
                            Просто я не очень хорошо знаю английский, поэтому мне приходится тратить время на осмысленные названия. И при этом давать комментарии на русском.
                              +2
                              Не стоит так делать. Стиль 1С — не лучшая практика. С# далеко не 1С, может стоит в нем придерживаться стандартов(английские название, camelcase, etc.)?
                            –1
                            Так в этой статье всего один метод и один параметр.
                            Кроме того самодокументация лучше чем английское название и русский комментарий, особенно сложных названий, там где на английском будет неоднозначное определение. Но я уже кучу копий по поводу руслиша сломал. Можно почитать все предыдущие статьи.
                              0
                              В статье нет упоминания, как добавить SignalR в проект .Net Core. А между тем, на данный момент это не такая простая задача. Так что, статья точно не «для чайников».
                                0
                                Согласен. Упустил. На данный момент есть описание для .VS 2017 с Angular 2
                                Но и тогда можно найти в интернете Real-time applications using ASP.NET Core, SignalR & Angular


                                Единственно, что нужно добавить либо в студии в диспетчере пакетов NuGet источники пакетов ссылку на

                                Asp.Net Core NuGet «https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json» /

                                либо в NuGet.config file

                                <packageSources>
                                    <add key="AspNetCore" value="https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json" />
                                    <add key="NuGet" value="https://api.nuget.org/v3/index.json" />
                                  </packageSources>
                                
                                

                                А все ссылки на нужные пакеты есть в project.json

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

                              Самое читаемое