
Довольно частый вопрос, который возникает у тех кто пробует разрабатывать под UWP это «Как UWP приложению получить данные из базы данных SQL Server?». Напрямую данные получить нельзя. Работа с базами данных у UWP приложений требует настроенного веб-сервиса.
Разработчики клиентских приложений как правило далеки от созданий серверных бэкендов, но им необходимо иметь хотя бы представление о сервисах.
Под катом описание того как создать локальный WCF REST сервис и получить от него данные приложением UWP. Сервис сможет получать данные из базы данных SQL Server, созданной в Azure (но аналогично можно получить данные и из любой локальной базы). Дополнительно, чтобы все не выглядело сильно банально, будет рассмотрена возможность размещения самого сервиса в Azure для работы с ним из все того же клиентского UWP приложения.
Создание REST сервиса
Для того чтобы тестировать наши приложения UWP создадим простой сервис. Я опишу создание WCF, а не Web API 2 сервиса, так как последний раз интересовался написанием бэкенда несколько лет назад (а не потому что у него есть преимущества).
Удаляем код примера, который будет создан для нас автоматически
Код, который удаляется
Из IService1.cs
Из Service1.svc.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 за уточнения/правки
