Pull to refresh

WCF REST сервисы и UWP приложения

Reading time 8 min
Views 11K

Довольно частый вопрос, который возникает у тех кто пробует разрабатывать под UWP это «Как UWP приложению получить данные из базы данных SQL Server?». Напрямую данные получить нельзя. Работа с базами данных у UWP приложений требует настроенного веб-сервиса.

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

Под катом описание того как создать локальный WCF REST сервис и получить от него данные приложением UWP. Сервис сможет получать данные из базы данных SQL Server, созданной в Azure (но аналогично можно получить данные и из любой локальной базы). Дополнительно, чтобы все не выглядело сильно банально, будет рассмотрена возможность размещения самого сервиса в Azure для работы с ним из все того же клиентского UWP приложения.

Создание REST сервиса


Для того чтобы тестировать наши приложения UWP создадим простой сервис. Я опишу создание WCF, а не Web API 2 сервиса, так как последний раз интересовался написанием бэкенда несколько лет назад (а не потому что у него есть преимущества).



Удаляем код примера, который будет создан для нас автоматически

Код, который удаляется
Из IService1.cs
  [OperationContract]
        string GetData(int value);

        [OperationContract]
        CompositeType GetDataUsingDataContract(CompositeType composite);

        // TODO: Add your service operations here

  // Use a data contract as illustrated in the sample below to add composite types to service operations.
    [DataContract]
    public class CompositeType
    {
        bool boolValue = true;
        string stringValue = "Hello ";

        [DataMember]
        public bool BoolValue
        {
            get { return boolValue; }
            set { boolValue = value; }
        }

        [DataMember]
        public string StringValue
        {
            get { return stringValue; }
            set { stringValue = value; }
        }
    }

Из Service1.svc.cs

       public string GetData(int value)
        {
            return string.Format("You entered: {0}", value);
        }

        public CompositeType GetDataUsingDataContract(CompositeType composite)
        {
            if (composite == null)
            {
                throw new ArgumentNullException("composite");
            }
            if (composite.BoolValue)
            {
                composite.StringValue += "Suffix";
            }
            return composite;
        }


Добавляем в IService1.cs следующую операцию контракта и код класса:

    [ServiceContract]
    public interface IService1
    {
        [WebGet(UriTemplate = "/GetScheduleJson",
        RequestFormat = WebMessageFormat.Json,
        ResponseFormat = WebMessageFormat.Json,
        BodyStyle = WebMessageBodyStyle.Bare)]
        List<Timetable> GetScheduleJson();
    }

    [DataContract]
    public class Timetable
    {
        [DataMember]
        public int id { get; set; }

        [DataMember]
        public DateTime arrivaltime { get; set; }

        [DataMember]
        public Int16 busnumber { get; set; }

        [DataMember]
        public string busstation { get; set; }
    }

Поле arrivaltime можно было бы сделать типа TimeSpan, но с типом DateTime гораздо удобнее впоследствии работать в JSON. В операции контракта можно указать и формат WebMessageFormat.Xml. Сама операция помечена атрибутом WebGet, а значит возвращает результат. При необходимости только выполнить код можно пометить операцию WebInvoke.

А в Service1.svc.cs добавляем следующий код:

        public List<Timetable> GetScheduleJson()
        {
            return GetSchedule();
        }

        private List<Timetable> GetSchedule()
        {
            List<Timetable> Schedule = new List<Timetable>
          {
            new Timetable
            {
                id=1, arrivaltime=DateTime.Parse("12:05:00"), busnumber=5, busstation ="Березка"
            },
            new Timetable
            {
                id=2, arrivaltime =DateTime.Parse("12:10:00"), busnumber=5, busstation ="Детский мир"
            }
          };
            return Schedule;
        }

Упрощенно сконфигурируем Web.config. Добавим endpointBehavior в раздел behaviors:

      <endpointBehaviors>
        <behavior name="restBehavior">
          <webHttp />
        </behavior>
      </endpointBehaviors>

И ниже в уже существующий код в тэг behavior добавим атрибут name со значением «servicebehavior»:

     <serviceBehaviors>
        <behavior name="servicebehavior"> 
          <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>

Теперь в корень тега system.serviceModel можем добавить сервис и endpoint:

    <services>
      <service name ="ServiceForUWP.Service1" behaviorConfiguration ="servicebehavior" >
      <endpoint name ="RESTEndPoint" contract ="ServiceForUWP.IService1" binding ="webHttpBinding"
        address ="" behaviorConfiguration ="restBehavior"/>
      </service>
    </services>


Получаем готовый сервис.

Запустив отладку (при этом необходимо чтобы в Solution Explorer был выделен проект) и открыв в браузере (в моем случае порт 64870)
http://localhost:64870/Service1.svc/GetScheduleJson
получим результат в виде JSON:
[{«arrivaltime»:"\/Date(1487408400000+0300)\/",«busnumber»:5,«busstation»:«Березка»,«id»:1},{«arrivaltime»:"\/Date(1487408700000+0300)\/",«busnumber»:5,«busstation»:«Детский мир»,«id»:2}]

Если мы захотим возвращать данные отфильтрованные по какому-либо параметру, то можем изменить операцию на подобную:

        [WebGet(UriTemplate = "/GetScheduleJson/{id}",
        RequestFormat = WebMessageFormat.Json,
        ResponseFormat = WebMessageFormat.Json,
        BodyStyle = WebMessageBodyStyle.Bare)]
        List<Timetable> GetScheduleJson(int id);

Теперь реализовав метод
List<Timetable> GetScheduleJson(int id)
мы получим результат зайдя по адресу http://localhost:64870/Service1.svc/GetScheduleJson/1. В данном случае 1 – это параметр, передаваемый методу.

Создание клиентского UWP приложения


Получить данные из приложения UWP проще простого. Есть 2 варианта: использовать Windows.Web.Http.HttpClient или же System.Net.Http.HttpClient.

Оба клиента могут быть использованы в UWP приложениях. Web чуть более новый (он вышел в 8.1), и он больше подходит для нативной разработки под UWP. Если же вы планируете использовать код в ASP.NET приложениях или в приложениях Xamarin под другие мобильные платформы, то вам лучше взять Net клиента. Кроме того на данный момент у Web клиента больше настроек и возможностей (например, возможность использования особого SSL сертификата для аутентификации).

Собственно, в .NET Core для приложений UWP, System.Net.Http это обертка над компонентом Windows.Web.Http. Но эта обертка поддерживает те же API, что и пространство System.Net.Http из .NET.

Далее два простых примера получения данных от сервиса:

var uri = new Uri("http://localhost:64870/service1.svc/GetScheduleJson");
            var client = new Windows.Web.Http.HttpClient();
            var json = await client.GetStringAsync(uri);

var uri = new Uri("http://localhost:64870/service1.svc/GetScheduleJson");
            System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
            System.Net.Http.HttpResponseMessage responseGet = await client.GetAsync(uri);
            string json = await responseGet.Content.ReadAsStringAsync();

Для того, чтобы десериализовать данные можно использовать NuGet пакет Newtonsoft.Json:

   
       List<Timetable> appsdata = JsonConvert.DeserializeObject<Timetable>(json);

Конечно, необходимо добавить еще и код класса Timetable (точно такой же как и в приложении сервиса).

Создание базы данных SQL Server в Azure


Создать базу данных в Azure несложно. Нужно зайти на портал и заполнить следующие поля:



Останется только выбрать ценовую категорию. Цены начинаются от 5 USD за месяц. Эту сумму вполне себе покроет бонус, получаемый от бесплатной регистрации в Dev Essentials (25 USD дается каждый месяц в течение года). При регистрации необходимо привязывать карточку. Для подобных регистраций, как правило, создается дополнительная карточка, лимит которой можно регулировать.

Строка подключения ASP.NET (проверка подлинности SQL) к базе данных в таком случае будет:

Server=tcp:timetableserverok.database.windows.net,1433;Initial Catalog=timetabledb;Persist Security Info=False;User ID={your_username};Password={your_password};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;

Ее можно посмотреть, зайдя в свойства базы данных на портале Azure. Для того чтобы получить возможность доступа к базе данных с текущей машины необходимо добавить ее IP в список брандмауэра.



Редактирование возможно из окна Server Explorer Visual Studio





Создадим какую-нибудь таблицу



И внесем любые тестовые данные.

Получение сервисом данных из базы SQL Server-а


Для того чтобы «вытянуть» данные из базы нам необходимо внести небольшие изменения в проект нашего сервиса. Добавить два пространства имен:

using System.Data;
using System.Data.SqlClient;

Переменную содержащую текст строки подключения к базе SQL Server:

public string ConnectionString = "Server=tcp:timetableserverok.database.windows.net,1433;Initial Catalog=timetabledb;Persist Security Info=False;User ID=alexej;Password=ЗДЕСЬ_ПАРОЛЬ;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;";

Я использую только что созданную базу в Azure, но, как уже упоминал, можно подключаться к любым базам данных в том числе и локальным (строку подключения в таком случае, конечно, необходимо будет заменить).

Теперь, чтобы «вытянуть» данные из базы, необходимо изменить код метода GetSchedule на следующий:

         private List<Timetable> GetSchedule()
        {
            using (DataSet ds = new DataSet())
            {
                using (SqlConnection sqlCon = new SqlConnection(ConnectionString))
                {
                    try
                    {
                        sqlCon.Open();
                        string sqlStr = "select * from Timetable";
                        using (SqlDataAdapter sqlDa = new SqlDataAdapter(sqlStr, sqlCon))
                        {
                            sqlDa.Fill(ds);
                        }
                    }
                    catch
                    {
                        return null;
                    }
                    finally
                    {
                        sqlCon.Close();
                    }
                }

                List<Timetable> Schedule = new List<Timetable>();

                using (DataTable dt = ds.Tables[0])
                {
                    foreach (DataRow dr in dt.Rows)
                    {
                        Schedule.Add(new Timetable()
                        {
                            id = Convert.ToInt16((dr["ID"])),
                            arrivaltime = DateTime.Parse(dr["arrivaltime"].ToString()),
                            busnumber = Convert.ToInt16((dr["busnumber"] ?? 0)),
                            busstation = dr["busstation"].ToString()
                        });
                    }
                }
                return Schedule;
            }
        }

Создание облачного сервиса


Для того чтобы разместить сервис в Azure необходимо скачать и установить Azure SDK for .NET (приблизительно 450 Мб) и создать новый проект особого типа Cloud Service.



Выбираем роль и переименовываем на свой вкус



В результате у нас будет создано два проекта: AzureCloudServiceTimetable и TimetableService
В второй (TimetableService) мы можем скопировать код из нашего локального сервиса. А именно – содержимое файлов IService1.cs, Service1.svc.cs, Web.config. После этого проект можно протестировать. В файле Web.config перед публикацией можно сделать изменения. В теге

<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>

изменить значения на false

Для публикации на Azure необходимо создать пакеты. На проекте AzureCloudServiceTimetable нужно вызвать контекстное меню и выбрать Package.



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



Необходимо установить 2 флажка: «Развернуть, даже если одна или несколько ролей содержат отдельный экземпляр» и «Запустить развертывание». После развертывания и запуска (запуск может занять некоторое время) можно будет делать запрос по URI:

http://servicetimetable.cloudapp.net/Service1.svc/GetScheduleJson

Этот адрес можно использовать в приложении UWP. Подробнее о развертывании: Создание и развертывание облачной службы

PS: Спасибо пользователю dmitry_dvm за уточнения/правки
Tags:
Hubs:
+12
Comments 34
Comments Comments 34

Articles