Структура функций Azure из репозитория Git

У меня есть класс С#, который выполняет поиск адреса. Я хочу представить это как функцию Azure. Я просматривал документацию, но не понимаю, как я могу/возможно ли сделать следующее:

  1. У меня есть репозиторий Git в Team Services, который содержит библиотеку классов моего AddressLookup. Может ли моя функция ссылаться на этот проект?
  2. Если я посмотрю на структуру папок сайта, я увижу, что он скопировал все исходные файлы из репозитория Git. Могу ли я заставить его построить решение или он буквально просто извлекает все файлы?
  3. Где в решении я помещаю функцию? Нужно ли создавать папку решения с именем функции и помещать туда соответствующие файлы?
  4. Мой класс AddressLookup возвращает объект, определенный в библиотеке классов. Сможет ли функция использовать и вернуть это?

Спасибо

Алекс


person ADringer    schedule 14.10.2016    source источник


Ответы (2)


Дополнение к вопросу 1. Вы пытаетесь настроить непрерывную интеграцию? Для непрерывной интеграции с функциями Azure вы можете ссылаться на следующее:

https://azure.microsoft.com/en-us/documentation/articles/functions-continuous-deployment/#setting-up-continuous-deployment

http://flgmwt.github.io/azure/azure-functions/c-sharp/2016/04/04/azure-fns-with-ci.html

-- Обновление от 17 октября --

Ниже приведены шаги, характерные для Team Services:

  1. Убедитесь, что ваша учетная запись VSTS связана с подпиской Azure. Следуйте инструкциям в этом статья.

  2. Перейдите на портал функций для своего приложения-функции и нажмите Настройки приложения-функции -> Настроить непрерывную интеграцию.

  3. В колонке Развертывания нажмите Настройка и настройте информацию о источнике развертывания (см. образец снимка ниже). Нажмите кнопку ОК. Подождите, пока синхронизация пройдет успешно. Закройте колонку Развертывания.

введите здесь описание изображения

  1. Подождите минуту и ​​обновите сеанс портала функций. Теперь вы должны увидеть функцию, добавленную на ваш сайт функций. На снимке ниже показана моя функция AddressLookup, которая была синхронизирована с моим проектом Team Services под названием MyFirstProject.

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

введите здесь описание изображения

--Конец обновления 17 октября--

Ответ на вопрос 2. Вот хорошая документация, описывающая структуру папок в Функциях Azure: https://azure.microsoft.com/en-us/documentation/articles/functions-reference/

Я также рекомендую дополнительную документацию по разработке C# для Функций Azure: https://azure.microsoft.com/en-us/documentation/articles/functions-reference-csharp/

Ответы на Q3 и Q4: я попытаюсь ответить на них, предоставив пример реализации. У меня нет никакого контекста реализации вашей библиотеки AddressLookup, однако в интересах предоставления примера я собираюсь сделать дикий прыжок и предположить, что это библиотека, которая будет выполнять некоторые операции геокодирования. Предполагая, что вы хотите использовать эту библиотеку в функции, запускаемой HTTP, вы можете начать с создания AddressLookup.dll, а затем загрузить ее в папку bin внутри вашей функции. Затем вы можете ссылаться на эту DLL из своего функционального сценария.

Например, взяв за основу эту статью, Я создал библиотеку AddressLookup.dll в Visual Studio со следующей реализацией. Эта DLL будет служить прокси для вашей библиотеки AddressLookup, чтобы я мог продемонстрировать, как мы можем использовать ее в функции.

using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Web;
using System.Xml.Linq;

namespace AddressLookup
{
    public class GeoLocation
    {
        public double Longitude { get; set; }

        public double Latitude { get; set; }
    }

    public class GeoCoder
    {
        private const string geoCodeLookupUrlPattern =
            "https://maps.googleapis.com/maps/api/geocode/xml?address={0}&key={1}";

        private const string addressLookupUrlPattern =
            "https://maps.googleapis.com/maps/api/geocode/xml?latlng={0},{1}&key={2}";

        private string _apiKey = null;
        public GeoCoder(string apiKey)
        {
            if (string.IsNullOrEmpty(apiKey))
            {
                throw new ArgumentNullException("apiKey");
            }

            _apiKey = apiKey;
        }

        public GeoLocation GetGeoLocation(string address)
        {
            GeoLocation loc = null;
            string encodedAddress = HttpUtility.UrlEncode(address);
            string url = string.Format(geoCodeLookupUrlPattern, encodedAddress, _apiKey);

            WebRequest request = WebRequest.Create(url);

            using (WebResponse response = request.GetResponse())
            {
                using (Stream stream = response.GetResponseStream())
                {
                    if (stream != null)
                    {
                        XDocument document = XDocument.Load(new StreamReader(stream));

                        XElement longitudeElement = document.Descendants("lng").FirstOrDefault();
                        XElement latitudeElement = document.Descendants("lat").FirstOrDefault();

                        if (longitudeElement != null && latitudeElement != null)
                        {
                            loc = new GeoLocation
                            {
                                Longitude = Double.Parse(longitudeElement.Value, CultureInfo.InvariantCulture),
                                Latitude = Double.Parse(latitudeElement.Value, CultureInfo.InvariantCulture)
                            };
                        }
                    }
                }
            }

            return loc;
        }

        public string GetAddress(GeoLocation loc)
        {
            string address = null;
            string url = string.Format(addressLookupUrlPattern, loc.Latitude, loc.Longitude, _apiKey);

            WebRequest request = WebRequest.Create(url);

            using (WebResponse response = request.GetResponse())
            {
                using (Stream stream = response.GetResponseStream())
                {
                    if (stream != null)
                    {
                        XDocument document = XDocument.Load(new StreamReader(stream));
                        XElement element = document.Descendants("formatted_address").FirstOrDefault();
                        if (element != null)
                        {
                            address = element.Value;
                        }
                    }
                }
            }

            return address;
        }
    }
}

Теперь давайте создадим функцию, активируемую HTTP, выполнив следующие шаги:

  1. Перейдите на Портал функций. Создайте функцию, используя шаблон HTTP Trigger — C#.
  2. Введите имя (например, AddressLookup) и уровень авторизации (например, Anonymous). Теперь вы должны увидеть функцию с именем AddressLookup, созданную с предварительно заполненным кодом.
  3. На левой панели нажмите кнопку Настройки приложения-функции.
  4. Необязательно: нажмите Настроить параметры приложения. В разделе "Настройки приложения" добавьте значение ключа GoogleMapsAPIKey с помощью ключа API, затем нажмите кнопку Сохранить. Примечание. Если вы пропустите этот шаг, позже вам потребуется жестко закодировать ключ в коде функции.
  5. Затем используйте службу Kudu для загрузки DLL. Нажмите кнопку Перейти к Куду. Это запустит новое окно браузера с консолью cmd. Введите следующее, чтобы перейти в каталог функций:

    cd site\wwwroot\AddressLookup

  6. Создайте папку bin, введя mkdir bin в командной строке следующим образом:

    введите здесь описание изображения

    Дважды щелкните папку bin и загрузить (см. "Добавить файлы") AddressLookup.dll в папку. Когда вы закончите, вы должны сделать аналогичный снимок ниже,

    введите здесь описание изображения

  7. Вернитесь на портал функций. В редакторе вашей функции в нижней части раздела «Код» нажмите Просмотр файлов. Теперь вы должны увидеть только что созданную папку bin следующим образом:

    введите здесь описание изображения

Замените содержимое предварительно заполненного скрипта функции следующим кодом.

#r "AddressLookup.dll"

using System;
using AddressLookup;
using System.Net;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    log.Info($"C# HTTP trigger function processed a request. RequestUri={req.RequestUri}");

    // Reading environment variable from App Settings, replace with hardcoded value if not using App settings
    string apiKey = System.Environment.GetEnvironmentVariable("GoogleMapsAPIKey", EnvironmentVariableTarget.Process);

    // Get request body
    dynamic data = await req.Content.ReadAsAsync<object>();
    string address = data?.address;
    string name = data?.name;

    GeoCoder geoCoder = new GeoCoder(apiKey);
    GeoLocation loc = geoCoder.GetGeoLocation(address);
    string formattedAddress = geoCoder.GetAddress(loc);

    HttpResponseMessage message = null;
    if (name == null)
    {
        message = req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name in the request body");
    }
    else
    {
        var msg = $"Hello {name}. Lon: '{loc.Longitude}', Lat: '{loc.Latitude}', Formatted address: '{formattedAddress}'"; 
        message = req.CreateResponse(HttpStatusCode.OK, msg);
    }

    return message;
}
  1. Нажмите кнопку Сохранить.
  2. В разделе «Выполнить» укажите следующее тело запроса:

    { "name": "Azure", "address": "One Microsoft Way Redmond WA 98052" }

  3. Нажмите кнопку Выполнить.

  4. Вы должны увидеть некоторые записи журнала, похожие на следующие:

2016-10-15T03:54:31.538 C# HTTP trigger function processed a request. RequestUri=https://myfunction.azurewebsites.net/api/addresslookup 2016-10-15T03:54:31.773 Function completed (Success, Id=e4308c0f-a615-4d43-8b16-3a6afc017f73)

и следующее ответное сообщение HTTP,

"Hello Azure. Lon: '-122.1283833', Lat: '47.6393225', Formatted address: '1 Microsoft Way, Redmond, WA 98052, USA'"

Поскольку это функция, активируемая HTTP, вы также можете протестировать свою функцию с помощью Postman. См. снимок ниже,

введите здесь описание изображения

Если вы загрузите свою собственную DLL на шаге 5 и отредактируете код функции для вызова вашей библиотеки, функция должна работать так же хорошо.

person Ling Toh    schedule 15.10.2016
comment
Спасибо за такой подробный ответ! Я следил за вашим ответом/ссылками, но не знаю, как автоматически развернуть мою dll AddressLookup в папку bin через CI. Моя единственная идея заключается в том, что у меня есть сборка Team Services, которая передает файл по FTP в функцию? - person ADringer; 16.10.2016
comment
Я обновил пост. Пожалуйста, обратитесь к сегменту с пометкой --Обновление 17 октября-- - person Ling Toh; 18.10.2016

Сборка, на которую вы хотите сослаться, заморожена или вы хотите видеть обновления? Если вы не хотите видеть обновления, посмотрите ответ Линг То.

Но если вы хотите видеть обновления при обновлении сборки:

Свяжите свою функцию с какой-либо формой непрерывной доставки. В официальной документации объясняется, как это сделать. . На данный момент вам, кажется, нужен отдельный репозиторий git, проект VSTS (или что-то еще), чтобы сделать это легко. (В Kudu можно отредактировать процесс развертывания, но я бы избегал этого, если это возможно).

Проект функций должен содержать только код самих функций. Таким образом, он должен содержать что-то вроде следующего:

global.json
host.json
packages.config
Web.config
function1/
function1/run.csx
function1/project.json
function1/function.json

Где вы должны заменить function1 на имя вашей функции.

После того, как вы настроили это для отправки на ваш хост функций, вы прошли большую часть пути к тому, чего хотите. Затем добавьте подкаталог function1/run, в который вы поместите файл yourAssembly.dll. Это должно быть автоматически скопировано сюда при успешной сборке проекта сборки. У меня нет опыта работы с VSTS, чтобы точно знать, как это сделать лучше всего, поэтому вам может понадобиться задать другой вопрос.

Теперь вы разместили сборку в нужном месте. Теперь вы ссылаетесь на него, добавляя справочную строку сборки в начало файла run.csx:

#r "yourAssembly.dll"

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

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

В идеальном мире этого будет достаточно.

Но функции не вызывают перестроение, если сборка обновляется в данный момент, только если обновляется project.json, run.csx или function.json. Поэтому в рамках этого процесса я просматриваю конец файла и добавляю или удаляю пустую строку. Оно должно быть бессмысленным (чтобы вы не меняли что-то важное), но достаточным для того, чтобы ваш инструмент контроля версий подумал, что файл изменился. Очевидно, что в этом шаге нет необходимости, если вы также внесли в файл другие изменения.

Если вы используете git, вы должны зафиксировать и отправить изменения. Функции увидят, что функция изменилась, и перекомпилируют. Вы должны увидеть это на панели журнала в самой функции.

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

person Iain Peddie    schedule 30.01.2017