C# — Дмитрий [KP0H] Пелевин https://pelevin.pro Сохраняю тишину в голове Tue, 02 Dec 2014 18:06:13 +0000 ru-RU hourly 1 48722140 Создание простейшей WCF-службы. Часть 3. https://pelevin.pro/2014/12/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%bf%d1%80%d0%be%d1%81%d1%82%d0%b5%d0%b9%d1%88%d0%b5%d0%b9-wcf-%d1%81%d0%bb%d1%83%d0%b6%d0%b1%d1%8b-%d1%87%d0%b0%d1%81%d1%82%d1%8c-3/ https://pelevin.pro/2014/12/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%bf%d1%80%d0%be%d1%81%d1%82%d0%b5%d0%b9%d1%88%d0%b5%d0%b9-wcf-%d1%81%d0%bb%d1%83%d0%b6%d0%b1%d1%8b-%d1%87%d0%b0%d1%81%d1%82%d1%8c-3/#respond Tue, 02 Dec 2014 18:04:52 +0000 http://pelevin.pro/?p=1896 Примечание автора: эта статья была написана в районе 2008 года, и, скорее всего, она уже морально устарела. Однако, судя по отзывам, все еще кому-то полезна. Статья разделена на несколько частей.

Часть 1, Часть 2, Часть 3

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

Итак, настало время создать клиентское приложение и попробовать нашу службу в действии.

Добавим в наше решение новый проект — консольное приложение Windows и назовем его CalcClient.

Консольное клиентское приложение

Консольное клиентское приложение

К созданному проекту нужно добавить ссылку на службу (Add ServiceReference).

Добавление ссылки на сервис

Добавление ссылки на сервис

В появившемся диалоге нужно нажать кнопку «Discover» (после чего в списке появится наша служба), дать имя ссылке на службу (напрмер, ServiceReference) и нажать Ok (не забываем запустить студию от имени администратора в Висте):

Диалоговое окно добавления ссылки на сервис

Диалоговое окно добавления ссылки на сервис

Примечание

VS использует то же самое приложение WcfSvcHost для создания Service Reference, ведь ей нужно получить метаданные службы, а для этого нужна функционирующая точка метаданных.

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

Теперь перейдем в тело метода Main (точки входа нашего консольного приложения) и напишем следующий код:

Main

Этот код демонстрирует, как правильно использовать клиентский объект для взаимодействия со службой, и как правильно закрывать соединение.

Сделаем оба проекта — библиотеку службы и наш новый клиент — стартовыми (это делается в свойствах решения).

Диалоговое окно выбора стартовых проектов

Диалоговое окно выбора стартовых проектов

Готово! Можно запускать решение. Вот какой результат мы получили:

Результат обращения к WCF службе

Результат обращения к WCF службе

Что логично — 1 + 2 = 3.

Изменим теперь код вызова метода службы на следующий:

CalcOperationResult res = client.Calc(1, 0, CalcOperationType.Div);

И запустим приложение. В результате получаем информацию об исключении (деление на ноль):

Результат обращения к WCF-службе — произошла ошибка

Результат обращения к WCF-службе — произошла ошибка

Заключение

Помните, в связи с выходом нового Framework’а материал статьи может быть устаревшим.

Не забывайте запускать VS с правами Администратора.

]]>
https://pelevin.pro/2014/12/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%bf%d1%80%d0%be%d1%81%d1%82%d0%b5%d0%b9%d1%88%d0%b5%d0%b9-wcf-%d1%81%d0%bb%d1%83%d0%b6%d0%b1%d1%8b-%d1%87%d0%b0%d1%81%d1%82%d1%8c-3/feed/ 0 1896
Создание простейшей WCF-службы. Часть 2. https://pelevin.pro/2014/12/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%bf%d1%80%d0%be%d1%81%d1%82%d0%b5%d0%b9%d1%88%d0%b5%d0%b9-wcf-%d1%81%d0%bb%d1%83%d0%b6%d0%b1%d1%8b-%d1%87%d0%b0%d1%81%d1%82%d1%8c-2/ https://pelevin.pro/2014/12/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%bf%d1%80%d0%be%d1%81%d1%82%d0%b5%d0%b9%d1%88%d0%b5%d0%b9-wcf-%d1%81%d0%bb%d1%83%d0%b6%d0%b1%d1%8b-%d1%87%d0%b0%d1%81%d1%82%d1%8c-2/#respond Tue, 02 Dec 2014 18:04:42 +0000 http://pelevin.pro/?p=1889 Примечание автора: эта статья была написана в районе 2008 года, и, скорее всего, она уже морально устарела. Однако, судя по отзывам, все еще кому-то полезна. Статья разделена на несколько частей.

Часть 1, Часть 2, Часть 3

Реализация сервиса

Итак, мы создали контракт нашей службы, теперь нужно этот контракт реализовать. Сразу переименуем файл Service1.cs в CalcService.cs (VS спросит, хотите ли вы переименовать тип, хранящийся в файле — нужно согласиться) и откроем его.

В xml-комментарии перед описанием класса сервиса видим то же самое предупреждение, что и в случае с интерфейсом. Удаляем его, а также все автоматически сгенерированное содержимое класса — оно нам не понадобится.

Теперь переводим курсор на имя наследуемого интерфейса, нажимаем Alt+Shift+F10 и в появившемся меню выбираем Implement interface ‘ICalcService’. Осталось лишь реализовать наш метод Calc:

Код метода Calc — Реализация службы CalcService

Код метода Calc — Реализация службы CalcService

WcfTestClient и WcfServiceHost

В состав .NET Framework 3.5 входит пара замечательных утилит для тестирования WCF-приложений: это WcfTestClient и WcfServiceHost. Расскажу о них поподробнее.

Первая из них — это универсальный тестовый клиент для WCF-сервисов, позволяющий вызывать методы службы, передавая им различные параметры, а также просматривать весь Xml-код запроса и ответа.

Примечание

Тестовый клиент, к сожалению, не поддерживает сеансы (о них подробнее в одной из следующих статей) и передачу композитных типов данных.

Так как тестовый клиент оперирует только с простыми типами данных, мы не сможем с помощью него проверить действие функции Calc.

Вторая утилита — это универсальное хост-приложение для WCF-сервисов. Она автоматически запускается студией при нажатии клавиши F5, когда стартовым проектом является библиотека WCF-службы. WcfServiceHost использует параметры сервиса из файла app.config библиотеки службы и, при наличии ошибок, сообщает об этом.

Попробуем запустить наше приложение, нажав клавишу F5. В системном трее появится значок WcfSvcHost. Дважды кликнув по нему, увидим такое окно:

WCF Service Host

WCF Service Host

В списке служб находится только одна служба — наш CalcService. Статус у него — «запущен», значит никаких ошибок на стороне службы не возникло. Если в статусе стоит «error» (ошибка), можно выбрать службу в списке и в поле Additional Information просмотреть подробное описание ошибки.

Второе окно, нас интересующее, — это тестовый клиент. После получения метаданных службы он выводит список методов в левой колонке. Как и следовало ожидать, наш метод Calc() тестовым клиентом не поддерживается — ведь он использует композитные типы данных.

WCF Test Client

WCF Test Client

Конфигурация службы

Настало время изучить файл конфигурации службы и разобраться с ее основными параметрами. Как мы помним из предыдущей статьи, точка соединения содержит в себе три основных объекта: адрес, привязку и контракт. Именно их мы сейчас и зададим.

Открываем файл app.config из проекта библиотеки службы и находим раздел <system.serviceModel>.

Перед тегом, открывающим данный раздел, видим следующий комментарий:

«<!— When deploying the service library project, the content of the config file must be added to the host’s app.config file. System.Configuration does not support config files for libraries. —>»

Переведу: при развертывании проекта библиотеки службы, содержимое конфигурационного файла должно быть добавлено в файл app.config приложения-хоста. Пространство имен System.Configuration не поддерживает конфигурационные файлы для библиотек. Но нас это пока не касается, так как мы используем утилиту WcfSvcHost, которая автоматически считывает параметры из файла конфигурации библиотеки.

Сейчас в разделе <system.serviceModel> видим только один подраздел — <services>, а в нем только один сервис — наш CalcService. У конфигурации службы есть два основных атрибута — это name, в котором нужно указать полное имя класса нашей службы (WcfCalcService.CalcService), и behaviorConfiguration — имя конфигурации поведения службы. Внутри описания службы объявлено две точки соединения — для самого сервиса и для его метаданных.

По умолчанию для точки соединения не указан адрес — установим его в «http://localhost/CalcService». В качестве привязки (binding) выберем пока basicHttpBinding (это привязка, которая обеспечивает просто транспорт по протоколу HTTP). В качестве контракта (contract) указан наш интерфейс — WcfCalcService.ICalcService. Также у точки соединения службы есть дочерний элемент <identity>, который служит для идентификации веб-приложения. Мы его пока проигнорируем.

Следующая точка соединения — это точка метаданных. Она используется для получения информации о контракте службы и сигнатурах ее методов. В качестве адреса у нее установлено значение «mex», без указания типа протокола. Это — относительный адрес, и для того, чтобы он работал, необходимо указание для него базового адреса. Что и сделано студией по умолчанию: в дочернем элементе точки соединения host\baseAddresses содержится элемент «add» с атрибутом baseAddress. Его значение мы установим в «http://localhost/CalcService». Теперь, при обращении по адресу «http://localhost/CalcService» клиент будет попадать к нашей службе, а по адресу «http://localhost/CalcService/mex» — к точке, обслуживающей метаданные.

Примечание

Заметьте, что точка соединения для метаданных использует специальные привязку и контракт — mexHttpBinding и IMetadataExchange соответственно. Менять эти значения не нужно.

Следующим за разделом <services> идет раздел <behaviors>, который описывает поведения служб. Для нашей службы там уже создан элемент «<behavior name=”WcfCalcService.Service1Behavior”>» (помните, в качестве значения атрибута behaviorConfiguration нашей службы как раз установлено «WcfCalcService.Service1Behavior»). В этом разделе содержится два элемента, перед каждым из которых видим увесистый комментарий:

1. To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment — Чтобы избежать раскрытия метаданных, установите значение ниже в false и точки соедидения метаданных перед развертыванием

2. 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 — Чтобы получать информацию об исключениях при возникновении ошибок в целях отладки, установите значение ниже в true. Установите значение ниже в false перед развертыванием для избежания раскрытия информации об исключительных ситуациях.

Нам до развертывания приложения еще далеко, поэтому пока оставляем все как есть.

Примечание

При возникновении необработанной исключительной ситуации на стороне службы, независимо от того, передаем мы подробную информацию об исключительной ситуации или нет, клиентский объект перейдет в состояние Faulted (об этом подробнее в одной из следующих статей), и соединение нужно будет создавать заново. Это очень неудобно, когда используются пошаговые сценарии с поддержкой сеансов, поэтому, для наглядности дальнейших демонстраций возможностей WCF мы не выкидываем Exception в нашем методе Calc, а включаем его в состав «результирующего» объектаCalcOperationResult.

Ниже приведен полный код файла app.config библиотеки службы:

WCF Service app.config file

WCF Service app.config file

 

 

 

 

Примечание

Если вы работаете в операционной системе Windows Vista, и попробуете сейчас запустить службу, появится окно приложения WcfSvcHost с сообщением об ошибке для службы калькулятора:

«System.ServiceModel.AddressAccessDeniedException: HTTP could not register URL http://+:80/CalcService/. Your process does not have access rights to this namespace (seehttp://go.microsoft.com/fwlink/?LinkId=70353 for details). → System.Net.HttpListenerException: Access is denied»

Это означает, что у процесса нет прав на регистрацию точки соединения по указанному адресу. Чтобы этой ошибки избежать, нужно просто запустить Visual Studio от имени администратора.

]]>
https://pelevin.pro/2014/12/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%bf%d1%80%d0%be%d1%81%d1%82%d0%b5%d0%b9%d1%88%d0%b5%d0%b9-wcf-%d1%81%d0%bb%d1%83%d0%b6%d0%b1%d1%8b-%d1%87%d0%b0%d1%81%d1%82%d1%8c-2/feed/ 0 1889
Создание простейшей WCF-службы. Часть 1. https://pelevin.pro/2014/12/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%bf%d1%80%d0%be%d1%81%d1%82%d0%b5%d0%b9%d1%88%d0%b5%d0%b9-wcf-%d1%81%d0%bb%d1%83%d0%b6%d0%b1%d1%8b-%d1%87%d0%b0%d1%81%d1%82%d1%8c-1/ https://pelevin.pro/2014/12/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%bf%d1%80%d0%be%d1%81%d1%82%d0%b5%d0%b9%d1%88%d0%b5%d0%b9-wcf-%d1%81%d0%bb%d1%83%d0%b6%d0%b1%d1%8b-%d1%87%d0%b0%d1%81%d1%82%d1%8c-1/#respond Tue, 02 Dec 2014 18:04:33 +0000 http://pelevin.pro/?p=1880

Примечание автора: эта статья была написана в районе 2008 года, и, скорее всего, она уже морально устарела. Однако, судя по отзывам, все еще кому-то полезна. Статья разделена на несколько частей.

Часть 1, Часть 2, Часть 3

Введение

Эта статья для тех, кого заинтересовала технология WCF, и те преимущества, которые она дает разработчику распределенных и сервис-ориентированных приложений.

Сегодня я расскажу о том, как создать простейшую WCF-службу. Для примера будем использовать классический вариант — калькулятор.

Создание службы

Для создания нашего приложения мы будем использовать Visual Studio 2008. Итак, вызовем диалоговое окно создания нового проекта и выбираем шаблон проекта под названием «WCF Service Library» и дадим ему имя «WcfCalcService».

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

1. App.config — файл конфигурации приложения. В этом файле также находятся настройки службы;

2. IService1.cs — файл с интерфейсом (контрактом) службы;

3. Service1.cs — файл с классом, реализующим интерфейс (т.е. функционал службы).

Контракт службы

Изначально VS открывает перед нами файл IService1.cs. Это вполне оправдано, ведь разработка любой службы должна начинаться с определения того, что же она все-таки будет делать. Поменяем имя интерфейса на ICalcService (а также переименуем файл в ICalcService.cs). Заметьте, что перед объявлением интерфейса стоит атрибут ServiceContract. Это-то как раз и означает, что данный интерфейс является контрактом WCF-службы.

Примечание:

В комментарии перед кодом интерфейса написано:

// NOTE: If you change the interface name “IService1″ here, you must also update the reference to “IService1″ in App.config.

Что означает: «Если вы измените имя интерфейса «IService1» здесь, вам также нужно будет обновить ссылку на «IService1» в файле App.config». Мы это сделаем чуть позже, а этот комментарий можно удалить.

Примечание:

Не забывайте пользоваться рефакторингом, чтобы везде, где в коде упоминается IService1, его имя заменилось на ICalcService.

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


OperationContract

Примечание

Ниже описания интерфейса находится описание типаCompositeType, который VS создает для примера. Его можно удалить.

Итак, теперь файл ICalcService.cs выглядит следующим образом:


ServiceContract

Вы, наверное, уже заметили, что мы нигде не описали типыCalcOperationResult и CalcOperationType. Этим мы сейчас и займемся — эти типы будут использоваться для обмена данными между клиентом и службой.

Примечание

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

Вообще, я рекомендую все, используемые для обмена типы, выделять в отдельную сборку, особенно, если вам нужен еще и какой-то общий функционал этих типов. Существует еще один вариант — описать интерфейсы типов, а реализацию сделать различной на клиенте и сервере. Такой вариант хорош при создании более-менее больших и сложных систем. Есть и третья возможность. Студия, при создании ссылки на сервис (Service Reference, это мы рассмотрим подробнее в статье про создание клиентских приложений), автоматически скачивает метаданные (описание структуры и функционала сервиса в специальном формате — WSDL) и по ним генерирует все контрактные типы, но у этого подхода есть два минуса. Первый — у нас нет доступа к методам сгенерированного типа (VS их просто не создает). Второй — для автоматического создания ссылки на службу нужна работающая служба с открытыми метаданными. Это встречается очень редко, если вы хотите создать клиентское приложение для существующей чужой службы. Поэтому, думаю, моя точка зрения по этому вопросу вполне ясна.

CreateProject

Создание проекта с общими типами

Мы воспользуемся первым вариантом: создадим еще одну сборку, добавив в решение новый проект с шаблоном «ClassLibrary» и дав ему имя «CalcCommon».


Добавление ссылки на System.Runtime.Serialization

Добавление ссылки на System.Runtime.Serialization

Чтобы в нашей общей сборке были доступны атрибуты для контрактов, нужно добавить ссылку на сборку System.Runtime.Serialization (правой кнопкой по проекту — Add reference).

Теперь удалим из нового проекта все лишнее, создадим новый файл класса (для этого можно нажать сочетание клавиш Alt+Shift+C) и назовем его CalcOperationResult.cs. Так выглядит описание типа CalcOperationResult:

DataContract

Атрибут DataContract говорит о том, что данный тип используется для описания контракта данных службы и будетсериализуемым специальным диспетчером сериализации (System.Runtime.Serialization.DataContractSerializer). Соответственно DataMember говорит, что данный член класса относится к описанию контракта данных и будет сериализован тем же DataContractSerializer’ом.

Создадим еще один файл, где опишем перечисление CalcOperationType:

EnumDataContract

Как видите, для описания перечисления так же используется атрибут DataContract. А вот для членов перечисления нужен специальный атрибут EnumMember. В противном случае все значения типа перечисления будут передаваться в виде строки.

Примечание

Не забывайте в список используемых модулей добавлять строку:
using System.Runtime.Serialization;

AddReference

Добавление ссылки на общую сборку

Отлично! Осталось подключить к проекту службы сборку CalcCommon (правой кнопкой по проекту — Add Reference — закладка Projects) и наш интерфейс полностью готов (не забываем про using CalcCommon).


Продолжение следует — во второй части статьи.

]]>
https://pelevin.pro/2014/12/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%bf%d1%80%d0%be%d1%81%d1%82%d0%b5%d0%b9%d1%88%d0%b5%d0%b9-wcf-%d1%81%d0%bb%d1%83%d0%b6%d0%b1%d1%8b-%d1%87%d0%b0%d1%81%d1%82%d1%8c-1/feed/ 0 1880
А как Вам метод, тело которого начинается с return? https://pelevin.pro/2014/07/%d0%b0-%d0%ba%d0%b0%d0%ba-%d0%b2%d0%b0%d0%bc-%d0%bc%d0%b5%d1%82%d0%be%d0%b4-%d1%82%d0%b5%d0%bb%d0%be-%d0%ba%d0%be%d1%82%d0%be%d1%80%d0%be%d0%b3%d0%be-%d0%bd%d0%b0%d1%87%d0%b8%d0%bd%d0%b0%d0%b5%d1%82/ https://pelevin.pro/2014/07/%d0%b0-%d0%ba%d0%b0%d0%ba-%d0%b2%d0%b0%d0%bc-%d0%bc%d0%b5%d1%82%d0%be%d0%b4-%d1%82%d0%b5%d0%bb%d0%be-%d0%ba%d0%be%d1%82%d0%be%d1%80%d0%be%d0%b3%d0%be-%d0%bd%d0%b0%d1%87%d0%b8%d0%bd%d0%b0%d0%b5%d1%82/#respond Mon, 07 Jul 2014 15:03:38 +0000 http://pelevin.pro/?p=1377 Друг похвастал какие чудеса он пишет в коде, на работе. Хороший пункт в тему того, почему у разработчика нельзя оставлять наедине с кодом, ему нужен хотя бы еще один в команду, чтобы попинывал и мотивировал.

Готов поиграть в игру: Кто сможет дать максимально подробное описание или комментарии к данному коду или переписать его максимально просто для человеческого прочтения? И сколько на это понадобится времени?

private List<BlankComposition> GetBlankInfo(SKDREntities db, Requisition inz, int deliveryType, long? languageId)
        {
            return db.RuleDescriptions
                .Where(rd => rd.OrganizationId == inz.ReqUnit && rd.IsActive)
                .ToList()
                .Where(rd => rd.DeliveryTypes.Split(',').Select(int.Parse).Contains(deliveryType))
                .DefaultIfEmpty()
                .SelectMany(or => db.RuleDescriptions
                    .Where(rd => rd.OrganizationId == inz.RequisitionOrg.OrgID && rd.IsActive)
                    .ToList()
                    .Where(rd => rd.DeliveryTypes.Split(',').Select(int.Parse).Contains(deliveryType)).DefaultIfEmpty(), (org, lab) => new { org, lab })
                .Select(rule => new[] { rule.org, rule.lab }.Where(r => r != null).ToList())
                .Select(rule => new
                    {
                        HeadHeight = rule.Select(r => r.Composition.HeaderHeight).FirstOrDefault(hh => hh != null) ?? "3cm",
                        FooterHeight = rule.Select(r => r.Composition.FooterHeight).FirstOrDefault(fh => fh != null) ?? "4.5cm",
                        DemographyDescriptionId = rule.Select(r => r.DemographyDescriptionId).FirstOrDefault(),
                        Suffix = rule.Select(r => r.Suffix).FirstOrDefault(),
                        Elements = rule
                                    .SelectMany(
                                        re => re.Composition.CompositionElements,
                                        (rd, element) => new { element, priority = rd == rule.First(), langPriority = element.LanguageId == languageId }
                                    )
                                    .GroupBy(g => g.element.ElementTypeId)
                                    .SelectMany(g => g
                                        .OrderByDescending(e => e.priority)
                                        .ThenByDescending(e => e.langPriority)
                                        .Take(1)
                                        .Select(e => e.element)
                                    )
                                    .Where(e => e.ElementId != null)
                                    .ToList()
                    }
                ).Select(item => new BlankComposition
                    {
                        HeaderHeight = item.HeadHeight,
                        FooterHeight = item.FooterHeight,
                        BlankElements = item.Elements.Select(e => new BlankElement
                        {
                            Width = e.Element.Width,
                            Height = e.Element.Height,
                            Left = e.Element.Left,
                            Top = e.Element.Top,
                            Image = e.Element.ImageId == null ? null : e.Element.Image.Data,
                            Text = e.Element.Text,
                            Placing = e.Placing
                        }).ToList(),
                        Demography = db.DemographyDescriptions
                            .Single(dd => dd.Id == item.DemographyDescriptionId)
                            .DemographyTables
                            .Select(dt => new DemographyInfo
                            {
                                Placing = dt.Placing,
                                Height = dt.Height,
                                Left = dt.Left,
                                Top = dt.Top,
                                Width = dt.Width,
                                Lines = DemographyFormatter.GetDemographyInfo(inz, dt.DemographyType.DemographyContents.OrderBy(o => o.SortOrder).ToList())
                            })
                            .ToList(),
                        Suffix = item.Suffix,
                        AddHyperlinks = item.Elements.Any(hy => hy.ElementTypeId == 6 && hy.ElementId != null)
                    }
                ).ToList();
        }
]]>
https://pelevin.pro/2014/07/%d0%b0-%d0%ba%d0%b0%d0%ba-%d0%b2%d0%b0%d0%bc-%d0%bc%d0%b5%d1%82%d0%be%d0%b4-%d1%82%d0%b5%d0%bb%d0%be-%d0%ba%d0%be%d1%82%d0%be%d1%80%d0%be%d0%b3%d0%be-%d0%bd%d0%b0%d1%87%d0%b8%d0%bd%d0%b0%d0%b5%d1%82/feed/ 0 1377
Windows Phone и тренировка мозга https://pelevin.pro/2013/01/windows-phone-%d0%b8-%d1%82%d1%80%d0%b5%d0%bd%d0%b8%d1%80%d0%be%d0%b2%d0%ba%d0%b0-%d0%bc%d0%be%d0%b7%d0%b3%d0%b0/ https://pelevin.pro/2013/01/windows-phone-%d0%b8-%d1%82%d1%80%d0%b5%d0%bd%d0%b8%d1%80%d0%be%d0%b2%d0%ba%d0%b0-%d0%bc%d0%be%d0%b7%d0%b3%d0%b0/#respond Tue, 22 Jan 2013 20:51:54 +0000 http://pelevin.pro/?p=608 Готова демоверсия приложения для тренировки памяти, фантазии и воображения.

Приложение готово для Windows Phone, в ближайшие дни я покажу его автору метода. Приложение во многом опирается на системоцелостную психологию, являющуюся фундаментом «метода Бронникова».

Надеюсь в скором времени после демонстрации приложение появится в Marketplace.

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

]]>
https://pelevin.pro/2013/01/windows-phone-%d0%b8-%d1%82%d1%80%d0%b5%d0%bd%d0%b8%d1%80%d0%be%d0%b2%d0%ba%d0%b0-%d0%bc%d0%be%d0%b7%d0%b3%d0%b0/feed/ 0 608
Магия дат в CRM 2011, DateTime, UTC https://pelevin.pro/2012/05/%d0%bc%d0%b0%d0%b3%d0%b8%d1%8f-%d0%b4%d0%b0%d1%82-%d0%b2-crm-2011-datetime-utc/ https://pelevin.pro/2012/05/%d0%bc%d0%b0%d0%b3%d0%b8%d1%8f-%d0%b4%d0%b0%d1%82-%d0%b2-crm-2011-datetime-utc/#comments Thu, 17 May 2012 04:18:04 +0000 http://pelevin.pro/?p=383 Поначалу я нигде не встретил такой ремарки и не обращал на этот факт внимания, пока время тестирования решения не совпало с ранним утром.

Суть заключается в том, что, выбирая на форме в поле типа Дата и Время, например, 1 мая 2012, Вы рассчитываете, что в БД так и запишется: 1 мая 2012. Но все не так просто.

CRM хранит Дату и Время в UTC, таким образом, выходит, что, если Ваш сервер находится где-то за Уралом, то время в БД будет отличаться уже более чем на 6 часов с тем, что Вы указали. Так, у меня, к примеру, получалось, что если установить дату 1 мая, то в автосгенерированном через плагин договоре ставилось 30 апреля.

В моем случае разница между DateTime, которое я ожидал получить и реальным составила 6 часов, это как раз часовой пояс Екатеринбурга.

Если бы я сразу был внимателен, то MSDN прямым текстом говорит «Specifies the attribute value in UTC format.». Сам CRM так и будет показывать в веб-форме ту дату, что Вы выбрали, но если Вы работаете с датами через плагин, то не забывайте возвращать дату из UTC в ваш часовой пояс.

]]>
https://pelevin.pro/2012/05/%d0%bc%d0%b0%d0%b3%d0%b8%d1%8f-%d0%b4%d0%b0%d1%82-%d0%b2-crm-2011-datetime-utc/feed/ 1 383
ReadLine для Binary Stream https://pelevin.pro/2012/05/readline-%d0%b4%d0%bb%d1%8f-binary-stream/ https://pelevin.pro/2012/05/readline-%d0%b4%d0%bb%d1%8f-binary-stream/#respond Thu, 10 May 2012 14:35:00 +0000 http://pelevin.pro/?p=358 При чтении данных из бинарного потока, например, из NetworkStream или FileStream, может потребоваться читать кусочки данных, а так же читать одну строку текста за раз. BinaryReader и Stream не поддерживают команды ReadLine. Вы можете использовать StreamReader, чтобы использовать ReadLine, но это не позволяет Вам читать кусочками. Read(byte[], int, int) не доступно в StreamReader.

Ниже предложено расширение для BinaryReader, позволяющие использовать ReadLine для бинарной строки. При необходимости оно позволяет читать кусочки данных так же, как текст — строками.

 

public class LineReader : BinaryReader
{
  private Encoding _encoding;
  private Decoder _decoder;   const int bufferSize = 1024;
  private char[] _LineBuffer = new char[bufferSize];
 public LineReader(Stream stream, int bufferSize, Encoding encoding)
    : base(stream, encoding)
  {
    this._encoding = encoding;
    this._decoder = encoding.GetDecoder();
  }
 public string ReadLine()
  {
    int pos = 0;   char[] buf = new char[2];   StringBuilder stringBuffer = null;
    bool lineEndFound = false;   while(base.Read(buf, 0, 2) > 0)
    {
      if (buf[1] == 'r')
      {
        // grab buf[0]
        this._LineBuffer[pos++] = buf[0];
        // get the 'n'
        char ch = base.ReadChar();
        Debug.Assert(ch == 'n');   lineEndFound = true;
      }
      else if (buf[0] == 'r')
      {
        lineEndFound = true;
      }
      else
      {
        this._LineBuffer[pos] = buf[0];
        this._LineBuffer[pos+1] = buf[1];
        pos += 2;   if (pos >= bufferSize)
        {
          stringBuffer = new StringBuilder(bufferSize + 80);
          stringBuffer.Append(this._LineBuffer, 0, bufferSize);
          pos = 0;
        }
      }

      if (lineEndFound)
      {
        if (stringBuffer == null)
        {
          if (pos > 0)
            return new string(this._LineBuffer, 0, pos);
          else
            return string.Empty;
        }
        else
        {
          if (pos > 0)
            stringBuffer.Append(this._LineBuffer, 0, pos);
          return stringBuffer.ToString();
        }
      }
    }   if (stringBuffer != null)
    {
      if (pos > 0)
        stringBuffer.Append(this._LineBuffer, 0, pos);
      return stringBuffer.ToString();
    }
    else
    {
      if (pos > 0)
        return new string(this._LineBuffer, 0, pos);
      else
        return null;
    }
  }
}

© Omar Zabib, Перевод 
]]>
https://pelevin.pro/2012/05/readline-%d0%b4%d0%bb%d1%8f-binary-stream/feed/ 0 358