Сайт на asp.net для начинающих

Создание веб-формы

Чтобы создать новую страницу, в контекстном меню проекта выберите Добавить→Веб-форма. После этого автоматически сгенерируются и заполнятся все 3 файла.

Примечание Веб-формы можно создавать с помощью встроенного графического конструктора веб-форм. Что бы открыть его, в контекстном меню нужной веб-формы нажмите на Открыть в конструкторе. Все необходимые элементы можно достать из Панели Элементов (Вид→Панель элементов). Но если вам нужна максимальная гибкость и функциональность — лучше разрабатывать формы вручную в коде.

Потом можно добавить ссылку на новую страницу в панель навигации в :

Примечание Обратите внимание, что в ссылке указывается название страницы без расширения. Если вы откроете новую страницу, то заметите, что вся общая вёрстка отсутствует

Это потому, что в новой странице не подвязывается разметка из. Чтобы исправить это, нужно в новой форме вместо сгенерированной вёрстки вставить это:

Если вы откроете новую страницу, то заметите, что вся общая вёрстка отсутствует. Это потому, что в новой странице не подвязывается разметка из . Чтобы исправить это, нужно в новой форме вместо сгенерированной вёрстки вставить это:

Во всех упоминаниях нужно заменить News на название вашей страницы. Теперь форма будет отображаться вместе с общей вёрсткой.

***

Для интеграции значений в вёрстку из aspx.cs используют специальный тег :

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

***

Получение значений параметров из URL производится через статический класс , в котором хранятся и прочие данные о запросе:

Изучить все возможности веб-форм можно в официальной документации.

Настройка стиля сайта Set up the site style

Выполните незначительную настройку меню, макета и домашней страницы сайта. A few simple changes will set up the site menu, layout, and home page.

Откройте ViewsShared\_Layout.cshtmlи внесите следующие изменения: Open ViewsShared\_Layout.cshtml, and make the following changes:

  • Замените все вхождения «My ASP.NET Application» и «Имя приложения» на «Contoso University». Change each occurrence of «My ASP.NET Application» and «Application name» to «Contoso University».
  • Добавление пунктов меню для учащихся, курсов, преподавателей и отделов и удалите запись контакта. Add menu entries for Students, Courses, Instructors, and Departments, and delete the Contact entry.

Изменения выделены в следующем фрагменте кода: The changes are highlighted in the following code snippet:

В ViewsHomeIndex.cshtml, замените содержимое файла следующим кодом, который заменяет текст о ASP.NET и MVC об этом приложении: In ViewsHomeIndex.cshtml, replace the contents of the file with the following code to replace the text about ASP.NET and MVC with text about this application:

Нажмите клавиши Ctrl + F5 для запуска веб-сайта. Press Ctrl+F5 to run the web site. Появится домашняя страница с меню. You see the home page with the main menu.

Добавление настроек базы данных при ее инициализации

Помимо добавления данных по умолчанию, в базе данных можно определить и другие настройки, которые будут применены при инициализации. Например, вы можете создать индекс для столбца FirstName таблицы Customers, чтобы ускорить поиск по имени покупателя в этой таблице. Чтобы определить различные настройки, вы можете использовать метод DbContext.Database.ExecuteSqlCommand(), в который передается произвольная SQL-команда для настройки различных аспектов базы данных. Этот метод вызывается также, в переопределенном методе Seed() объекта инициализации. В следующем примере показано, как задать индекс для столбца CustomerId:

Очевидно, что с помощью этого метода вы можете выполнить любую SQL-команду для настройки базы данных. На рисунке ниже показан добавленный в таблицу индекс:

Для разработки новых приложений мы рекомендуем ASP.NET Core Razor Pages по ASP.NET MVC контроллеры и представления. For new development, we recommend ASP.NET Core Razor Pages over ASP.NET MVC controllers and views. Для следующего вида серии руководств см. в разделе с помощью Razor Pages, руководства: Начало работы с Razor Pages в ASP.NET Core. For a tutorial series similar to this one using Razor Pages, see Tutorial: Get started with Razor Pages in ASP.NET Core. Новый учебник. The new tutorial:

  • проще для выполнения; Is easier to follow.
  • содержит больше рекомендаций по EF Core; Provides more EF Core best practices.
  • использует более эффективные запросы; Uses more efficient queries.
  • более актуально, так как используются новейшие API; Is more current with the latest API.
  • охватывает дополнительные возможности; Covers more features.
  • является предпочтительным подходом для разработки новых приложений. Is the preferred approach for new application development.

В этой серии руководств вы узнаете, как создать приложение ASP.NET MVC 5, использующий Entity Framework 6 для доступа к данным. In this series of tutorials, you learn how to build an ASP.NET MVC 5 application that uses Entity Framework 6 for data access. В этом учебнике используется Code First рабочего процесса. This tutorial uses the Code First workflow. Сведения о том, как выбрать Code First, Database First или Model First, см. в разделе создать модель. For information about how to choose between Code First, Database First, and Model First, see Create a model.

В этой серии руководств описывается построение примера приложения университета Contoso. This tutorial series explains how to build the Contoso University sample application. Пример приложения веб-сайт простой университета. The sample application is a simple university website. С ним вы можете просматривать и обновлять учащихся, курсах и сведения о преподавателе. With it, you can view and update student, course, and instructor information. Ниже приведены два созданных экранов. Here are two of the screens you create:

В этом учебнике рассмотрены следующие задачи. In this tutorial, you:

  • Создать веб-приложение MVC Create an MVC web app
  • Настройка стиля сайта Set up the site style
  • Установка Entity Framework 6 Install Entity Framework 6
  • Создание модели данных Create the data model
  • Создание контекста базы данных Create the database context
  • Инициализация базы данных с тестовыми данными Initialize DB with test data
  • Настройка EF 6 для использования LocalDB Set up EF 6 to use LocalDB
  • Создание контроллера и представлений Create controller and views
  • Просмотр базы данных View the database

Запись (занесение) изображения в БД SQL Server

Достаточно часто приложения, особенно это касается web-приложений, работают с большим количеством графических объектов. Зачастую, эти графические файлы хранятся на том же сервере, что и само приложение, а в БД находятся лишь ссылки (пути) на эти графические файлы. Таким образом удается значительно снижать объем информации находящейся в БД.

Однако, случается так, что целесообразнее в БД хранить сами изображения. Для этих целей создадим простую таблицу:

Имеем три столбца: ID(первичный ключ), ImageByte(поле для самого изображения) и AltText(текст для свойства alt тега img изображения).

К примеру, на локальном диске «С» имеется графический файл GraphFile.png, который мы и хотим занести в БД.

Для рвботы с БД будем использовать объект-команду SqlCommand.
Для начала настраиваем объект SqlConnection и формируем объект SqlCommand:

SqlConnection connection = new SqlConnection(myConnectionString);
SqlCommand cmd = new SqlCommand(
   "INSERT INTO MyTable (ID, ImageByte, AltText)" + 
   "VALUES (@ID, @ImageByte, @AltText)", connection);

Затем, используя тип FileStream читаем содержимое файла

FileStream fStream = new FileStream(
   "c:\\GraphFile.png", FileMode.Open, FileAccess.Read);

Теперь создадим массив байтов размерностью в количество полученных байтов и переместим в него данные из объекта fStream:

Byte[] imageBytes = new byte;
fStream.Read(imageBytes, 0, imageBytes.Length);

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

Теперь осалось лишь настроить параметры для команды вставки:

SqlParameter par = new SqlParameter(
    "@ID", SqlDbType.UniqueIdentifier);
par.Value = Guid.NewGuid();
par.Direction = ParameterDirection.Input;
cmd.Parameters.Add(par);

par = new SqlParameter("@ImageByte", SqlDbType.Image);
par.Value = imageBytes;
cmd.Parameters.Add(par);

par = new SqlParameter("@AltText", SqlDbType.NVarChar);
par.Value = "Просто картинка";
cmd.Parameters.Add(par);

Передаем в БД готовую команду на выполнение:

connection.Open();
cmd.ExecuteNonQuery();
connection.Dispose();

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

В следующей статье мы разберем пример чтения бай-данных (изображения) из поля таблицы Sql-Server БД

Больше информации читайте по теме:

Добавление и редактирование данных в типизированные DataTableИмпорт csv-данных в DataSetИмпорт sql-данных в формат .csv

Оценить статью:
?54321

Оценивая статью, Вы подсказываете нам насколько данная информация стала
полезной именно для Вас. Мы ожидаем, прежде всего, оценку доступности материала
для понимания и простоты его изложения. Дополнительные комментари, вопросы,
дополнения и замечания Вы можете указать на странице
От ВасЧастичное, или полное копирование материала данной
статьи возможно лишь при размещении ссылки на данную страницу.

Создание базы данных в зависимости от условия

С помощью метода Database.SetInitializer() можно управлять поведением Code-First для создания базы данных при изменении модели. Этот метод принимает экземпляр интерфейса IDatabaseInitializer . В Entity Framework есть три класса, реализующих этот интерфейс и обеспечивающих возможность выбора поведения Code-First при инициализации базы данных:

CreateDatabaseIfNotExists

Экземпляр этого класса используется в Code-First по умолчанию для всех классов контекста. Это безопасный способ инициализации, при котором база данных никогда не будет удалена и данные не будут потеряны. При этом способе инициализации, база данных создается только один раз, когда ее еще не существует. Если модель данных была изменена, например мы добавили новый класс, то Entity Framework обнаружит эти изменения (с помощью таблицы __MigrationHistory) и возбудит исключение, т.к. при таком типе инициализации нельзя удалить базу данных, а соответственно нельзя отразить на нее новую модель.

DropCreateDatabaseIfModelChanges

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

DropCreateDatabaseAlways

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

Помимо этих стандартных типов инициализации вы можете создать произвольный тип инициализации, реализовав интерфейс >метод InitializeDatabase() принимающий объект контекста. Примером пользовательского механизма инициализации может послужить класс DontDropDbJustCreateTablesIfModelChanged, который находится в расширении EF CodeFirst пакета NuGet. С помощью этого инициализатора вы можете не беспокоится об удалении базы данных, т.к. он затрагивает только таблицы, которые изменились в модели.

Установить инициализатор, используемый по умолчанию, можно также в конфигурационном файле приложения, как показано в примере ниже:

Для этого используется раздел contexts настроек Entity Framework. При структуре этого приложения эти настройки нужно указывать в файле Web.config веб-приложения, а не в проекте, где мы создавали модель

Обратите внимание, что в этом примере показано использование атрибута disableDatabaseInitialization. Если вы установите его в true, то сможете отключить автоматическую инициализацию базы данных в приложении, как мы делали это с использованием метода Initialize(null)

В атрибуте type узла context указывается полное имя класса контекста, а также имя сборки, где содержится этот класс. В атрибуте type узла databaseInitializer указывается полный тип инициализатора, обратите внимание на синтаксис этой инструкции.

Класс DbContext и его регистрация в ASP.NET Core

Класс контекста — еще одна важная часть приложения. Этот класс должен наследовать от базового класса , который содержит информацию и конфигурацию для доступа к базе данных. Итак, давайте создадим наш контекстный класс с именем в папке :

Дополнительные параметры отправляются в базовый класс через конструктор с помощью параметра . Наконец, мы видим свойство типа , и это довольно важная часть.

EF Core ищет все общедоступные свойства внутри класса контекста приложения, а затем сопоставляет их имена с именами таблиц в базе данных. Затем он входит в класс, который предоставляется в свойстве `DbSet` (в нашем случае это класс ), и сопоставляет все общедоступные свойства в столбцы таблицы с одинаковыми именами и типами (StudentId, Name и Age).

Если наш класс имеет какие-либо ссылки на другие классы (прямо сейчас их нет, но мы создадим отношения в следующих статьях), EF Core будет использовать эти ссылочные свойства и создавать отношения в база данных.

Регистрация класса контекста в IOC ASP.NET Core

После того, как мы закончили создание класса , мы можем перейти к его регистрации.

Для этого мы собираемся открыть класс и изменить метод :

Мы используем метод расширения для регистрации нашего класса ApplicationContext в контейнере IOC. Внутри метода мы предоставляем строку подключения к нашему классу контекста, а также можем предоставить дополнительные параметры (если в этом появится необходимость).

Быстродействие SQLDataReader

Сравнительный тест быстродействия двух вариантов команд с включенным и отключенным пулингом соединений. В первом варианте выполняется команда запроса к одной таблице с повтором 100 раз. Во втором варианте выполняется команда запроса данных у 30 таблиц одновременно, также с повтором 100 раз.

Первое подключение к базе данных:

При первом соединении с базой данных одиночная команда с пулингом выполняется почти в 2 раза дольше, чем с отключёнными пулами. Сложные команды выполняются практически одинаковое время. Логично предположить, что дополнительное время затрачивается на создание пула.

Последующие запросы к базе данных:

Последующие запросы показывают эффективность «пуловой» оптимизации подключений технологии ADO.NET. Одиночные команды выполняются почти в 8 раз быстрее при использовании пулов подключений. В случае со сложными командами эффект использования оптимизации снижается, но всё равно без пулов запросы выполняются медленнее в 1,73 раза.

Веб-API JSON

JSON — это аббревиатура от JavaScript Object Notation. Это формат файла, в котором объекты данных хранятся в удобочитаемой форме. Это самый популярный формат, когда дело касается создания и использования API. Многие приложения разрабатываются ASP.NETна внутренней стороне и реализуются во внешней среде, такой как React, Blazor, Bootstrap или Angular, с использованием веб-API JSON. Вот пример файла JSON:

Квадратные скобки обозначают список или массив объектов. Фигурные скобки { }представляют объект JSON. Внутри этого объекта есть пары ключ-значение с разными типами данных, ключ «имя» со значением «Джеймс Бонд».

JSON обрабатываются с помощью набора HTTP-методов, подобных CRUD. Каждый HTTP-запрос использует определённый метод. Метод по умолчанию GET, но вы можете использовать POST, PUT или DELETE если вы хотите различные модели поведения.

Вставка данных в базу данных

В этом разделе показано, как создать страницу, позволяющую пользователям добавлять новый продукт в таблицу базы данных Product . После вставки новой записи продукта на странице отображается обновленная таблица с помощью страницы листпродуктс. cshtml , созданной в предыдущем разделе.

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

На веб-сайте создайте новый файл CSHTML с именем инсертпродуктс. cshtml.

Замените существующую разметку следующим:

Текст страницы содержит HTML-форму с тремя текстовыми полями, которые позволяют ввести имя, описание и цену. Когда пользователь нажимайте кнопку » Вставить «, код в верхней части страницы открывает подключение к базе данных смаллбакери. sdf . Затем вы получаете значения, отправленные пользователем с помощью объекта, и назначаете эти значения локальным переменным.
Чтобы убедиться, что пользователь указал значение для каждого обязательного столбца, необходимо зарегистрировать каждый элемент, который требуется проверить:

Вспомогательная функция проверяет наличие значения в каждом зарегистрированном вами поле

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

( Оператор означает и — этот тест имеет значение, если это отправка формы, а все поля прошли проверку.)
Если все столбцы проверены (не были пустыми), Создайте инструкцию SQL для вставки данных, а затем выполните ее, как показано далее.

Для вставки значений необходимо включить заполнители параметров ( , , ).

Note
В качестве меры предосторожности всегда следует передавать значения в инструкцию SQL с помощью параметров, как показано в предыдущем примере. Это дает возможность проверить данные пользователя, а также помогает защититься от попыток отправить вредоносные команды в базу данных (иногда это называется атаками путем внедрения кода SQL).

Чтобы выполнить запрос, используйте этот оператор, передав ему переменные, содержащие значения для замены заполнителей:

После выполнения инструкции пользователю отправляется страница со списком продуктов, использующих эту строку:

Если проверка не прошла бы, вы пропускаете вставку
Вместо этого у вас есть вспомогательный метод на странице, который может отображать накопленные сообщения об ошибках (если таковые имеются):

Обратите внимание, что блок style в разметке включает определение класса CSS с именем

Это имя класса CSS, используемого по умолчанию для элемента, который содержит любые ошибки проверки
В этом случае класс CSS указывает, что сводные ошибки проверки отображаются красным цветом и полужирным шрифтом, но можно определить класс для отображения любого форматирования.

Тестирование страницы вставки

  1. Просмотр страницы в браузере. На странице отображается форма, похожая на ту, которая показана на следующем рисунке.

  2. Введите значения для всех столбцов, но убедитесь, что столбец Price не заполнен.

  3. Нажмите кнопку Вставить. На странице отображается сообщение об ошибке, как показано на следующем рисунке. (Новая запись не создается.)

  4. Заполните форму полностью, а затем нажмите кнопку Вставить. На этот раз отображается страница листпродуктс. cshtml , в которой отображается новая запись.

Связывание SQL данных с DataList Control

В то время как Repeater является итератором общего назначения, DataList обеспечивает некоторые дополнительные возможности управления размещением в листинге. В отличие от Repeater, DataList позволяет включить дополнительные элементы вне определения шаблона, подобно строкам, ячейкам и промежуткам таблицы, которые содержат атрибуты стиля. Это предоставляет более широкие возможности форматирования. Например, DataList поддерживает свойства RepeatColumns и RepeatDirection, которые определяют, должны ли данные быть представлены в нескольких столбцах и в каком направлении (вертикально или горизонтально) чтобы представить элементы данных. DataList также поддерживает атрибуты стиля: тип, размер и имя шрифта. Этот пример показывает, как обратиться к SQL базе данных, которая содержит заголовки книг и немного ключевой информации о каждой книге, и затем отображает данные, используя DataList. Результат имеет всю заданную информацию для каждой книги, сгруппированную вместе, и информация для каждой книги представлена в двух столбцах в порядке слева направо.

Доступ к SQL базе данных

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

<%@ Import Namespace="System.Data" %><%@ Import Namespace="System.Data.SQL" %><html>

2. В пределах тэга <script language=»C#» runat=»server»>, включите функцию Page_Load, которая обеспечит подключение к базе данных, создаст и заполнит новый Dataset информацией из базы данных, и затем свяжет Repeater control с Dataset. Это показано в следующем примере кода:

<script language="C#" runat="server">    void Page_Load(Object sender, EventArgs e) {

а) Установите подключение к базе данных «pubs», расположенной на локальном компьютере.

        SQLConnection myConnection = new SQLConnection                     ("server=localhost; uid=sa;pwd=;database=pubs");

б) Соединитесь с SQL базой данных, используя запрос «SELECT *», чтобы получить все данные из таблицы «Titles».

        SQLDataSetCommand myCommand = new SQLDataSetCommand                             ("select * from Titles", myConnection);

в) Создайте и заполните Dataset.

        DataSet ds = new DataSet();        myCommand.FillDataSet(ds, "Titles");

г) Свяжите MyDataList с таблицей «Titles», используя заданное по умолчанию представление. Заметьте, что MyDataList — это «ID» установленный для DataList в <body> страницы.

        MyDataList.DataSource = ds.Tables.DefaultView;        MyDataList.DataBind();    }

3. В тэге страницы <body>, отобразите данные.

</script><body topmargin="0" leftmargin="0" marginwidth="0" marginheight="0">

4. Откройте DataList, устанавливая его для двух столбцов, которые будут заполнен в горизонтальном порядке.

  <ASP:DataList id="MyDataList" RepeatColumns="2" RepeatDirection=                                         "Horizontal" runat="server">

5. Установите шаблон DataList с именем «itemtemplate».

      <template name="itemtemplate">

а) Используйте элемент <div>, что бы обрабатывать данные для каждой книги как логический модуль.

        <div style="padding:15,15,15,15;font-size:10pt;                                             font-family:Verdana">

б) Используйте вложенный элемент <div> для связи данных заголовка и показа их различными шрифтами.

          <div style="font:12pt verdana;color:darkred">            <i><b><%# DataBinder.Eval(Container.DataItem, "title")%>                                                            </i></b>          </div>          <br>

в) Связываем и показываем остальную часть данных в шрифте, установленном элементом <div>.

          <b>Title ID: </b><%# DataBinder.Eval(Container.DataItem,                                               "title_id") %><br>          <b>Category: </b><%# DataBinder.Eval(Container.DataItem,                                                    "type")%><br>          <b>Publisher ID: </b><%# DataBinder.Eval                             (Container.DataItem, "pub_id") %><br>          <b>Price: </b><%# DataBinder.Eval                     (Container.DataItem, "price", "$ {0}") %><p>        </div>      </template>  </ASP:DataList></body></html>

О AddDbContextPool

Вместо метода AddDbContext мы можем использовать метод AddDbContextPool. Мы можем использовать либо первый, либо второй метод, но во втором методе мы включаем пул DbContext. Это не будет создавать каждый раз новый экземпляр, но сначала проверит, есть ли доступные экземпляры в пуле, и если они есть, он будет использовать один из них.

Сейчас наш класс контекста готов к использованию с инъекцией зависимостей (DI) внутри нашего приложения. Итак, давайте добавим контроллер Values ​​(API) и изменим его:

Как вы могли заметить, мы внедряем наш класс контекста внутрь конструктора контроллера, что является обычным решением для DI.

Особенности.NET и. ASP.NET

.NET Core включает:

  • Кроссплатформенная поддержка различных компьютерных операционных систем, мобильных устройств и даже игровых консолей.
  • Поддержка языков программирования C #, F # и Visual Basic.
  • Базовые библиотеки для работы со строками, датами, файлами / вводом-выводом и т. Д.
  • Редакторы и инструменты для Windows, Linux, macOS и Docker

ASP.NET добавляет:

  • Поддержка обработки веб-запросов на C # или F #.
  • Синтаксис шаблонов страниц Razor для создания динамических веб-страниц с использованием C #.
  • Библиотеки для распространённых веб-шаблонов, таких как MVC.
  • Система аутентификации с библиотеками, базой данных и шаблонными страницами для обработки входов в систему с многофакторной аутентификацией и внешней.
  • аутентификацией с третьими сторонами, такими как Google, Twitter, Facebook и т.д.
  • Расширения редактора, которые реализуют подсветку синтаксиса, автозавершение кода и другие функции, настроенные для веб-разработки.

Архитектуры, отличные от MVC

Razor Pages

Razor Pages — это формат веб-разработки по умолчанию в ASP.NETCore. Это страничная модель, встроенная в ASP.NETCore MVC. Он имеет функции, аналогичные MVC, но имеет собственный синтаксис и менее загромождённый макет.

В стиле MVC части вашего приложения разбиты на папки для контроллера, модели и представления. В структуре Razor Pages нет отдельных папок, а вместо этого используется одна папка «Pages» в качестве папки маршрута для всех частей.

Вместо отдельного контроллера с действиями и каждым действием, представляющим представление, Razor Pages представляет собой компактную версию стиля MVC, которая прикрепляет контроллер или модель представления непосредственно к странице.

Репозиторий

Репозитории — это особый тип класса, используемый для добавления уровня абстракции между доступом к данным и вашими контроллерами. Он не используется в программах в формате MVC, но важен для других типов ASP.NET основных приложений, таких как приложения Razor Pages.

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

Сначала вы создаёте интерфейс со всеми объявлениями функций, отвечающих за выполнение операций CRUD. Затем вы создаёте класс репозитория и вводите контекст своей базы данных. Эти классы репозитория реализуют функции интерфейса и предоставляют свою логику для обработки операций. Затем один из этих репозиториев внедряется в ваш контроллер для обеспечения функциональности, поэтому вам не нужно вводить базу данных непосредственно в контроллеры.

Рейтинг
( Пока оценок нет )
Editor
Editor/ автор статьи

Давно интересуюсь темой. Мне нравится писать о том, в чём разбираюсь.

Понравилась статья? Поделиться с друзьями:
Люкс-хост
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: