Продолжаем кодить на C#. «Hello, world!» в контейнере.

usawa0

Client
Регистрация
29.11.2019
Сообщения
59
Благодарностей
22
Баллы
8
Такс, все сделал по гайду и отдал голос, держу в курсе. На VS19 полет нормальный. Буду изучать, что к чему. Показалось, что много нужно прописывать, если так для каждого проекта. Полагаю, это можно как-то автоматизировать. На первых порах, у меня вопрос, как сделать несколько проектов в одном решении. Так же, интересно посмотреть как используют визуалку в реальных условиях, на примере простенького регистратора.
 
  • Спасибо
Реакции: SHILY

SHILY

Client
Регистрация
05.06.2016
Сообщения
258
Благодарностей
307
Баллы
63
Такс, все сделал по гайду и отдал голос, держу в курсе. На VS19 полет нормальный. Буду изучать, что к чему. Показалось, что много нужно прописывать, если так для каждого проекта. Полагаю, это можно как-то автоматизировать. На первых порах, у меня вопрос, как сделать несколько проектов в одном решении. Так же, интересно посмотреть как используют визуалку в реальных условиях, на примере простенького регистратора.
Благодарю! Это отлично, что всё ок на 19-й.
Да я к прошлому конкурсу шаблонов хотел вложить один проект, но возникли проблемы технического и временного характера:bk: Скорее всего, к новому конкурсу что-нибудь сварганю :-)


Показалось, что много нужно прописывать, если так для каждого проекта.
На самом деле, несколько раз написать и всё потом будет автоматом выходить - дело привычки.
Тем более, даже пакеты не нужно будет ставить самому, достаточно написать метод, с тем же самым "IHostBuilder", нажать "Alt+Enter" и интеллисенс сам предложит найти и установить нужный пакет :D Да и к тому же, начальная заморочка того стоит, ибо процесс написания кода становится более простым, а проект - гибким, поскольку не нужно запариваться сильно по поводу внешних зависимостей :-)
 
Последнее редактирование:
  • Спасибо
Реакции: nrg8601

usawa0

Client
Регистрация
29.11.2019
Сообщения
59
Благодарностей
22
Баллы
8
несколько раз написать и всё потом будет автоматом выходить - дело привычки.
Да, тоже так думаю. Главное, что я понял, к чему именно привыкать 8-)

к новому конкурсу что-нибудь сварганю
Однозначно голосую.
 
  • Спасибо
Реакции: SHILY

indigo666

Client
Регистрация
10.05.2014
Сообщения
1 154
Благодарностей
374
Баллы
83
Слишком резкий переход с уровня первой темы, до уровня этой. Продолжением навряд ли можно назвать ) Там базовые понятия и операции. Тут резкий переход к сложному
 

SHILY

Client
Регистрация
05.06.2016
Сообщения
258
Благодарностей
307
Баллы
63
Слишком резкий переход с уровня первой темы, до уровня этой. Продолжением навряд ли можно назвать ) Там базовые понятия и операции. Тут резкий переход к сложному
Во-первых, а чего тянуть кота за причинное место? :D
Собственно, это для тех, кому было мало, и, кто ищет новые подходы к разработке...:-)
Во-вторых, вполне себе продолжение C#, DI - не такая сложная штука. По сути, для тех, кому там всё было просто, поделился годным (по сугубо личному мнению) подходом :-) А, в-третьих, просто чуть-чуть пошутковал:al:
 
Последнее редактирование:

amyboose

Client
Регистрация
21.04.2016
Сообщения
2 312
Благодарностей
1 191
Баллы
113
Уточнение по поводу Scoped.
Автор написал: Scoped (AddScoped) - инстанс сервиса создается единожды для каждого запроса.
Но на самом деле Scoped объекты живут в рамках одной операции Scope. Просто в рамках ASP NET практически всегда Scope - это границы одного запроса. На деле же можно создавать самому Scope и регулировать время жизни вручную.

Например, прямо сейчас в коде пишу такое:
C#:
using IServiceScope serviceScope = _services.CreateScope();
IServiceProvider provider = serviceScope.ServiceProvider;
 
  • Спасибо
Реакции: djaga и SHILY

SHILY

Client
Регистрация
05.06.2016
Сообщения
258
Благодарностей
307
Баллы
63
Уточнение по поводу Scoped.
Я, кстати, не знал про ручную регулировку времени жизни, пока не было нужды это использовать, но информация очень полезная на будущее.
Благодарю за уточнение! :-)
 

ZSharp

Client
Регистрация
29.09.2013
Сообщения
395
Благодарностей
126
Баллы
43
Отличная статья.
Редко захожу, с голосованием опоздал (

Нет проблем с многопотоками?
Зенка странно использует статику, не знаю как работает внутри DI но есть предположения, что это кладётся, а потом дёргается из какого-то статического объекта.
 

SHILY

Client
Регистрация
05.06.2016
Сообщения
258
Благодарностей
307
Баллы
63
Отличная статья.
Редко захожу, с голосованием опоздал (

Нет проблем с многопотоками?
Зенка странно использует статику, не знаю как работает внутри DI но есть предположения, что это кладётся, а потом дёргается из какого-то статического объекта.
Спасибо!

Что-то даже об этом не думал, всегда почему-то было в голове, что IoC корректно работает с потоками и объектами лучше нас (скорее всего, где-то прочитал и забыл, но в подкорке отложилось:ap:).
Сам ещё не успел покопаться под капотом у IoC, но нет, проблем быть не должно, даже с уровнем жизни Singleton:-)
В принципе, одна из задач IoC и DI - избавить код от статики.

Но, чтоб вам жилось спокойнее, решил проверить:
-Создал класс, где будут лежать id потоков
93513


-Закинул в конфигу
93514


-Запросил объекты у провайдера сервисов и положил туда данные (с таймаутом, чтоб все успели)
93515


-Сделал вывод данных
93516


-Запустил 10 выполнений в 10 потоков
93517
 

ZSharp

Client
Регистрация
29.09.2013
Сообщения
395
Благодарностей
126
Баллы
43
  • Спасибо
Реакции: SHILY

Serjio Leone

Client
Регистрация
20.09.2017
Сообщения
114
Благодарностей
84
Баллы
28
Ага, мне просто неудобно держать две студии (19 и 22), так что, пришлось на тестовый билд сесть, ибо у меня VS 2022. Всё было хорошо, пока студия не обновилась до 17.2 :-)
То же самое обновил VS 2022 до версии 17.2.4
Теперь ошибка подключения... приходится делать танцы с бубнами чтоб подключить.
 

SHILY

Client
Регистрация
05.06.2016
Сообщения
258
Благодарностей
307
Баллы
63
  • Спасибо
Реакции: Serjio Leone

Serjio Leone

Client
Регистрация
20.09.2017
Сообщения
114
Благодарностей
84
Баллы
28

Mikhail B.

Client
Регистрация
23.12.2014
Сообщения
14 415
Благодарностей
5 454
Баллы
113
Хотел изучить после курса @Brabus_bots, но понял что еще рановато. Может быть потом вернусь. Спасибо за статью)
 
  • Спасибо
Реакции: Brabus_bots и SHILY

SHILY

Client
Регистрация
05.06.2016
Сообщения
258
Благодарностей
307
Баллы
63
Хотел изучить после курса @Brabus_bots, но понял что еще рановато. Может быть потом вернусь. Спасибо за статью)
Когда начнешь писать проекты в студии, то очень рекомендую разобраться с данной темой, данный инструмент и подход очень спасают в относительно больших проектах :-)
 
  • Спасибо
Реакции: Mikhail B.

Mikhail B.

Client
Регистрация
23.12.2014
Сообщения
14 415
Благодарностей
5 454
Баллы
113
Когда начнешь писать проекты в студии, то очень рекомендую разобраться с данной темой, данный инструмент и подход очень спасают в относительно больших проектах :-)
Да я как раз смотрю курсы @sibbora, там более глубокое изучение основ C#, как искать готовые коды и т.д. Уже когда с этим разбирусь, можно полноценно в VS переходить.
 
  • Спасибо
Реакции: SHILY

SHILY

Client
Регистрация
05.06.2016
Сообщения
258
Благодарностей
307
Баллы
63
Да я как раз смотрю курсы @sibbora, там более глубокое изучение основ C#, как искать готовые коды и т.д. Уже когда с этим разбирусь, можно полноценно в VS переходить.
Плюсую! Сам у него курс проходил ещё давно, очень толковый мужик и материал преподносит классно :-)
 
  • Спасибо
Реакции: Mikhail B.

ZSharp

Client
Регистрация
29.09.2013
Сообщения
395
Благодарностей
126
Баллы
43
Вечер в хату, автоматизаторы всего и вся!
Посмотреть вложение 92264

Данное писание можно считать продолжением статьи «Начинаем кодить на c#» от господина @Brabus_bots.
Собственно, это для тех, кому было мало, и, кто ищет новые подходы к разработке...:-)

Долго ходить вокруг да около не буду, начну сразу лить воду, готовьтесь принимать душ…
(ʘ ͜ʖ ʘ)

Вот мы уже знаем, что такое переменные, циклы и goto, который не раз выстреливал нам в левое колено…
Да и вообще мы крутые ребята, которые умеют писать “Hello, world” закрытыми глазами!
Но всё-таки мы - зеннолабовцы - с форума «Сообщество профессионалов автоматизации», а значит нам всегда хочется знать больше!
Мы хотим покорять новые горизонты! Что ж, погнали! Время писать «Hello, world!» используя контейнеры
(⊙ˍ⊙)


Что у нас по плану?
  • Кто такой этот магический кубик «Проект Visual Studio»
  • Познакомиться с Dependency Injection (DI), IoC-контейнерами и зачем оно нам нужно...
  • Напишем мелкий и бесполезный проект в Visual Studio 2022 для демонстрации использования DI

Если вам здесь всё понятно, то проблем в понимании материала ниже возникнуть не должно.
Код обычного C# кубика:
// дёргаем локальный метод для отправки полученного текста в лог
Print(GetTextFromSite());

// отправка сообщения в лог
void Print(string sendText)
{
    project.SendToLog(sendText, ZennoLab.InterfacesLibrary.Enums.Log.LogType.Info, true, ZennoLab.InterfacesLibrary.Enums.Log.LogColor.Green);
}

// получение текста текста с сайта
string GetTextFromSite()
{
    // получаем страницу вики
    var doc = new Global.Zennolab.HtmlAgilityPack.HtmlWeb().Load("https://en.wikipedia.org/wiki/%22Hello,_World!%22_program");
    
    // получаем текст по xPath
    var hw = doc.DocumentNode?.SelectSingleNode("//div[contains(@id, 'content')]/descendant::div[contains([USER=46442]@class[/USER], 'c#')]")?.InnerText;
    
    // выдёргиваем нужное регуляркой и возвращаем
    return hw != null ? Regex.Match(hw, @"(?<=&quot;).*?(?=&quot;)").Value : string.Empty;
}


Начнем с небольшой теории, для большего понимания:
Универсальный узел - инкапсулирует все ресурсы приложения и функциональные возможности времени существования
(согласен, мелкомягкие пишут как-то страшно, но всё, что нам нужно будет – пихнуть заранее созданную конфигурацию сервисов в "IHostBuilder" и всё забилдить ключевым словом "Build").

Инкапсуляция – механизм сокрытия какой-либо реализации/деталей от лишних глаз и предоставление итоговому клиенту
только того функционала, который мы задумывали (для этого используются модификаторы доступа, такие как: private, internal, public и т.д.).
Посмотреть вложение 92270

Dependency Injection (DI) – процесс предоставления внешней зависимости программному компоненту.
В проекте, осуществлять инъекции будем через конструктор класса. Следует знать, что это не единственная возможность,
есть и другие способы: инъекция через метод и свойств, но, как говорил мой сэмпай - “это бэд практис” -
¯\_(ツ)_/¯.

Зависимость — это любой объект, от которого зависит другой объект (например, если мы в методе используем инстанс зенки,
то сам метод будет зависим от этого инстанса зенки, получается, инстанс – наша внешняя зависимость).

IoC-контейнер — это библиотека/фреймворк которая позволит нам упростить и автоматизировать написание кода с использованием данного подхода.
Она будет создавать и предоставлять нам готовые инстансы в проекте (если мы их конечно же зарегистрировали в конфиге).
Основные IoC библиотеки, которые предоставляют нам магию: «Microsoft.Extensions.DependencyInjection» и «Autofac» (мы будем использовать реализацию от Microsoft).

Интерфейс – это контракт, некая абстракция, который должен реализовать класс (например, в созданном проекте,
у нас есть класс “Program”, который реализует интерфейс “IZennoExternalCode”, то есть, “Program” должен по контракту реализовать
метод “Execute”, который прописан в интерфейсе “IZennoExternalCode”, а как именно это будет реализовано – уже наша забота,
а не разработчиков ZennoPoster (они и так лапочки).


Подготовка инструментов:
(1) Медлить не будем, поэтому, сразу врываемся с ноги на сайт Microsoft, вежливо говорим “Ня, аригато”, качаем Visual Studio 2022 и устанавливаем её по инструкциям из интернетов.
Посмотреть вложение 92231
Посмотреть вложение 92233

Запускаем скаченный “Visual Studio Setup” и выбираем “.NET Desktop Development” (не уверен, но этого должно хватить)
Посмотреть вложение 92236

Проекты для ZP у нас будут создаваться на древнем .NET Framework 4.6.2, так что, на всякий случай ставим эти пакеты тоже.
Посмотреть вложение 92240
Тыкаем "Install" (у меня "Modify", так, как студия уже установлена)

(2) Создаём новый проект в “ProjectMaker” и добавляем сие чудо-кубик.
Посмотреть вложение 92246

(3) Кликаем на создание нового проекта в свойствах кубика.
Посмотреть вложение 92247

(4)Заполняем поля и кликаем “OK”
Посмотреть вложение 92248
“Название решения” – даём более общее название, ибо там может быть много проектов (решение – это то, что объединяет проекты).
“Название проекта” – даём более конкретное название, там будет наш код (повторять название, как я – не обязательно, просто люблю такое соблюдение неймспейсов).

То есть, такое именование будет так же корректным.
Посмотреть вложение 92249

А вот и произошла магия - наш первый проект в Visual Studio создан.
Посмотреть вложение 92271

П.С. Наши свойства кубика будут заполнены автоматически и в будущем мы сможем спокойно подключаться к проекту.
Посмотреть вложение 92252


Примечание:
Замечу, мы должны выставить в ProjectMaker и Visual Studio тёмную тему.
Для чего? Ходят слухи, что иначе программы могут работать некорректно…
(⊙ˍ⊙)
Сделали?! Ну всё, теперь, мы точно готовы двигаться дальше…(¬‿¬)


Практика:
Посмотреть вложение 92261

Вообще, всё интуитивно понятно, но пакеты тут, если что
Посмотреть вложение 92263
Посмотреть вложение 92253
(1)
Я создал тут папочку для вас, в которую добавил новый статический класс.
(2) Так же добавляем в него юзинг установленной библиотеки для работы с IoC.
(3) В классе добавляем статический метод расширения для "IServiceCollection", где будет проходить наша регистрация сервисов
для последующего предоставления в качестве внешних зависимостей. На данный момент, там зарегистрированы: инстанс браузера и инстанс зенки.

Посмотреть вложение 92254
(1) Ключевое слово “this”, в данном контексте, говорит нам о том, что это метод расширения, а расширяем мы "IServiceCollection".
То есть, когда в следующий раз, мы тыкнем « . » после объекта типа "IServiceCollection" (правильнее сказать, объекта, который реализовывает данный интерфейс,
но нам это знать сейчас не обязательно), то у нас будет доступен наш метод “AddApplication” в который мы должны будем передать "Instance" и "IZennoPosterProjectModel"
(первый параметры – IServiceCollection – передавать не нужно, поскольку, именно у его мы вызываем этот метод и он будет передан автоматически).
П.С. Методы расширения могут быть только в статическом классе и статическом методе.
(2) Тут мы добавляем/регистрируем наши сервисы/инстансы в рамках жизненного цикла называемого “Scoped
(он, кстати, написан тоже, как метод расширения; это видно на всплывашке при наведении курсора на метод и ключевому слову “extension”).
Transient (AddTransient) - инстанс сервиса создается каждый раз, когда его запрашивают. Этот жизненный цикл лучше всего подходит для легковесных, не фиксирующих состояние, сервисов.
Scoped (AddScoped) - инстанс сервиса создается единожды для каждого запроса.
Singleton (AddSingleton) - инстанс сервиса создается при первом запросе (или при запуске ConfigureServices, если вы указываете инстанс там), а затем каждый последующий запрос будет использовать этот же инстанс.

Источник: Разница между AddTransient, AddScoped и AddSingleton в ASP.NET Core / Хабр (habr.com)

Могу ошибаться, но, в рамках зенки, "Scoped" и "Singleton" - будет практически одно и тоже.
(3) "IServiceCollection" – это возвращаемый тип (то есть, мы используем метод расширения для "IServiceCollection" и его же возвращаем, таким образом, это образует Fluent Interface/Fluent Api).
(4) Всё это входные параметры метода (сигнатура метода).

Ещё чуть-чуть разжую, на всякий…)
Посмотреть вложение 92255
(1) Это сервисы, которые мы регистрируем.
(2) А это мы запихиваем готовые инстансы этих сервисов (обычно, контейнер сам нам создает и предоставляет инстансы, но, в данном случае,
нам нужны уже готовые, которые пришли прямиком из ZennoPoster или ProjectMaker).

Чтоб всё у нас завелось - я вернулся в наш класс “Program” и…
Посмотреть вложение 92256
(1) Добавляем юзинг хостинга (пакета, который мы ранее должны были установить).
(2) Так же добавляем стандартный метод для создания хост билдера, в который передаём инстансы пришедшие из зенки.
(3) И именно тут используем наш, выше написанный, метод расширения.
services” – это и есть тот самый первый параметр “IServiceCollection” (можете сами потом убедиться, наведя мышку на этот объект).
(4) Разумеется, вызываем метод для создания хост билдера.
(5) А следом вызываем и метод сборки.

Всё! Теперь наш контейнер сможет нам творить магию в виде инъекций инстансов зарегистрированных сервисов в конструкторы классов,
то бишь, будет предоставлять нам внешние зависимости
(~ ̄▽ ̄)~
Собственно, толку пока не много, нужно вывести наш первый “Hello, world!”


«Hello, world!» в контейнере:

Посмотреть вложение 92257
(1) Собственно, мы создали наш первый условный сервис (да, обычный класс).
(2) Добавили метод “Run”, который будем вызывать для старта сервиса (название может быть любым).
(3) Добавили конструктор класса, именно сюда IoC будет производить нашу инъекцию, когда мы обратимся к методу “Run”.
Заметьте, что у конструктора класса “0 references”, а это значит, что на него никто не ссылается, а он будет вызваться…
Что-то это, если не магия?! Атеистам шах и мат!
(⊙_(⊙_⊙)_⊙)

Посмотреть вложение 92258
(1) Теперь регистрируем созданный сервис в нашем регистраторе.
Опять же, обратите внимание, что никакого инстанса мы не пихаем,
IoC за нас будет создавать его и предоставлять конструктору нужные внешние зависимости.
Получается так, что в конструкторе сервиса (созданном нами классе) мы можем использовать любой порядок внешних зависимостей.

Посмотреть вложение 92259
(1) Всё, теперь нам требуется просто запросить наш сервис у хоста (если быть точным, то у "IServiceProvider", который обитает в свойствах "IHost".
Не устану повторять: мы запрашиваем сервис у которого есть конструктор, но ничего не передаём для самого конструктора, IoC сам всё сделает исходя из конфига.
По поводу "IServiceProvider": мы так же можем использовать его в конструкторах не статических классов, если нам потребуется
выдернуть какой-то инстанс сервиса похожим образом, при этом, не нужно заранее регистрировать "IServiceProvider" в нашем конфиге, он уже зарегистрирован под капотом.
(2) И вызвать тот самый метод “Run

Теперь просто запускаем проект в зенке и наблюдаем за нашим итоговым результатом - «Hello, world!» - в контейнере!
Посмотреть вложение 92260

Задачка на подумать: это не обязательно, но, в идеале, теперь неплохо бы было избавиться от завязки на реализации,
и завязаться на абстракции, то есть, на интерфейсе, например “IHelloWorldService”, а как это сделать – придётся решать вам самим.


¯\_( ͡° ͜ʖ ͡°)_/¯

Резюме по Dependency Injection и IoC-контейнеру:
DI и IoC – вещи не сложные, но это прям то, что доктор прописал для больших проектов
планируемых расширяться в будущем (для мелких так же подойдёт), где есть куча зависимостей и сложной реализации и т.д.
Данный подход к разработке проекта: упрощает написание кода, делает его более гибким, поддерживаемым, расширяемым и более приятным.
К тому же, не нужно беспокоиться за стейт объектов и хранить всё по статическим переменным /ᐠ。ꞈ。ᐟ\

Резюме по кубику “Проект Visual Studio”:
Очевидно, что данная фича будет очень полезна тем, кто начал начал использовать C# в своих проектах.
Разработчики ZennoPoster делают шаги в правильном направлении (осталось открыть API
для решения ReCaptcha2 из Visual Studio, - цены бы вам тогда не было).
Написание проектов в VS ускоряет и упрощает разработку за счёт умного "Интеллисенс", а "Nuget package manager" - открывает доступ к огромному количеству библиотек,
да и дебажить в студии просто волшебно (◡‿◡)


П.С. Это была моя первая статья, и это был лишь косплей на программиста, не судите строго...:-)
Посмотреть вложение 92266


Visual Studio 2022 v17.2.1
ZennoPoster v7.7.1.0
(это минимальная версия для поддержки 22-й студии)
Обновился до последней зенки из-за этой статьи.

Ну в студии всё работает, а как запуститься без студии?

Компилировать в один файл, что-то там не работает. Файл 12кб получается.
"Выполнение действия ExecuteVisualStudioProject. Код возврата -3002 .Tester2.dll execution error
Не удалось загрузить файл или сборку ""Microsoft.Extensions.Hosting.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60"" либо одну из их зависимостей. Найденное определение манифеста сборки не соответствует ссылке на сборку. (Исключение из HRESULT: 0x80131040)"

Пошёл другим путём.
Добавлю думаю через ExternalAssemblies dll и подключусь из кода. (если без DI просто вывести лог, то работает).
Но в этом случае зависимостей конечно уйма, и не работает.

Выполнение действия CSharp OwnCode. Не удалось загрузить файл или сборку "Microsoft.Extensions.Hosting.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60" либо одну из их зависимостей. Найденное определение манифеста сборки не соответствует ссылке на сборку. (Исключение из HRESULT: 0x80131040)
Добавил изначально 3 основные dll, ошибка.
Потом добавил ВСЕ dll из папки проекта (даже Logging, которые с каким-то пакетом подгрузились)
Microsoft.Bcl.AsyncInterfaces.dll
Microsoft.Extensions.Configuration.dll
Microsoft.Extensions.Configuration.Abstractions.dll
Microsoft.Extensions.Configuration.Binder.dll
Microsoft.Extensions.Configuration.CommandLine.dll
Microsoft.Extensions.Configuration.EnvironmentVariables.dll
Microsoft.Extensions.Configuration.FileExtensions.dll
Microsoft.Extensions.Configuration.Json.dll
Microsoft.Extensions.Configuration.UserSecrets.dll
Microsoft.Extensions.DependencyInjection.dll
Microsoft.Extensions.DependencyInjection.Abstractions.dll
Microsoft.Extensions.FileProviders.Abstractions.dll
Microsoft.Extensions.FileProviders.Physical.dll
Microsoft.Extensions.FileSystemGlobbing.dll
Microsoft.Extensions.Hosting.dll
Microsoft.Extensions.Hosting.Abstractions.dll
Microsoft.Extensions.Logging.dll
Microsoft.Extensions.Logging.Abstractions.dll
Microsoft.Extensions.Logging.Configuration.dll
Microsoft.Extensions.Logging.Console.dll
Microsoft.Extensions.Logging.Debug.dll
Microsoft.Extensions.Logging.EventLog.dll
Microsoft.Extensions.Logging.EventSource.dll
Microsoft.Extensions.Options.dll
Microsoft.Extensions.Options.ConfigurationExtensions.dll
Microsoft.Extensions.Primitives.dll
System.Buffers.dll
System.Diagnostics.DiagnosticSource.dll
System.Memory.dll
System.Numerics.Vectors.dll
System.Runtime.CompilerServices.Unsafe.dll
System.Text.Encodings.Web.dll
System.Text.Json.dll
System.Threading.Tasks.Extensions.dll
System.ValueTuple.dll

Ошибка та же.

Ошибка вообще одна и та же, хоть при использовать как dll, хоть при подключении через общий код.

Кроме helloworld что-то запускается вообще?
Что делаю не так?
 

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 790
Благодарностей
5 720
Баллы
113
Ну в студии всё работает, а как запуститься без студии?
Ну так не зря же сейчас все в Визуалку переходят, потому, что добавить dll в зенку это то еще приключение.
 

ZSharp

Client
Регистрация
29.09.2013
Сообщения
395
Благодарностей
126
Баллы
43
Обнаружил что в папке с dll нет "Microsoft.NETFramework.ReferenceAccemblies".
Где взять этот файл?
В менеджере NuGet она добавлена, но в папке сборки её нет почему-то.

Ну так не зря же сейчас все в Визуалку переходят, потому, что добавить dll в зенку это то еще приключение.
Зато есть красивая кнопка )))
94565
 

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 790
Благодарностей
5 720
Баллы
113

BAZAg

Client
Регистрация
08.11.2015
Сообщения
1 788
Благодарностей
2 453
Баллы
113
Спасибо!

Что-то даже об этом не думал, всегда почему-то было в голове, что IoC корректно работает с потоками и объектами лучше нас (скорее всего, где-то прочитал и забыл, но в подкорке отложилось:ap:).
Сам ещё не успел покопаться под капотом у IoC, но нет, проблем быть не должно, даже с уровнем жизни Singleton:-)
В принципе, одна из задач IoC и DI - избавить код от статики.

Но, чтоб вам жилось спокойнее, решил проверить:
-Создал класс, где будут лежать id потоков
Посмотреть вложение 93513

-Закинул в конфигу
Посмотреть вложение 93514

-Запросил объекты у провайдера сервисов и положил туда данные (с таймаутом, чтоб все успели)
Посмотреть вложение 93515

-Сделал вывод данных
Посмотреть вложение 93516

-Запустил 10 выполнений в 10 потоков
Посмотреть вложение 93517
Не знаю как форум процитирует сообщение, из-за чего сделал скриншоты с отметкой.
После того, как люди прочитали Ваше сообщение - у всех дружно сложилось мнение, что контейнер каким-то магическим образом решает проблемы с многопоточностью.
Но, это совершенно не так.
94567


Скриншот ниже с документации:
94568


Как проверить?
Зарегистрируйте как Singleton например Random rand = new Random(); (у Вас там сейчас список Thread).
И вместо добавления данных в список - попробуйте подёргать rand.Next(100);
Запустите шаблончик в 100 потоков минут на 5-10.
Из-за многопоточности внутреннее состояние rand будет нарушено - и любой вызов будет возвращать 0, что и подтверждает что Singleton что в контейнере, что без него должен строиться так, чтобы сам объект был потокобезопасным.

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

ZSharp

Client
Регистрация
29.09.2013
Сообщения
395
Благодарностей
126
Баллы
43
это да... но это не добавление в зенку. это создает сборку и использует ее из той папки в которой она находится.
а если добавлять через GAC и общий код, то может и не получиться.
Так пробовал и это сделать не работает
Компилировать в один файл, что-то там не работает. Файл 12кб получается.
Цель моего сообщения не в том, чтобы упрекать или ещё чего - я просто хочу чтобы в подкорке у людей отложилось что не важно кто управляет зависимостями - нужно думать о потокобезопасности в любом случае.
:ay:
 

ZSharp

Client
Регистрация
29.09.2013
Сообщения
395
Благодарностей
126
Баллы
43

ZSharp

Client
Регистрация
29.09.2013
Сообщения
395
Благодарностей
126
Баллы
43
Со сборкой в однофайловую dll получилось.
Оказалось не такая уж она и одна эта dll ))
Нужно добавлять все dll из проекта, тогда работает. (детская ошибка ;-))
Ну если точнее, то запустилось.
Буду пробовать на сколько работает.
 

SHILY

Client
Регистрация
05.06.2016
Сообщения
258
Благодарностей
307
Баллы
63

SHILY

Client
Регистрация
05.06.2016
Сообщения
258
Благодарностей
307
Баллы
63
Цель моего сообщения не в том, чтобы упрекать или ещё чего - я просто хочу чтобы в подкорке у людей отложилось что не важно кто управляет зависимостями - нужно думать о потокобезопасности в любом случае.
Видимо я не до конца разобрался в вопросе, приношу свои извинения, если кого-то ввел в заблуждение. А Вас благодарю за информативный комментарий :-)
 
  • Спасибо
Реакции: Ilshakin и BAZAg

Dozer009

Client
Регистрация
26.11.2012
Сообщения
156
Благодарностей
6
Баллы
18
Благодарю за статью!
на windows 7 ставил под VS2022 - не пошла на ZP 7.7.0.0 !( не прочитал, что минимальные требования 7.7.1.0 надо для 2022 )
откатил на VS2019, запустил начала просить 4.6.2, поставил, все ок запустилась! будем изучать С# под VS
теперь нужна статья Пишем первый проект под ZP via MVS
собрал свой первый проект из всех снипетов)) , которые быстро попались под руку
 

Вложения

Последнее редактирование:

Azakim

Client
Регистрация
25.07.2021
Сообщения
165
Благодарностей
31
Баллы
28
Вечер в хату, автоматизаторы всего и вся!
Посмотреть вложение 92264

Данное писание можно считать продолжением статьи «Начинаем кодить на c#» от господина @Brabus_bots.
Собственно, это для тех, кому было мало, и, кто ищет новые подходы к разработке...:-)

Долго ходить вокруг да около не буду, начну сразу лить воду, готовьтесь принимать душ…
(ʘ ͜ʖ ʘ)

Вот мы уже знаем, что такое переменные, циклы и goto, который не раз выстреливал нам в левое колено…
Да и вообще мы крутые ребята, которые умеют писать “Hello, world” закрытыми глазами!
Но всё-таки мы - зеннолабовцы - с форума «Сообщество профессионалов автоматизации», а значит нам всегда хочется знать больше!
Мы хотим покорять новые горизонты! Что ж, погнали! Время писать «Hello, world!» используя контейнеры
(⊙ˍ⊙)


Что у нас по плану?
  • Кто такой этот магический кубик «Проект Visual Studio»
  • Познакомиться с Dependency Injection (DI), IoC-контейнерами и зачем оно нам нужно...
  • Напишем мелкий и бесполезный проект в Visual Studio 2022 для демонстрации использования DI

Если вам здесь всё понятно, то проблем в понимании материала ниже возникнуть не должно.
Код обычного C# кубика:
// дёргаем локальный метод для отправки полученного текста в лог
Print(GetTextFromSite());

// отправка сообщения в лог
void Print(string sendText)
{
    project.SendToLog(sendText, ZennoLab.InterfacesLibrary.Enums.Log.LogType.Info, true, ZennoLab.InterfacesLibrary.Enums.Log.LogColor.Green);
}

// получение текста текста с сайта
string GetTextFromSite()
{
    // получаем страницу вики
    var doc = new Global.Zennolab.HtmlAgilityPack.HtmlWeb().Load("https://en.wikipedia.org/wiki/%22Hello,_World!%22_program");
    
    // получаем текст по xPath
    var hw = doc.DocumentNode?.SelectSingleNode("//div[contains(@id, 'content')]/descendant::div[contains([USER=46442]@class[/USER], 'c#')]")?.InnerText;
    
    // выдёргиваем нужное регуляркой и возвращаем
    return hw != null ? Regex.Match(hw, @"(?<=&quot;).*?(?=&quot;)").Value : string.Empty;
}


Начнем с небольшой теории, для большего понимания:
Универсальный узел - инкапсулирует все ресурсы приложения и функциональные возможности времени существования
(согласен, мелкомягкие пишут как-то страшно, но всё, что нам нужно будет – пихнуть заранее созданную конфигурацию сервисов в "IHostBuilder" и всё забилдить ключевым словом "Build").

Инкапсуляция – механизм сокрытия какой-либо реализации/деталей от лишних глаз и предоставление итоговому клиенту
только того функционала, который мы задумывали (для этого используются модификаторы доступа, такие как: private, internal, public и т.д.).
Посмотреть вложение 92270

Dependency Injection (DI) – процесс предоставления внешней зависимости программному компоненту.
В проекте, осуществлять инъекции будем через конструктор класса. Следует знать, что это не единственная возможность,
есть и другие способы: инъекция через метод и свойств, но, как говорил мой сэмпай - “это бэд практис” -
¯\_(ツ)_/¯.

Зависимость — это любой объект, от которого зависит другой объект (например, если мы в методе используем инстанс зенки,
то сам метод будет зависим от этого инстанса зенки, получается, инстанс – наша внешняя зависимость).

IoC-контейнер — это библиотека/фреймворк которая позволит нам упростить и автоматизировать написание кода с использованием данного подхода.
Она будет создавать и предоставлять нам готовые инстансы в проекте (если мы их конечно же зарегистрировали в конфиге).
Основные IoC библиотеки, которые предоставляют нам магию: «Microsoft.Extensions.DependencyInjection» и «Autofac» (мы будем использовать реализацию от Microsoft).

Интерфейс – это контракт, некая абстракция, который должен реализовать класс (например, в созданном проекте,
у нас есть класс “Program”, который реализует интерфейс “IZennoExternalCode”, то есть, “Program” должен по контракту реализовать
метод “Execute”, который прописан в интерфейсе “IZennoExternalCode”, а как именно это будет реализовано – уже наша забота,
а не разработчиков ZennoPoster (они и так лапочки).


Подготовка инструментов:
(1) Медлить не будем, поэтому, сразу врываемся с ноги на сайт Microsoft, вежливо говорим “Ня, аригато”, качаем Visual Studio 2022 и устанавливаем её по инструкциям из интернетов.
Посмотреть вложение 92231
Посмотреть вложение 92233

Запускаем скаченный “Visual Studio Setup” и выбираем “.NET Desktop Development” (не уверен, но этого должно хватить)
Посмотреть вложение 92236

Проекты для ZP у нас будут создаваться на древнем .NET Framework 4.6.2, так что, на всякий случай ставим эти пакеты тоже.
Посмотреть вложение 92240
Тыкаем "Install" (у меня "Modify", так, как студия уже установлена)

(2) Создаём новый проект в “ProjectMaker” и добавляем сие чудо-кубик.
Посмотреть вложение 92246

(3) Кликаем на создание нового проекта в свойствах кубика.
Посмотреть вложение 92247

(4)Заполняем поля и кликаем “OK”
Посмотреть вложение 92248
“Название решения” – даём более общее название, ибо там может быть много проектов (решение – это то, что объединяет проекты).
“Название проекта” – даём более конкретное название, там будет наш код (повторять название, как я – не обязательно, просто люблю такое соблюдение неймспейсов).

То есть, такое именование будет так же корректным.
Посмотреть вложение 92249

А вот и произошла магия - наш первый проект в Visual Studio создан.
Посмотреть вложение 92271

П.С. Наши свойства кубика будут заполнены автоматически и в будущем мы сможем спокойно подключаться к проекту.
Посмотреть вложение 92252


Примечание:
Замечу, мы должны выставить в ProjectMaker и Visual Studio тёмную тему.
Для чего? Ходят слухи, что иначе программы могут работать некорректно…
(⊙ˍ⊙)
Сделали?! Ну всё, теперь, мы точно готовы двигаться дальше…(¬‿¬)


Практика:
Посмотреть вложение 92261

Вообще, всё интуитивно понятно, но пакеты тут, если что
Посмотреть вложение 92263
Посмотреть вложение 92253
(1)
Я создал тут папочку для вас, в которую добавил новый статический класс.
(2) Так же добавляем в него юзинг установленной библиотеки для работы с IoC.
(3) В классе добавляем статический метод расширения для "IServiceCollection", где будет проходить наша регистрация сервисов
для последующего предоставления в качестве внешних зависимостей. На данный момент, там зарегистрированы: инстанс браузера и инстанс зенки.

Посмотреть вложение 92254
(1) Ключевое слово “this”, в данном контексте, говорит нам о том, что это метод расширения, а расширяем мы "IServiceCollection".
То есть, когда в следующий раз, мы тыкнем « . » после объекта типа "IServiceCollection" (правильнее сказать, объекта, который реализовывает данный интерфейс,
но нам это знать сейчас не обязательно), то у нас будет доступен наш метод “AddApplication” в который мы должны будем передать "Instance" и "IZennoPosterProjectModel"
(первый параметры – IServiceCollection – передавать не нужно, поскольку, именно у его мы вызываем этот метод и он будет передан автоматически).
П.С. Методы расширения могут быть только в статическом классе и статическом методе.
(2) Тут мы добавляем/регистрируем наши сервисы/инстансы в рамках жизненного цикла называемого “Scoped
(он, кстати, написан тоже, как метод расширения; это видно на всплывашке при наведении курсора на метод и ключевому слову “extension”).
Transient (AddTransient) - инстанс сервиса создается каждый раз, когда его запрашивают. Этот жизненный цикл лучше всего подходит для легковесных, не фиксирующих состояние, сервисов.
Scoped (AddScoped) - инстанс сервиса создается единожды для каждого запроса.
Singleton (AddSingleton) - инстанс сервиса создается при первом запросе (или при запуске ConfigureServices, если вы указываете инстанс там), а затем каждый последующий запрос будет использовать этот же инстанс.

Источник: Разница между AddTransient, AddScoped и AddSingleton в ASP.NET Core / Хабр (habr.com)

Могу ошибаться, но, в рамках зенки, "Scoped" и "Singleton" - будет практически одно и тоже.
(3) "IServiceCollection" – это возвращаемый тип (то есть, мы используем метод расширения для "IServiceCollection" и его же возвращаем, таким образом, это образует Fluent Interface/Fluent Api).
(4) Всё это входные параметры метода (сигнатура метода).

Ещё чуть-чуть разжую, на всякий…)
Посмотреть вложение 92255
(1) Это сервисы, которые мы регистрируем.
(2) А это мы запихиваем готовые инстансы этих сервисов (обычно, контейнер сам нам создает и предоставляет инстансы, но, в данном случае,
нам нужны уже готовые, которые пришли прямиком из ZennoPoster или ProjectMaker).

Чтоб всё у нас завелось - я вернулся в наш класс “Program” и…
Посмотреть вложение 92256
(1) Добавляем юзинг хостинга (пакета, который мы ранее должны были установить).
(2) Так же добавляем стандартный метод для создания хост билдера, в который передаём инстансы пришедшие из зенки.
(3) И именно тут используем наш, выше написанный, метод расширения.
services” – это и есть тот самый первый параметр “IServiceCollection” (можете сами потом убедиться, наведя мышку на этот объект).
(4) Разумеется, вызываем метод для создания хост билдера.
(5) А следом вызываем и метод сборки.

Всё! Теперь наш контейнер сможет нам творить магию в виде инъекций инстансов зарегистрированных сервисов в конструкторы классов,
то бишь, будет предоставлять нам внешние зависимости
(~ ̄▽ ̄)~
Собственно, толку пока не много, нужно вывести наш первый “Hello, world!”


«Hello, world!» в контейнере:

Посмотреть вложение 92257
(1) Собственно, мы создали наш первый условный сервис (да, обычный класс).
(2) Добавили метод “Run”, который будем вызывать для старта сервиса (название может быть любым).
(3) Добавили конструктор класса, именно сюда IoC будет производить нашу инъекцию, когда мы обратимся к методу “Run”.
Заметьте, что у конструктора класса “0 references”, а это значит, что на него никто не ссылается, а он будет вызваться…
Что-то это, если не магия?! Атеистам шах и мат!
(⊙_(⊙_⊙)_⊙)

Посмотреть вложение 92258
(1) Теперь регистрируем созданный сервис в нашем регистраторе.
Опять же, обратите внимание, что никакого инстанса мы не пихаем,
IoC за нас будет создавать его и предоставлять конструктору нужные внешние зависимости.
Получается так, что в конструкторе сервиса (созданном нами классе) мы можем использовать любой порядок внешних зависимостей.

Посмотреть вложение 92259
(1) Всё, теперь нам требуется просто запросить наш сервис у хоста (если быть точным, то у "IServiceProvider", который обитает в свойствах "IHost".
Не устану повторять: мы запрашиваем сервис у которого есть конструктор, но ничего не передаём для самого конструктора, IoC сам всё сделает исходя из конфига.
По поводу "IServiceProvider": мы так же можем использовать его в конструкторах не статических классов, если нам потребуется
выдернуть какой-то инстанс сервиса похожим образом, при этом, не нужно заранее регистрировать "IServiceProvider" в нашем конфиге, он уже зарегистрирован под капотом.
(2) И вызвать тот самый метод “Run

Теперь просто запускаем проект в зенке и наблюдаем за нашим итоговым результатом - «Hello, world!» - в контейнере!
Посмотреть вложение 92260

Задачка на подумать: это не обязательно, но, в идеале, теперь неплохо бы было избавиться от завязки на реализации,
и завязаться на абстракции, то есть, на интерфейсе, например “IHelloWorldService”, а как это сделать – придётся решать вам самим.


¯\_( ͡° ͜ʖ ͡°)_/¯

Резюме по Dependency Injection и IoC-контейнеру:
DI и IoC – вещи не сложные, но это прям то, что доктор прописал для больших проектов
планируемых расширяться в будущем (для мелких так же подойдёт), где есть куча зависимостей и сложной реализации и т.д.
Данный подход к разработке проекта: упрощает написание кода, делает его более гибким, поддерживаемым, расширяемым и более приятным.
К тому же, не нужно беспокоиться за стейт объектов и хранить всё по статическим переменным /ᐠ。ꞈ。ᐟ\

Резюме по кубику “Проект Visual Studio”:
Очевидно, что данная фича будет очень полезна тем, кто начал начал использовать C# в своих проектах.
Разработчики ZennoPoster делают шаги в правильном направлении (осталось открыть API
для решения ReCaptcha2 из Visual Studio, - цены бы вам тогда не было).
Написание проектов в VS ускоряет и упрощает разработку за счёт умного "Интеллисенс", а "Nuget package manager" - открывает доступ к огромному количеству библиотек,
да и дебажить в студии просто волшебно (◡‿◡)


П.С. Это была моя первая статья, и это был лишь косплей на программиста, не судите строго...:-)
Посмотреть вложение 92266


Visual Studio 2022 v17.2.1
ZennoPoster v7.7.1.0
(это минимальная версия для поддержки 22-й студии)
Спасибо за подробную статью
 
  • Спасибо
Реакции: SHILY

GudVin92

Client
Регистрация
11.07.2018
Сообщения
3
Благодарностей
0
Баллы
1
ProjectMaker/ZennoPoster 7.7.19

Пишу в C# кубике:
project.SendToLog("сообщение", "название", LogType.Info, true, LogColor.Blue);

Получаю ошибки:
Ошибка в действии "CS0103" "The name 'LogType' does not exist in the current context". [Строка: 1; Cтолбец: 45]
Ошибка в действии "CS0103" "The name 'LogColor' does not exist in the current context". [Строка: 1; Cтолбец: 65]

Как правильно указать LogType и LogColor?
 

Кто просматривает тему: (Всего: 1, Пользователи: 0, Гости: 1)