Показаны сообщения с ярлыком c#. Показать все сообщения
Показаны сообщения с ярлыком c#. Показать все сообщения

среда, 22 июня 2016 г.

Получение значения вложенного XElement-а.

[Getting the value of a nested XElement]

Допустим у нас есть следующий документ:

XDocument xDoc = XDocument.Parse(@"<Data> <X> <Y> <Z>Здесь!</Z> </Y> </X> </Data>");

Получение значения XElementa  „Z“ достаточно тривиальная задача:

String value = xDoc.Root.Element("X").Element("Y").Element("Z").Value;

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

Что бы получить значение «в один вызов», можно воспользоваться методами расширениями:

public static String xValueOrNull (this XElement xEl) {
if (xEl == null) return null;
else return xEl.Value;
}
public static XElement xElementOrNull(this XElement root, String elemName) {
if (root == null) return null;
return root.Element(elemName);
}  
Тогда вызов будет примерно таким:

String val = xDoc.Root.xElementOrNull("X").xElementOrNull("Y").xElementOrNull("Z").xValueOrNull();

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

public static String xFuncValueOrNull(this XElement xEl, String elemName, Func<XElement, String> func) {
if (xEl == null) return null;
XElement subEl = xEl.Element(elemName);
if (subEl == null) return null;
return func(subEl);
}

И вызов:

String val = xDoc.Root.xFuncValueOrNull("X", x => x.xFuncValueOrNull("Y", y => y.xFuncValueOrNull("Z", z => x.Value)));

вторник, 16 февраля 2016 г.

Использование XING API из c# (.NET)

[Using the XING API in c # (.NET)]

Далее представлен пример работы с XING-API из десктопного приложения (WinForms).
Документация по XING-API:https://dev.xing.com/docs/resources.

Итак, для начала использования API необходимо прежде всего получить ключ и секретное слово на следующей страничке: https://dev.xing.com/applications.

Использование API предполагает придерживания специального протокола OAuth (https://dev.xing.com/docs/authentication). Как это делается можно увидеть в коде. Особенностью десктопного приложения является то, что верификация пользователя выполняется “вручную” - пользователь должен передать в приложение специальный код, который отображается после его авторизации в браузере. В веб-приложении этот код можно вычитать из возвращаемых параметров запроса программно.

Ресурсы, которые я использовал:

  • .Net SDK for XING API

https://netforxing.codeplex.com/ (странная реализация, но подсмотрел как использовать DotNetOpenAuth)

  • Wie man die XING-API nutzt (на немецком, описан процесс использования OAuth в XING)

http://www.heise.de/ix/artikel/Sozial-integriert-1892413.html

  • API Explorer für die XING REST-API (программа для тестирования API)

https://www.cybertribe.de/druid/de/ApiExplorer


В приложении понадобятся следующие пакеты библиотек:

DotNetOpenAuth OAuth 1.0(a) Consumer:


и RestSharp:

Диалог ввода использовал из сборки "Microsoft.VisualBasic".

Интерфейс приложения:

По нажатию на кнопку "Login" (button1_Click) выводится URL, который необходимо ввести в браузере для авторизации пользователя и получения кода верификации. Этот код необходимо ввести в последующем диалоге ввода. После логина можно вызывать методы API, например получение информации о пользователе - кнопка "Get Data" (button2_Click).


Итак, код:

public partial class Form1 : Form
{
    public Form1() {
        InitializeComponent();
    }

    String consumerKey = "<your key>";
    String consumerSecret = "<your secret>";

    String access_token = null;
    String access_token_secret = null;

    String verifyPIN = null;

    private void button1_Click(object sender, EventArgs e) {
           
        A();
        B();

        verifyPIN = Microsoft.VisualBasic.Interaction.InputBox("PIN: ",
                    "Input Data",
                    "", 0, 0);

        C();
    }

    private void button2_Click(object sender, EventArgs e) {

        String userID = Microsoft.VisualBasic.Interaction.InputBox("User ID",
                    "Input Data",
                    "me", 0, 0);

        D(userID);
    }

    // -----------------
    // -----------------

    // Get Authorization Token
    void A() {
        RestClient client = new RestClient("https://api.xing.com");

        client.Authenticator = OAuth1Authenticator.ForRequestToken(
                                                        consumerKey,
                                                        consumerSecret,
                                                        "oob");

        RestRequest request = new RestRequest("v1/request_token", Method.POST);
        RestResponse response = (RestResponse)client.Execute(request);

        var qs = HttpUtility.ParseQueryString(response.Content);
        access_token = qs["oauth_token"];
        access_token_secret = qs["oauth_token_secret"];
    }

    // Use Token to get the verifier.
    void B() {
        RestClient client = new RestClient("https://api.xing.com");

        RestRequest request = new RestRequest("/v1/authorize");
        request.AddParameter("oauth_token", access_token);
        var url = client.BuildUri(request).ToString();

        // use this url to get PIN Code
        MessageBox.Show(url);
    }

    // Use Access Token and Verifier to get the last Token needed
    void C() {
        RestClient client = new RestClient("https://api.xing.com");

        client.Authenticator = OAuth1Authenticator.ForAccessToken(
                                                        consumerKey,
                                                        consumerSecret,
                                                        access_token,
                                                        access_token_secret,
                                                        verifyPIN);

        RestRequest request = new RestRequest("v1/access_token", Method.POST);
        RestResponse response = (RestResponse)client.Execute(request);

        // Change tokens to the correct Access Tokens
        // now we have all Tokens in our TokenManager
        var qs = HttpUtility.ParseQueryString(response.Content);
        access_token = qs["oauth_token"];
        access_token_secret = qs["oauth_token_secret"];
    }

    // Get Data
    void D(String userID) {
        RestClient client = new RestClient("https://api.xing.com");

        client.Authenticator = OAuth1Authenticator.ForProtectedResource(
                                                        consumerKey,
                                                        consumerSecret,
                                                        access_token,
                                                        access_token_secret);

        RestRequest request = new RestRequest("/v1/users/" + userID);
        RestResponse response = (RestResponse)client.Execute(request);


        //Verify Result
        //Assert.NotNull(response);
        //Assert.Equal(HttpStatusCode.OK, response.StatusCode);

        MessageBox.Show(response.Content);
    }
}



воскресенье, 25 августа 2013 г.

Linq. Изменение порядка сортировки OrderBy без IComparer.

[Linq. Change the sort order by using OrderBy without IComparer]

Метод-расширение OrderBy предоставляет собой отличное и эффективное средство для упорядочивания коллекций, например, по строковому ключу. Можно использовать версию метода без компаратора, тогда используется его стандартная общая реализация. Все же, для строк рекомендуется использовать специальный строковой компаратор StringComparer, с помощью которого можно учитывать регистр, язык и региональные параметры. Но даже его в некоторых, казалось бы, элементарных случаях может не хватать. Например тогда, когда среди ключей есть нулевые значения, эти элементы занимают самые первые позиции. А мне нужно было их откинуть назад. Что делать? Казалось бы, пришло время браться за реализацию своего компаратора! Но, оказывается, можно обойтись без нее, нужно лишь правильно выбирать ключ, т.е. "подменять" его нулевые значения значением "последнего ряда". У меня это "xxxxxxxxxx".

Итак, для наглядности на примере:

using System;
using System.Collections.Generic;
using System.Linq;

namespace BlogApp
{
    class Program
    {
        static void Main(string[] args) {

            List<MyData> myDataList = new List<MyData>();
            myDataList.Add(new MyData() { Val1 = "D", Val2 = "1" });
            myDataList.Add(new MyData() { Val1 = "B", Val2 = "2" });
            myDataList.Add(new MyData() { Val1 = "A", Val2 = "3" });
            myDataList.Add(new MyData() { Val1 = "D", Val2 = "4" });
            myDataList.Add(new MyData() { Val1 = null, Val2 = "5" });
            myDataList.Add(new MyData() { Val1 = null, Val2 = "6" });
            myDataList.Add(new MyData() { Val1 = "C", Val2 = "7" });
            myDataList.Add(new MyData() { Val1 = "B", Val2 = "8" });

            var res1 = myDataList.OrderBy(x => x.Val1);
            var res2 = myDataList.OrderBy(x => x.Val1 == null ? "xxxxxxxxxx" : x.Val1); // !!!!!!!

            Console.WriteLine("Result 1:");
            Console.WriteLine(String.Join(Environment.NewLine, res1.Select(x => String.Format("{0}:{1}", x.Val2, x.Val1))));
            Console.WriteLine();
            Console.WriteLine("Result 2:");
            Console.WriteLine(String.Join(Environment.NewLine, res2.Select(x => String.Format("{0}:{1}", x.Val2, x.Val1))));
            Console.ReadLine();
        }
    }

    public class MyData
    {
        public String Val1 { get; set; }
        public String Val2 { get; set; }
    }
}

Результат:

воскресенье, 24 июня 2012 г.

Генерация Assembly Version при помощи T4 шаблонов

[Generation of Assembly Version using the T4 templates]

Assembly Version используется .NET фреймворком во время построения и во время исполнения приложения для определения загружаемой сборки. Другими словами, Assembly Version является важной частью имени сборки, ее однозначно определяющей.

Assembly File Version является номером версии файла (отображается в Windows Explorer) и НЕ ИСПОЛЬЗУЕТСЯ .NET-ран-таймом для локализации сборки.

Эти версии указываются в атрибутах сборки и как правило находятся в файле AssemblyInfo.cs:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]


Если сборки создаются очень часто, ручное изменение значений указанных атрибутов становится накладным. Для автоматического увеличения номера построения (Build Number) и ревизии (Revision) можно использовать символ звездочки (*):
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("1.0.0.*")]


Компилятор сгенерирует номера самостоятельно, и этого в большинстве случаев будет достаточно. Но выдаваемые номера трудно поддаются интерпретации , и если хочется наделить числа большим смыслом, например, привязать к дате/времени, придется делать это при помощи сторонних средств. Для этих целей есть множество утилит и надстроек к Visual Studio в виде AddIn-ов, я же воспользуюсь встроенными средствами Visual Studio - генерацей кода по текстовым шаблонам: Text Template Transformation Toolkit, или коротко T4. При помощи этого инструмента можно генерировать заветные строки самостоятельно, наделив нумерацию необходимой логикой. У меня в примере нумерация привязана к году, месяцу, числу, и часу, когда создается сборка. Добавьте новый файл шаблона в проект, и назовите его, например, “AssemblyVersion.tt”:

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
// Assembly Version
// Generated at <#= DateTime.Now #>
//
// [major version].[minor version].[build number].[revision number]
// build number = yy MM = Year Month (e.g.: 1102 = 2011 February)
// revision number = ddHH = Day Hour (e.g.: 1411 = 14 "February" at 11 )
//
// to generate for every build in BuildEvents:
// "%CommonProgramFiles%\Microsoft Shared\TextTemplating\10.0\TextTransform.exe" "$(ProjectDir)AssemblyVersion.tt" -out "$(ProjectDir)AssemblyVersion.cs"

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

[assembly: AssemblyVersion("1.0.<#= DateTime.Now.ToString("yyMM") #>.<#= DateTime.Now.ToString("ddHH") #>")]
[assembly: AssemblyFileVersion("1.0.<#= DateTime.Now.ToString("yyMM") #>.<#= DateTime.Now.ToString("ddHH") #>")]


Генерация кода по файлам шаблонов (*.tt) происходит автоматически при сохранении изменений в шаблоне или при явном вызове соответствующей команды (например из контекстного меню). Также можно попросить Visual Studio обработать все шалоны проекта. Но нам нужно добиться генерации при каждом построении (build) проекта. Для этого можно воспользоваться этим (http://msdn.microsoft.com/ru-ru/library/ee847423.aspx) руководством от MS "Создание кода в процессе построения".  Я же вызываю генератор шаблонов из командной строки в Prebuild-событии:

"%CommonProgramFiles%\Microsoft Shared\TextTemplating\10.0\TextTransform.exe" "$(ProjectDir)AssemblyVersion.tt" -out "$(ProjectDir)AssemblyVersion.cs"


Не забудьте удалить или закомментировать ненужные уже строки в файле AssemblyInfo.cs!


Замечания:
- Не уверен, но кажется, что генерация по шаблонам T4 не поддерживаются в Visual Studio Express - версии.
- Вам не обойтись от изменения версии файла (AssemblyFileVersion) если вы в проекте установки (Setup Project) используете параметр "RemovePreviousVersion=true". Файлы обновляются, только если имеют большую версию, чем уже установленые.
- Для проекта типа Class Library (Visual Basic) в Visual Studio 2010 автоматическая генерация версии при помощи знака (*) не срабатывает. Это известная проблема описана здесь (https://connect.microsoft.com/VisualStudio/feedback/details/588047), поэтому для автоматической генерации вам, наверное, придется использовать "внешние" средства, такие как T4.
- Если необходимо, чтобы все сборки проекта имели одинаковый номер версии, можно воспользоваться следующим способом:
    -- Удалите определяющие версию атрибуты во всех проектах (файлы AssemblyInfo).
    -- В одном из проектов создайте файл с атрибутами версии (AssemblyVersion, AssemblyFileVersion). Можно воспользоваться описанной в статье кодо-генерацией.
    -- Добавьте этот файл к каждому из проектов, но КАК ССЫЛКУ (as link).

------------------------------------------

Ресурсы:
How to use Assembly Version and Assembly File Version
http://support.microsoft.com/kb/556041/en-us

Создание кода и текстовые шаблоны T4
http://msdn.microsoft.com/ru-ru/library/bb126445

Official T4 team blog - официальный блог команды разработчиков t4
http://blogs.msdn.com/b/t4/
И их официальный блог примеров
http://t4talk.codeplex.com/

воскресенье, 19 февраля 2012 г.

Отменяемая асинхронная операция при помощи Task Parallel Library .NET 4

Недавно в одной WinForms программе мне понадобилось воспользоваться асинхронными операции, чтобы не блокировать графический интерфейс во время длительных расчетов. Это можно реализовать, конечно же, очень многими способами, от “ручного” программирования потоков (Threads) до использования таких конструкций как BackgroundWorker. Но мне захотелось (наконец-то!) попробовать новые библиотеки для параллельного программирования появившиеся в .NET 4 версии.

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

Вот что у меня получилось:


    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;
    using System.Threading.Tasks;

    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }

            private void startButton_Click(object sender, EventArgs e)
            {
                StopCalcIfRun();
                BeginCalc();
            }

            private void stopButton_Click(object sender, EventArgs e)
            {
                StopCalcIfRun();
            }

            // --------------------------------------

            class CalcResult
            {
                public int IntResult;
            }

            // объект задачи одновременно является флагом, нулевое значение которого указывает на то, запущена ли операция
            volatile Task calcTask = null;

            void StopCalcIfRun()
            {
                Task task = calcTask;

                if (task != null)
                {
                    // извлекаем CancellationTokenSource для отмены операции
                    CancellationTokenSource ts = (CancellationTokenSource)task.AsyncState;
                    ts.Cancel();

                    try
                    {
                        task.Wait();
                    }
                    catch (AggregateException e)
                    {
                        // Если e.InnerExceptions содержит исключение TaskCanceledException,
                        // то это указывает на "успешную" отмену задачи, а не на сбой
                    }
                }
            }

            void BeginCalc()
            {
                TaskScheduler uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();

                CancellationTokenSource tockenSource = new CancellationTokenSource();
                CancellationToken tocken = tockenSource.Token;

                // Создаем и запускаем длительную операцию.
                // Вниманине, tockenSource "закрепляется" за задачей как State Object, и используется для ее отмены в StopCalcIfRun методе
                calcTask = Task.Factory.StartNew((xxx) =>
                {
                    try
                    {
                        // уже отменили задачу?
                        tocken.ThrowIfCancellationRequested(); // *** Отмена задачи ***
                        CalcResult result = new CalcResult();

                        // длительные вычисления
                        result.IntResult++;
                        System.Threading.Thread.Sleep(1000);
                     
                        if (tocken.IsCancellationRequested) { // *** Отмена задачи ***
                            // ... освобождаем ресурсы
                            tocken.ThrowIfCancellationRequested();
                        }

                        // продолжаем длительные вычисления
                        result.IntResult++;
                        System.Threading.Thread.Sleep(1000);
                     
                        if (tocken.IsCancellationRequested) { // *** Отмена задачи ***
                            // ... освобождаем ресурсы
                            tocken.ThrowIfCancellationRequested();
                        }
                     
                        return result;
                    }
                    finally
                    {
                        calcTask = null;
                    }
                }, tockenSource, tocken);

                // Это продолжение будет вызвано в случае возникновения исключения (см. TaskContinuationOptions).
                // Исключение TaskCanceledException отменяющее задачу является корректным завершением задачи
                // и к таким исключениям не относится!
                calcTask.ContinueWith((ant) =>
                {
                     MessageBox.Show(ant.Exception.InnerException.ToString());
                }
                    , System.Threading.CancellationToken.None
                    , TaskContinuationOptions.OnlyOnFaulted
                    , uiTaskScheduler);

                // Продолжение если задача отменяется.
                // ant.Result есть null, не стоит к нему обращатся
                calcTask.ContinueWith((ant) =>
                {
                    resultTextBox.Text += "Canceled;";
                }
                    , System.Threading.CancellationToken.None
                    , TaskContinuationOptions.OnlyOnCanceled
                    , uiTaskScheduler);

                // Полное завершение задачи => отображаем результаты
                calcTask.ContinueWith((ant) =>
                {
                    resultTextBox.Text += ant.Result.IntResult + ";";
                }
                    , System.Threading.CancellationToken.None
                    , TaskContinuationOptions.OnlyOnRanToCompletion
                    , uiTaskScheduler);
            }
        }
    }


Ресурсы:

Threading in C#, Joseph Albahari
http://www.albahari.com/threading/part5.aspx

Перевод части статьи Джозефа Албахари (Joseph Albahari) от Sergey Teplyakov
http://sergeyteplyakov.blogspot.com/2010/09/52.html

Все дело в SynchronizationContext [RU. MSDN Magazine]
http://msdn.microsoft.com/ru-ru/magazine/gg598924.aspx

Отмена задач [RU. MSDN Статья]
http://msdn.microsoft.com/ru-ru/library/dd997396.aspx