Как стать автором
Обновить

.Net Core, WCF и ODATA клиенты

Время на прочтение5 мин
Количество просмотров24K
Хотел написать статью про использование этих технологий в 1С, но потом решил пока написать про использование WCF, ODATA в .Net Core. Поэтому в этой статье не будет так раздражающего всех Руслиша и Ъ. .Net Core технология молодая и быстроразвивающаяся. При этом и отличающаяся от своего старшего брата.

В VS 2015 Update 3 для приложения под .Net Core нет привычного нам элемента меню «Добавить Ссылку на Службу». Вместо этого есть «Add Connected Services». Про то, как его установить, можно посмотреть здесь WCF Connected Service for .NET Core 1.0 and ASP.NET Core 1.0 is now available. Для подопытного сервиса был выбран Web сервис ЦБ Веб-сервис для получения ежедневных данных (курсы валют, учетные цены драг. металлов...). В общем подключился по методичке выше. Получил описание классов. Но при этом есть отличие от большого брата.

var client = new DailyInfoSoapClient(DailyInfoSoapClient.EndpointConfiguration.DailyInfoSoap);

Прежде всего в том, что они ушли от конфигурационных файлов.

Так перечисление:

public enum EndpointConfiguration
        {
            
            DailyInfoSoap,
            
            DailyInfoSoap12,
        }

И Конструктор

public DailyInfoSoapClient(EndpointConfiguration endpointConfiguration) : 
                base(DailyInfoSoapClient.GetBindingForEndpoint(endpointConfiguration), DailyInfoSoapClient.GetEndpointAddress(endpointConfiguration))
        {
            this.Endpoint.Name = endpointConfiguration.ToString();
            ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
        }

И вспомогательные методы:

// Надо понимать, что этот метод компилится в рантайме. Могу ошибаться.
static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials);

 private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
        {
            if ((endpointConfiguration == EndpointConfiguration.DailyInfoSoap))
            {
                System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
                result.MaxBufferSize = int.MaxValue;
                result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
                result.MaxReceivedMessageSize = int.MaxValue;
                result.AllowCookies = true;
                return result;
            }
            if ((endpointConfiguration == EndpointConfiguration.DailyInfoSoap12))
            {
                System.ServiceModel.Channels.CustomBinding result = new System.ServiceModel.Channels.CustomBinding();
                System.ServiceModel.Channels.TextMessageEncodingBindingElement textBindingElement = new System.ServiceModel.Channels.TextMessageEncodingBindingElement();
                textBindingElement.MessageVersion = System.ServiceModel.Channels.MessageVersion.CreateVersion(System.ServiceModel.EnvelopeVersion.Soap12, System.ServiceModel.Channels.AddressingVersion.None);
                result.Elements.Add(textBindingElement);
                System.ServiceModel.Channels.HttpTransportBindingElement httpBindingElement = new System.ServiceModel.Channels.HttpTransportBindingElement();
                httpBindingElement.AllowCookies = true;
                httpBindingElement.MaxBufferSize = int.MaxValue;
                httpBindingElement.MaxReceivedMessageSize = int.MaxValue;
                result.Elements.Add(httpBindingElement);
                return result;
            }
            throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
        }
        
        private static System.ServiceModel.EndpointAddress GetEndpointAddress(EndpointConfiguration endpointConfiguration)
        {
            if ((endpointConfiguration == EndpointConfiguration.DailyInfoSoap))
            {
                return new System.ServiceModel.EndpointAddress("http://www.cbr.ru/DailyInfoWebServ/DailyInfo.asmx");
            }
            if ((endpointConfiguration == EndpointConfiguration.DailyInfoSoap12))
            {
                return new System.ServiceModel.EndpointAddress("http://www.cbr.ru/DailyInfoWebServ/DailyInfo.asmx");
            }
            throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
        }

То есть все прописывается в коде.

Вторая особенность .Net Core в том, что в ней нет на данный момент DataSet. А System.Data.DataTable только пустое объявление. Поэтому метод, который в большом .Net возвращает System.Data.DataSet, в версии .Net Core возвращает стандартный для непонятного описания ArrayOfXElement. Например этот метод:

var dataset = await client.EnumValutesAsync(false);

Так как с DataSet идет схема. Я вспомнил про Accessing XML Schema Information During Document Validation и хотел сделать чтение по схеме в ExpandoObject. Но в .Net Core пока нет XmlSchema.

Теперь перейдем к ODATA. Технология сходна с WCF. Воспользуемся методичкой OData Client Code Generation Tool. Для примера возьмем сервис с версией ODATA V4.

var uri = new Uri(@"http://services.odata.org/V4/OData/OData.svc/");
        var client = new ODataDemo.DemoService(uri);
        client.Format.UseJson();

Кто не работал с ODATA может посмотреть примеры здесь — Calling an OData Service From a .NET Client (C#). Опять же есть особенности при использовании ODATA в .Net Core.

Так безобидный метод:


var products = client.Products.Expand(p => p.Supplier);
            foreach (var p in products)
            {
                Console.WriteLine("{0}\t{1}\t{2}", p.Name, p.Price, p.Supplier.Name);
            }

вызывает ошибку.

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

То есть требует такого кода:

var query = container.Products.Expand("ProductDetail,Categories");

            System.AsyncCallback OnCustomersQueryComplete = (result =>
                {
          

                    foreach (var product in query.EndExecute(result))
                    {
                        DisplayProduct(product);
                    }

                });
           query.BeginExecute(OnCustomersQueryComplete, query);

Но это не наш подход. На просторах интернета была найдена статья Extending the OData Async Extensions to DataServiceCollection Methods. В этой статье есть исходники на ODataAsyncExtensions.cs.

Там есть метод:


 public static async Task<IEnumerable<TResult>> ExecuteAsync<TResult>(this DataServiceQuery<TResult> query)
        {
            var queryTask = Task.Factory.FromAsync<IEnumerable<TResult>>(query.BeginExecute(null, null),
                (queryAsyncResult) =>
                {
                    var results = query.EndExecute(queryAsyncResult);
                    return results;
                });

            return await queryTask;
        }

Но на самом деле сейчас есть и в сбоке Microsoft.OData.Client у DataServiceQuery обычный метод:

public Task<IEnumerable<TElement>> ExecuteAsync();

Поэтому воспользуемся им:

var query = container.Products.Expand("Supplier,Categories");

            //https://blogs.msdn.microsoft.com/writingdata_services/2013/03/04/extending-the-odata-async-extensions-to-dataservicecollectiont-methods/
            //http://odata.github.io/odata.net/#OData-Client-Code-Generation-Tool
            var result = await query.ExecuteAsync();

            foreach (var product in result)
            {
                DisplayProduct(product);
            }

Про использвание ODATA 1C сервисов в большом .Net у меня статья Linq to ODATA. Так что .Net Core быстро развивается.

В конце статьи хотел бы попросить совета. Не стал писать про использование WCF и ODATA в 1С по следующим причинам.

1. Для WCF в 1С есть аналог, но он не понимает WS протоколов, сообщений в Header (хотя там все меняется). Но я не нашел каких то сервисов с ними.
2. C ODATA очень удобно работать из .Net. Конечно можно сделать отдельную сборку с методами на .Net или использовать динамическую компиляцию, но 1С ки в большинстве массе своей не знают C#.

На данный момент 1С ников заинтересовали такие темы.

1C Messenger для отправки сообщений, файлов и обмена данными между пользователями 1С, вэб страницы, мобильными приложениями а ля Skype, WhatsApp

.Net в 1С. На примере использования HTTPClient,AngleSharp. Удобный парсинг сайтов с помощью библиотеки AngleSharp, в том числе с авторизацией аля JQuery с использованием CSS селекторов. Динамическая компиляция

Использование классов .Net в 1С для новичков

Так что если у кого есть на примете компоненты с Вау-эффектом, пишите, буду премного благодарен.
Теги:
Хабы:
Всего голосов 14: ↑13 и ↓1+12
Комментарии0

Публикации

Истории

Работа

Ближайшие события