CoderCastrov logo
CoderCastrov
Азур

Azure Functions, Очереди и хранилище таблиц, также известное как серверная архитектура на примере

Azure Functions, Очереди и хранилище таблиц, также известное как серверная архитектура на примере
просмотров
16 мин чтение
#Азур
Table Of Content

Кратко: Я хотел купить мышь, которая дважды в год находится в распродаже со скидкой 50%. Мне было лень проверять, когда она находится в распродаже, поэтому я создал веб-сайт, который будет делать это за меня и отправлять мне электронное письмо при изменении цены/наличия с помощью серверной архитектуры Azure и фреймворка .NET Core. Если вы следуете за мной, я научу вас, как сделать это самостоятельно, или вы можете использовать мой веб-сайт.

Слово "серверная архитектура"

Чтобы быть ясным, "серверная архитектура" != отсутствие сервера, не существует такой вещи, как веб-архитектура, в которой не участвует сервер. По моему мнению, "серверная архитектура" - это плохо выбранное слово для архитектуры, которая не требует целого сервера для выполнения задачи обслуживания небольшого объема обработки, извлечения или записи данных. Вместо этого есть один "Мега-Гига-Тера" большой сервер (обычно какой-то облачный провайдер, такой как Amazon, Azure, Google services или подобный), который может использоваться как место, где мы можем развернуть очень маленькие/крошечные методы (заглушки), которые могут обрабатывать эти небольшие запросы на обработку данных.

Идея

В этом демонстрационном проекте я попытаюсь охватить несколько областей экосистемы Azure безсерверной архитектуры и использовать их на реальном приложении. Моя идея заключается в создании веб-страницы, на которой я смогу создавать список товаров/предметов, доступных в онлайн-магазинах, и отслеживать их изменения, и когда происходит изменение цены или доступности, мне будет отправляться электронное письмо с обновлениями. Таким образом, я сэкономлю время, проверяя цены на товары, чтобы узнать, когда они находятся в распродаже или становятся доступными. Чтобы получить данные с определенного URL, мы будем использовать парсер для сбора данных с других веб-сайтов. У каждого веб-сайта/интернет-магазина свой собственный макет, и для этого нам нужно настроить наш парсер, чтобы он мог парсить различные магазины.

Azure функции

Как я уже упоминал ранее, я использовал Azure функции для создания серверной функциональности. Функция Azure может быть вызвана/триггернута несколькими способами, в этой демонстрации я буду использовать Http, Queue и Timer триггерные функции. Для получения более подробной информации вы можете ознакомиться с полной документацией по этой ссылке.

Очереди Azure

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

Вот официальное определение от Microsoft для очередей:

Azure Queue Storage - это сервис для хранения большого количества сообщений. Вы можете получать доступ к сообщениям из любой точки мира с помощью аутентифицированных вызовов с использованием HTTP или HTTPS. Сообщение в очереди может иметь размер до 64 КБ. Очередь может содержать миллионы сообщений, до общего лимита емкости учетной записи хранилища. Очереди обычно используются для создания накопления работ для асинхронной обработки.

Для получения дополнительной информации о очередях вы можете посмотреть эту ссылку.

Таблицы Azure

В большинстве случаев, когда вы создаете веб-приложение, возникает необходимость хранить некоторые данные. Обычно мы сохраняем их в таблицы или файлы. В зависимости от характера приложения или наших навыков, мы можем выбрать между SQL или NoSQL хранилищем. В большинстве моих проектов я использовал MSSQL и PostgreSQL, но в этой демонстрации я буду использовать хранилище таблиц Azure.

Вот определение таблиц Azure от Microsoft:

Хранилище таблиц Azure хранит большие объемы структурированных данных. Сервис представляет собой хранилище NoSQL, которое принимает аутентифицированные вызовы изнутри и за пределами облака Azure. Таблицы Azure идеально подходят для хранения структурированных нереляционных данных.

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


Доступ к данным таблицы

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

Прямой доступ

Вы можете использовать библиотеки Microsoft Azure Storage для .NET и иметь прямой доступ к хранилищу таблиц. Я использовал это в IUserProfileService для операций CRUD. Этот способ более дешевый, потому что вы будете обращаться к данным напрямую, обходя функции Azure.

Доступ с использованием Azure Functions

Еще один способ получить доступ к вашим данным - это создать Azure Function, которая будет делать это, поэтому вместо того, чтобы писать напрямую в таблицу, вы передадите данные в Azure Function, а затем функция сохранит данные в таблице. Это может показаться избыточным, так как вам нужно поддерживать функцию и платить за использование функции, но у этого также есть свои преимущества. Вы можете привязаться к различным очередям и уведомлять их; вы можете легко регистрировать все в одном месте и получать множество других преимуществ. Я использую этот подход в реализации IScrapeConfigService.

_ScrapeConfigsController communication with Azure Storage_

Отправка электронной почты

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

_Поток отправки электронной почты_

ASP.NET Identity Core

Всякий раз, когда пользователи участвуют в веб-приложении, всегда требуется возможность аутентификации или авторизации. Обычно, когда мы используем .net core в качестве технологии, мы обычно используем службу Identity. Одно из свойств Identity - это требование поставщика данных MSSQL, но это не подходит для меня в этой демонстрации, так как я выбрал использовать таблицы Azure в качестве хранилища. Конечно, мы можем создать гибридное приложение, где идентификатор будет храниться в MSSQL, а все остальные данные - в таблицах Azure, но я нашел пакет NuGet, который реализует всю функциональность Identity, но будет полагаться на таблицы Azure, это замечательно. В этом проекте я буду использовать пакет Identityazuretable для части идентификации.

_Identityazuretable architecture_

Ресурсы Azure

Я упомянул несколько служб Azure. Чтобы воспроизвести это решение, вам понадобятся несколько ресурсов Azure.

Функция Azure

Для функций Azure вам нужно создать Function App. В процессе создания Function App на шаге Hosting вам будет предложено создать учетную запись хранилища, если у вас еще нет созданной, просто введите имя, и мастер создаст ее для вас. Вы можете увидеть мою на скриншотах ниже. После создания вы можете проверить вашу учетную запись хранилища здесь. Вам понадобится эта учетная запись хранилища для таблиц и очередей, которые мы будем использовать в этом руководстве. Для PlanType, также на вкладке Hosting, я выбрал Consumption (без сервера), потому что это было самое дешевое решение, которое удовлетворит потребности этого приложения. Пожалуйста, всегда перед созданием ресурса Azure проверяйте его стоимость, потому что он может быть слишком дорогим или несовместимым с потребностями вашего продукта/приложения.

_Function app, которую я использую в этой демонстрации_ _Учетная запись хранилища, которую я использую в этой демонстрации_

SendGrid

Также я упомянул SendGrid, чтобы иметь возможность отправлять электронные письма с помощью Azure функций, вам понадобится учетная запись SendGrid. Вы можете создать ее через ресурсы Azure или создать напрямую на их веб-сайте. После создания учетной записи SendGrid вам нужно получить ApiKey, который свяжет FunctionApp с вашей учетной записью SendGrid (если у вас есть вопросы по этому поводу, пожалуйста, дайте мне знать). После получения SendGridApiKey вам нужно добавить его в настройки конфигурации Azure Functions (см. скриншоты).

_Перейдите к настройке приложения Azure_ _Конфигурация ключа API SendGrid_

Сервис приложений

В конце концов, вам понадобится сервис приложений или место, где вы можете разместить свой веб-сайт, который будет взаимодействовать с Azure Functions. Конечно, вы можете разместить свое веб-приложение где угодно и все равно общаться с Azure Functions, но в этом примере я решил придерживаться сервисов Azure, поэтому я создал сервис приложений и разместил здесь свое приложение на .net core. Создание сервиса приложений довольно просто с помощью мастера, будьте внимательны с тарифными планами (планом сервиса приложений), я выбрал бесплатный вариант, но, конечно, есть еще много других планов, которые вы можете использовать для масштабирования вашего приложения.

_Пример моих настроек для сервиса приложений_

Application Insights

Application Insights - это отличный способ отслеживать состояние вашего Function App или App Service. Здесь вы можете увидеть много полезной информации, связанной с доступностью, ошибками, неудачными запросами, временем ответа сервера, запросами сервера и многими другими вещами. Я создал два сервиса Application Insights, один для Azure Function и один для App Service.

Data that is shown in Application Insights

Группа ресурсов

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

Services in the resource group that I used for this demo

Управление затратами и выставление счетов

Очень важно, когда вы используете онлайн-сервис с оплатой по мере использования, как в данном случае, проверять свой счет каждые несколько дней, чтобы избежать больших или неожиданных затрат. Я сделал эту ошибку однажды и получил огромный счет, потому что выбрал некоторый тарифный план, о стоимости которого я не был осведомлен (Всегда проверяйте цены Azure, всегда!). В этом разделе вы можете проверить, сколько вы потратили до сих пор (если вообще) и сколько стоит каждая услуга.

_Пример выставления счета моей демонстрации_

Решение для парсера продуктов

Как я упоминал в первом предложении, моя идея заключалась в создании парсера продуктов, который будет собирать информацию о продуктах и отправлять мне электронное письмо при изменении. В следующем контексте я постараюсь объяснить самые важные вещи в приложении, если что-то неясно или недостаточно информации, пожалуйста, дайте мне знать. Вы можете увидеть в репозитории, что мое решение разделено на 5 проектов. Я постараюсь объяснить их назначение и самый важный код в них.

ProductScraper web

Это веб-проект. Здесь я настраиваю параметры Identity для использования пакета NuGet [ElCamino.AspNetCore.Identity.AzureTable](https://www.nuget.org/packages/ElCamino.AspNetCore.Identity.AzureTable/) для регистрации/аутентификации/авторизации пользователей. Вы можете увидеть эту конфигурацию в файле [Startup.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper/Startup.cs) в методе ConfigureServices.

public void ConfigureServices(IServiceCollection services)
    {
        services.AddDefaultIdentity<IdentityUser>(options =>
        {
            options.SignIn.RequireConfirmedAccount = false;
            options.User.RequireUniqueEmail = true;
            options.Password.RequireDigit = false;
            options.Password.RequireLowercase = false;
            options.Password.RequireUppercase = false;
            options.Password.RequireNonAlphanumeric = false;
        })
        //ElCamino configuration
        .AddAzureTableStores<ApplicationDbContext>(new Func<IdentityConfiguration>(() =>
        {
            IdentityConfiguration idconfig = new IdentityConfiguration
            {
                TablePrefix = Configuration.GetSection("AzureTable:IdentityConfiguration:TablePrefix").Value,
                StorageConnectionString = Configuration.GetSection("AzureTable:StorageConnectionString").Value,
                LocationMode = Configuration.GetSection("AzureTable:IdentityConfiguration:LocationMode").Value,
                IndexTableName = TableName.IdentityIndex, // default: AspNetIndex
                RoleTableName = TableName.IdentityRoles,   // default: AspNetRoles
                UserTableName = TableName.IdentityUsers   // default: AspNetUsers
            };
            return idconfig;
        }))
        .AddDefaultTokenProviders()
        .AddDefaultUI()
        .CreateAzureTablesIfNotExists<ApplicationDbContext>(); //can remove after first run;
        
        ...
        ...
    }

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

Также в файле [Startup.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper/Startup.cs) в методе ConfigureServices есть настройка для таблицы Azure, которую я использую. Здесь я добавляю настройки для каждой таблицы Azure, такие как ConnectionString и TableName.

Конфигурация таблицы ProductInfo

services.AddScoped<IAzureTableStorage<ProductInfo>>(factory =>
        {
            return new AzureTableStorage<ProductInfo>(
                new AzureTableSettings(
                    storageConnectionString: Configuration.GetSection("AzureTable:StorageConnectionString").Value,
                    tableName: TableName.ProductInfo));
        });

Конфигурация таблицы ScrapeConfig

services.AddScoped<IAzureTableStorage<ScrapeConfig>>(factory =>
        {
            return new AzureTableStorage<ScrapeConfig>(
                new AzureTableSettings(
                    storageConnectionString: Configuration.GetSection("AzureTable:StorageConnectionString").Value,
                    tableName: TableName.ScrapeConfig));
        });

Конфигурация таблицы UserProfile

services.AddScoped<IAzureTableStorage<UserProfile>>(factory =>
        {
            return new AzureTableStorage<UserProfile>(
                new AzureTableSettings(
                    storageConnectionString: Configuration.GetSection("AzureTable:StorageConnectionString").Value,
                    tableName: TableName.UserProfile));
        });

Также, в этом проекте, в разделе Identity, при регистрации нового пользователя, я создаю запись в таблице UserProfile, в этой таблице мы будем хранить всю дополнительную информацию, связанную с пользователями. Вы можете увидеть это в файле [Register.cshtml.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper/Areas/Identity/Pages/Account/Register.cshtml.cs) в методе OnPostAsync. Вот сегмент кода, который добавляет запись в таблицу UserProfile. Обратите внимание, что значение Id записи в таблице IdentityUsers используется в качестве Userid в таблице UserProfile, это связь между этими двумя таблицами.

UserProfile userProfile = new UserProfile
    {
        UserId = user.Id,
        EnableEmailNotifications = true,
        DaysBetweenEmailNotifications = 7
    };
    await _userProfileService.AddAsync(userProfile);

Также, для того чтобы пользователь(и) могли быть администратором в этом приложении и иметь возможность создавать/редактировать/удалять ScrapeConfigurations, я в этом же методе проверяю, является ли этот пользователь первым, который создает профиль, если да, то я присваиваю ему административное право.

if (await _userProfileService.GetUsersCount() == 1) 
    {
        Claim claim = new Claim(ClaimTypes.Role, ClaimValues.Admin, ClaimValueTypes.String);
        await _userManager.AddClaimAsync(user, claim);
    }

Чтобы иметь возможность авторизоваться с помощью прав, вам нужно добавить политику авторизации в файле [Startup.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper/Startup.cs) в методе ConfigureServices

services.AddAuthorization(options =>
    {
        options.AddPolicy(Policy.AdminOnly, policy => policy.RequireClaim(ClaimTypes.Role, ClaimValues.Admin));
    });

После того, как вы настроили политику для администраторов, вы можете добавить [Authorize(Policy = Policy.AdminOnly)] перед объявлением любого контроллера или метода, чтобы применить эту политику. Я использовал это в [ScrapeConfigsController.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper/Controllers/ScrapeConfigsController.cs). Или, если вы хотите использовать это внутри метода, вы можете использовать User.IsInRole(ProductScraper.Common.Naming.ClaimValues.Admin)

Также, в этом проекте я создал два контроллера, [ProductsController.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper/Controllers/ProductsController.cs) и [ScrapeConfigsController.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper/Controllers/ScrapeConfigsController.cs), они используют сервисы, созданные в проекте ProductScraper.Services, взаимодействуют с данными, читают и записывают в таблицы Azure или вызывают функции Azure.

ProductScraper.Common

Это небольшой проект netstandard2.0. Здесь я храню все общие вещи для всех остальных проектов. Например, чтобы избежать использования строк и магических чисел в коде, я размещаю их здесь и затем использую их как константные поля. Таким образом, когда я изменяю имя функции или очереди, мне не нужно проверять весь код, я просто изменяю имя здесь, в этом проекте. Когда вы используете функции и очереди, легко забыть изменить имя очереди во всех функциях, связанных с этой очередью, но такой подход помогает избежать такой ситуации. Вот как я отслеживаю имена таблиц:

    public static class TableName
    {
        public const string ProductInfo = "ProductInfo";
        public const string ScrapeConfig = "ScrapeConfig";
        public const string UserProfile = "UserProfile";

        public const string IdentityIndex = "IdentityIndex";
        public const string IdentityRoles = "IdentityRoles";
        public const string IdentityUsers = "IdentityUsers";
    }

А вот как я использую их позже:

    [FunctionName(FunctionName.ScrapeProduct)]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", Route = FunctionName.ScrapeProduct + "/{userId}/{productId}")] HttpRequest req,
        [Table(TableName.ProductInfo, "{userId}")] CloudTable productInfoTable,
        [Table(TableName.ScrapeConfig)] CloudTable scrapeConfigTable,
        [Queue(QueueName.EmailsToSend)] IAsyncCollector<SendGridMessage> emailMessageQueue,
        string userId,
        string productId,
        ILogger log)
    {
        ...
    }

ProductScraper.Functions

Это проект, в котором разрабатываются функции Azure. Я разработал три типа функций Azure: срабатывание по HTTP, очередь и таймер.

Функции, срабатывающие по HTTP:

  • [ScrapeProduct.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/ScrapeFunctions/ScrapeProduct.cs)
  • [GetAllScrapeConfigs.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/ScrapeConfigFunctions/GetAllScrapeConfigs.cs)
  • [GetScrapeConfig.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/ScrapeConfigFunctions/GetScrapeConfig.cs)
  • [AddScrapeConfig.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/ScrapeConfigFunctions/AddScrapeConfig.cs)
  • [UpdateScrapeConfig.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/ScrapeConfigFunctions/UpdateScrapeConfig.cs)
  • [DeleteScrapeConfig.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/ScrapeConfigFunctions/DeleteScrapeConfig.cs)
  • [EmailSender.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/Common/EmailSender.cs)

Функции, вызываемые из очереди:

  • [EmailToSendGrid.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/Common/EmailToSendGrid.cs)
  • [GenerateProductUpdateEmail.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/EmailNotificationsFunctions/GenerateProductUpdateEmail.cs)
  • [ScrapeUserProducts.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/ScrapeFunctions/ScrapeUserProducts.cs)

Функции, вызываемые по таймеру:

  • [CheckForUsersReadyForEmailNotification.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/EmailNotificationsFunctions/CheckForUsersReadyForEmailNotification.cs)

Парсинг

Исходная идея этого проекта заключалась в парсинге веб-страниц. Весь код парсинга находится в методе Scrape класса [Utils.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Functions/Common/Utils.cs). Здесь я использую пакет [HtmlAgilityPack](https://html-agility-pack.net/), который отлично справляется с парсингом веб-страниц. Чтобы получить информацию с веб-сайта, нам нужно настроить, какая информация является для нас важной, в данном случае - название, цена и наличие товара. Поскольку каждый веб-сайт имеет свой собственный макет, мы создадим конфигурацию для каждого домена, с которого мы хотим получить информацию. Я создал видео, в котором показываю, как добавить конфигурацию для веб-сайта.

_Добавить конфигурацию парсинга_ _Добавить продукт в список_

Автоматический парсинг

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

chart

Ручной парсинг

Пользователь также может вручную проверять изменения продукта при открытии веб-страницы, и в списке продуктов есть кнопка "Проверить", которая вызывает функцию парсинга.

Manual scraping

ProductScraper.Functions.Tests

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

ProductScraper.Models

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

ProductScraper.Services

В этих проектах находятся все сервисы, которые использует веб-проект для доступа к данным. Каждый сервис имеет свой собственный интерфейс и реализацию, которые можно найти в соответствующих папках Interfaces и Implementations. Как я уже упоминал выше, здесь реализованы два подхода к доступу к данным хранилища Azure: один с использованием Functions, а другой - с прямым доступом к таблицам Azure Storage.

Доступ к данным с использованием Azure функций

В файле [ScrapeConfigService.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Services/Implementations/ScrapeConfigService.cs) вы можете увидеть, как я вызываю Azure функцию для чтения/записи данных в хранилище Azure Table.

Прямой доступ к данным

В файле [ProductInfoService.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Services/Implementations/ProductInfoService.cs) вы можете увидеть, как я напрямую обращаюсь к Azure таблице, используя реализацию паттерна репозитория в файле [AzureTableStorage.cs](https://github.com/stevcooo/ProductScraper/blob/master/ProductScraper.Services/Implementations/AzureTableStorage.cs). В этом сервисе я передаю AzureTableSettings, в котором хранится информация о таблице, чтобы получить доступ к хранилищу Azure Table.

Код и тестирование Azure Functions локально

Если вы предпочитаете пропустить создание учетной записи Azure, вы можете, конечно, тестировать функции Azure локально, так же, как вы тестируете свои веб-приложения локально перед их публикацией. Чтобы иметь возможность запускать функции Azure локально, следуйте этой ссылке, где Microsoft предоставляет подробную информацию: Код и тестирование Azure Functions локально

Azure Storage Explorer

Это очень полезный инструмент, который позволяет вам просматривать содержимое вашей учетной записи хранилища, какие таблицы созданы, какие очереди есть и какие сообщения находятся в очереди. Он также позволяет просматривать вашу локальную учетную запись хранилища. Для получения дополнительной информации, пожалуйста, проверьте следующую ссылку: Azure Storage Explorer

Запуск решения

Когда вы клонируете этот репозиторий, вы сможете открыть решение с помощью VisualStudio. Вы можете проверить веб-проект, установив ProductScraper в качестве стартового проекта, или вы можете протестировать функции с помощью Postman или другого инструмента для тестирования API, установив ProductScraper.Functions в качестве стартового проекта. Также есть возможность запустить оба проекта и протестировать весь рабочий процесс. Для этого вам нужно щелкнуть правой кнопкой мыши на решении и выбрать Set Startup Projects..., а затем на появившемся экране выбрать Multiple startup projects, а затем в списке выбрать опцию Start для ProductScraper и ProductScraper.Functions. Теперь, когда вы нажимаете на кнопку Start или F5, оба проекта запускаются, и вы можете протестировать решение.

Set startup projects Choose which projects to start

Контактная информация

Если у вас возникли проблемы или вопросы по содержанию этого блога, не стесняйтесь связаться со мной по электронной почте stevan[at]kostoski.com

Источник

stevcooo/ProductScraper

TL;DR: Я хотел купить мышь, которая дважды в год находилась на скидке в 50%. Мне было лень проверять, когда она находится на скидке, поэтому я...

github.com

Блог

https://blog.kostoski.com/blog/4

Счастливого кодирования!