суббота, 1 февраля 2014 г.

jQuery Deferreds: Использование Deferred объектов в jQuery 1.5

Взято отсюда

http://habrahabr.ru/post/112960/

1 февраля 2011 в 15:36

Использование Deferred объектов в jQuery 1.5 перевод

Deferred объекты появились в jQuery 1.5. Они позволяют отделить логику, которая зависит от результатов выполнения действия от самого действия. Для JavaScript Deferred объекты не новы, они уже были в MochiKit и Dojo, но с изменениями логики jQuery ajax от Julian Aubourg, внедрение Deferred объектов было неминуемо. С Deferred объектами несколько callback могут быть связаны с результатом задачи и любые из них могут быть привязаны к действию даже после начала его выполнения. Выполняемая задача может быть асинхронна, но не обязательно.

Deferred объекты теперь встроены в $.ajax() таким образом вы будете получать их автоматически. Обработчики теперь могут быть связаны с результатом следующим образом:
// $.get, ajax запрос, он асинхронный по умолчанию
var req = $.get('foo.htm')
   .success(function( response ){
      // что-нибудь делаем с ответом
   })
   .error(function(){
      // делаем что-нибудь если запрос провалился
   });
 
// это выполнится перед тем как $.get() будет выполнено
doSomethingAwesome();
 
// Делаем что-то ещё перед завершением запроса
req.success(function( response ){
   // делаем  что-то ещё с ответом
   // он будет выполнен когда запрос завершится, а если запрос завершен, то будет вызван немедленно
   // если запрос уже был выполнен
});


Мы теперь не ограничены одним обработчиком success, error или complete, теперь у нас есть самоорганизуемые очереди обратных вызовов(FIFO), взамен простых функций обратного вызова.

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

Копнем глубже, представьте себе ситуацию, в которой мы хотим вызвать функцию после нескольких одновременных запросов AJAX. Это легко сделать с хэлпером отложенных объектов — $.when():
function doAjax(){
   return $.get('foo.htm');
}
 
function doMoreAjax(){
   return $.get('bar.htm');
}
 
$.when( doAjax(), doMoreAjax() )
   .then(function(){
      console.log( 'I fire once BOTH ajax requests have completed!' );
   })
   .fail(function(){
      console.log( 'I fire if one or more requests failed.' );
   });

Пример на jsFiddle jsfiddle.net/ehynds/Mrqf8/

Это все работает потому что сейчас все методы jQuery AJAX возвращают объект, содержащий Promise объект, который используется для отслеживания асинхронных вызовов. Promise это доступный на чтение наблюдатель за результатом действия. Deferred объекты смотрят на наличие метода promise() для определения возможно ли наблюдать за объектом или нет (object is observable or not). $.when() ждет когда все AJAX запросы будут выполнены, когда это произойдет функции обратного вызова, прицепленные к $.when() через .then() и .fail(), будут вызваны в зависимости от того с каким результатом завершилась задача. Функции обратного вызова вызываются в порядке поступления(fifo очередь).

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

$.ajax() возвращает не стандартный объект, содержащий другой Deferred-подобный объект. Я уже описал promise(), но вы также нашли then(), success(), error() и множество других. У вас нет доступа ко всему Ajax-Deferred объекту. Только promise(), методы обратного вызова, а так же методы isRejected() и isResolved(), которые могут быть использованы для проверки статуса Deferred объекта.

Но почему бы не вернуть весь объект? Если это было так, то можно было бы программно завершить Ajax-Deferred объект, не дожидаясь Ajax ответа. Это потенциально нарушает всю парадигму.

Регистрация Callback'ов


В примерах, показанных выше, методы then(), success() и fail() используются для регистрации функций обратного вызова в Deferred объектах, но вам доступно больше методов, особенно когда вы работаете с Ajax-Deferred.

Методы доступны всем Deferred объектам(AJAX, $.when и те, которые создаются руками):
.then( doneCallbacks, failedCallbacks )
.done( doneCallbacks )
.fail( failCallbacks )

Ajax-Deferred объект имеет 3 дополнительных метода, которые просто дублируют описанные выше. Они предоставляют из себя семантические альтернативы и схожи по именам со старыми обработчиками, к которым мы все привыкли:
.complete( doneCallbacks, failCallbacks )
.success( doneCallbacks )
.error( failCallbacks )

Таким образом, следующие три примера эквивалентны (success читается лучше, чем если бы он был в контексте запроса AJAX, не правда ли?)
$.get("/foo/").done( fn );
 
// аналогично:
 
$.get("/foo/").success( fn );
 
// аналогично:
 
$.get("/foo/", fn );


Создание своего Deferred поведения


Мы знаем, что методы $.ajax и $.when предоставляют нам deferred API внутри, но мы можете сделать свой собственный вариант:
function getData(){
   return $.get('/foo/');
}
 
function showDiv(){
    var dfd = $.Deferred();
 
    $('#foo').fadeIn( 1000, dfd.resolve );
 
    return dfd.promise();
}
 
$.when( getData(), showDiv() )
    .then(function( ajaxResult ){
        console.log('The animation AND the AJAX request are both done!');
 
        // 'ajaxResult' это ответ от сервера
    });

Пример на jsFiddle jsfiddle.net/ehynds/JSw5y/

Внутри showDiv() я создаю новый Deferred объект, выполняющий анимацию и возвращающий promise. Deferred завершается (вспомните dequeue(), если вы знакомы с методами работы с очередями в jQuery) после того как fadeIn() завершится. Между моментом времени когда возвращается promise и завершается Deferred, мы регистрируем callback, ожидающий удачное выполнение обоих задач. Поэтому, когда оба задания завершены, callback будет выполнен.

getData() возвращает объект с методом promise, который позволяет $.when() наблюдать за выполнением действия. Подобный объект возвращает showDiv()

Отложенные Deferred объекты


Мы можем сделать ещё один шаг для регистрации индивидуальных функций обратного вызова у getData() и showDiv() и регистрации их promise'ов в одном мастер-Deferred объекте.

Если вы желаете, чтобы что-нибудь произошло после выполнения success getData() или success showDiv() независимо, так же как и после success обоих getData() и showDiv(), просто зарегистрируйте callback для их индивидуальных Deferred объектов и свяжите их вместе через $.when
function getData(){
   return $.get('/foo/').success(function(){
      console.log('Fires after the AJAX request succeeds');
   });
}
 
function showDiv(){
    var dfd = $.Deferred();
 
    dfd.done(function(){
      console.log('Fires after the animation succeeds');
    });
 
    $('#foo').fadeIn( 1000, dfd.resolve );
 
    return dfd.promise();
}
 
$.when( getData(), showDiv() )
    .then(function( ajaxResult ){
        console.log('Fires after BOTH showDiv() AND the AJAX request succeed!');
 
        // 'ajaxResult' is the server’s response
    });

Пример на jsFiddle jsfiddle.net/ehynds/W3cQc/

Цепочки Deferred


Обратные вызовы Deferred могут быть представлены в виде цепочек, так как promise возвращается из callback методов. Вот реальный пример от @ajpiano:
function saveContact( row ){
   var form = $.tmpl(templates["contact-form"]),
      valid = true,
      messages = [],
      dfd = $.Deferred();
 
   /*
      bunch of client-side validation here
   */
 
   if( !valid ){
      dfd.resolve({
         success: false,
         errors: messages
      });
   } else {
      form.ajaxSubmit({
         dataType: "json",
         success: dfd.resolve,
         error: dfd.reject
      });
   }
 
   return dfd.promise();
};
 
saveContact( row )
   .then(function(response){
      if( response.success ){
         // saving worked; rejoice
      } else {
         // client-side validation failed
         // output the contents of response.errors
      }
   })
   .fail(function(err) {
      // AJAX request failed
   });

Функция saveContact() проверяет форму и сохраняет результат в переменную valid. Если валидация провалилась, deferred объект завершается с объектом, содержащим булеан success и массив ошибок. Если форма прошла валидацию — deferred объект завершается, но в этот раз с ответом от AJAX запроса. Обработчик fail() слушает ошибки транспорта (404, 500 и другие)

Не наблюдаемые задачи


Deferreds особенно полезны, когда логика выполнения может или не может быть асинхронной, и вы хотите абстрагировать это условие от основного кода. Ваша задача может вернуть promise, но она может также возвращать строку, объект, или что-то другое.

В этом примере, когда первый раз кликаем на ссылку «launch application», AJAX запрос говорит серверу сохранить и возвратить текущий timestamp. Timestamp сохраняется в data-кэше элемента после выполнения AJAX запроса. Приложение заботится только о первом клике. На последующих клика timestamp берется из data-кэша, вместо того, чтобы делать запрос на сервер.
function startTask( element ){
   var timestamp = $.data( element, 'timestamp' );
 
   if( timestamp ){
       return timestamp;
   } else {
       return $.get('/start-task/').success(function( timestamp ){
           $.data( element, 'timestamp', timestamp );
       });
   }
}
 
$('#launchApplication').bind('click', function( event ){
   event.preventDefault();
 
   $.when( startTask(this) ).done(function( timestamp ){
       $('#status').html( '<p>You first started this task on: ' + timestamp + '</p>');
   });
 
   loadApplication();
});


Когда $.when() понимает, что его первый аргумент не имеет promise (и поэтому он не наблюдаемый), он создает новый deferred объект и завершает его с объектом data и возвращает promise этого отложенного объекта. Так, что любой объект без promise может быть наблюдаемым.

Одно маленькое но, вы не можете сделать отложенный запуск объекта, который возвращает объект со своим методом promise. Deferred объекты определяются о наличию метода promise, но jQuery не проверяет возвращает ли promise необходимый объект. Поэтому этот код содержит ошибку syntax error:
var obj = {
   promise: function(){
      // do something
   }
};
 
$.when( obj ).then( fn );


Заключение


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

+110
10217
250
azproduction 202,2 G+

комментарии (19)



+3
Acid_Beast,#
Спасибо вам!


+7
k12th,#
Отличная вещь. Надо переходить на 1.5 как можно скорее.


+2
VitaZheltyakov,#
↵
↑
Хотелось бы, но jQuery UI версию 1.5 ещё не поддерживает.


0
k12th,#
↵
↑
UI же не везде нужен.


0
Irker,#
↵
↑
Везде его использовать и не заставляют…


+6
hannimed,#
↵
↑
и не всем он нравится…


0
dapmoed,#
↵
↑
На первый взгляд UI нормально работает с 1.5. По крайней мере в моих проектах.


+18
pacaya,#
Мой мозг ломается при попытке понять русский язык данной статьи. С пониманием javascript части вроде проще…


+5
TheShock,#
↵
↑
У меня тоже такое в последнее время. Всё чаще мне Javascript понять проще чем русский.


+3
kulakowka,#
Отличная статья!
Я думаю было бы уместно, приводить примеры, где это уже используется и каким образом. Что бы менее опытные разработчики, которые еще сами не могут придумать зачем им этот функционал, могли бы лучше разобраться в этом нововведении и прикинуть как они могли бы использовать это в своих проектах.


0
azproduction,#
↵
↑
Один из вариантов находится по «Не наблюдаемые задачи». Сейчас ещё наваяю где я вижу вариант использования.


+5
seriyPS,#
Ну, если упростить до предела, то можно сказать так:
Раньше писали
$.ajax({
    "success": function(data){
        сделать_что_то_одно(data);
        сделать_еще_что_то(data);
    },
    "error": function(err){
        обработать_ошибку(err);
})

теперь будет
$.ajax().success(function(data){
    сделать_что_то_одно(data);
}).success(function(data){
    сделать_еще_что_то(data);
}).error(function(err){
    обработать_ошибку(err);
})

Ну и стало проще связывать Ajax запросы и всякие анимашки.

А вообще функциональное программирование рулит когда дело касается асинхронности


+18
TEHEK,#
↵
↑
Не совсем. Важно и то, что раньше делали
(function () {
    var done1 = false, done2 = false;
    
    function whenDone () {
        // 
    }
    
    $.get("url", function () {
            done1 = true;
            if (done1 && done2)
                whenDone();
        }
    );
    
    $.get("url", function () {
            done2 = true;
            if (done1 && done2)
                whenDone()
        }
    );
})()


А теперь можно
$.when( $.get("url"), $.get("url2"))
    .then(whenDone);

четверг, 16 января 2014 г.

Dynamically Composing Expression Predicates

http://www.albahari.com/nutshell/predicatebuilder.aspx

Dynamically Composing Expression Predicates

Suppose you want to write a LINQ to SQL or Entity Framework query that implements a keyword-style search. In other words, a query that returns rows whose description contains some or all of a given set of keywords.
We can proceed as follows:
IQueryable<Product> SearchProducts (params string[] keywords)
{
  IQueryable<Product> query = dataContext.Products;

  foreach (string keyword in keywords)
  {
    string temp = keyword;
    query = query.Where (p => p.Description.Contains (temp));
  }
  return query;
}
The temporary variable in the loop is required to avoid the outer variable trap, where the same variable is captured for each iteration of the foreach loop.
So far, so good. But this only handles the case where you want to match all of the specified keywords. Suppose instead, we wanted products whose description contains any of the supplied keywords. Our previous approach of chaining Where operators is completely useless! We could instead chain Union operators, but this would be inefficient. The ideal approach is to dynamically construct a lambda expression tree that performs an or-based predicate.
Of all the things that will drive you to manually constructing expression trees, the need for dynamic predicates is the most common in a typical business application. Fortunately, it’s possible to write a set of simple and reusable extension methods that radically simplify this task. This is the role of our PredicateBuilder class.

Using PredicateBuilder

Here's how to solve the preceding example with PredicateBuilder:
IQueryable<Product> SearchProducts (params string[] keywords)
{
  var predicate = PredicateBuilder.False<Product>();

  foreach (string keyword in keywords)
  {
    string temp = keyword;
    predicate = predicate.Or (p => p.Description.Contains (temp));
  }
  return dataContext.Products.Where (predicate);
}
If querying with Entity Framework, change the last line to this:
return objectContext.Products.AsExpandable().Where (predicate);
The AsExpandable method is part of LINQKit (see below).
The easiest way to experiment with PredicateBuilder is with LINQPad. LINQPad lets you instantly test LINQ queries against a database or local collection and has direct support for PredicateBuilder (press F4 and check 'Include PredicateBuilder').

PredicateBuilder Source Code

Here's the complete source:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
 
public static class PredicateBuilder
{
  public static Expression<Func<T, bool>> True<T> ()  { return f => true;  }
  public static Expression<Func<T, bool>> False<T> () { return f => false; }
 
  public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr1,
                                                      Expression<Func<T, bool>> expr2)
  {
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
    return Expression.Lambda<Func<T, bool>>
          (Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
  }
 
  public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
                                                       Expression<Func<T, bool>> expr2)
  {
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
    return Expression.Lambda<Func<T, bool>>
          (Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
  }
}
PredicateBuilder is also shipped as part of LINQKit, a productivity kit for LINQ to SQL and Entity Framework.
If you're using LINQ to SQL, you can use the PredicateBuilder source code on its own.

If you're using Entity Framework, you'll need the complete LINQKit - for the AsExpandable functionality. You can either reference LINQKit.dll or copy LINQKit's source code into your application.

How it Works

The True and False methods do nothing special: they are simply convenient shortcuts for creating anExpression<Func<T,bool>> that initially evaluates to true or false. So the following:
var predicate = PredicateBuilder.True <Product> ();
is just a shortcut for this:
Expression<Func<Product, bool>> predicate = c => true;
When you’re building a predicate by repeatedly stacking and/or conditions, it’s useful to have a starting point of either true or false (respectively). Our SearchProducts method still works if no keywords are supplied.
The interesting work takes place inside the And and Or methods. We start by invoking the second expression with the first expression’s parameters. An Invoke expression calls another lambda expression using the given expressions as arguments. We can create the conditional expression from the body of the first expression and the invoked version of the second. The final step is to wrap this in a new lambda expression.
Entity Framework's query processing pipeline cannot handle invocation expressions, which is why you need to call AsExpandable on the first object in the query. By calling AsExpandable, you activate LINQKit's expression visitor class which substitutes invocation expressions with simpler constructs that Entity Framework can understand.

More Examples

A useful pattern in writing a data access layer is to create a reusable predicate library. Your queries, then, consist largely ofselect and orderby clauses, the filtering logic farmed out to your library. Here's a simple example:
public partial class Product
{
  public static Expression<Func<Product, bool>> IsSelling()
  {
    return p => !p.Discontinued && p.LastSale > DateTime.Now.AddDays (-30);
  }
}
We can extend this by adding a method that uses PredicateBuilder:
public partial class Product
{
  public static Expression<Func<Product, bool>> ContainsInDescription (
                                                params string[] keywords)
  {
    var predicate = PredicateBuilder.False<Product>();
    foreach (string keyword in keywords)
    {
      string temp = keyword;
      predicate = predicate.Or (p => p.Description.Contains (temp));
    }
    return predicate;
  }
}
This offers an excellent balance of simplicity and reusability, as well as separating business logic from expression plumbing logic. To retrieve all products whose description contains “BlackBerry” or “iPhone”, along with the Nokias and Ericssons that are selling, you would do this:
var newKids  = Product.ContainsInDescription ("BlackBerry", "iPhone");

var classics = Product.ContainsInDescription ("Nokia", "Ericsson")
                      .And (Product.IsSelling());
var query =
  from p in Data.Products.Where (newKids.Or (classics))
  select p;
The And and Or methods in boldface resolve to extension methods in PredicateBuilder.
An expression predicate can perform the equivalent of an SQL subquery by referencing association properties. So, if Producthad a child EntitySet called Purchases, we could refine our IsSelling method to return only those products that have sold a minimum number of units as follows:
public static Expression<Func<Product, bool>> IsSelling (int minPurchases)
{
  return prod =>
    !prod.Discontinued &&
     prod.Purchases.Where (purch => purch.Date > DateTime.Now.AddDays(-30))
                    .Count() >= minPurchases;
}

Nesting Predicates

Consider the following predicate:
p => p.Price > 100 &&
     p.Price < 1000 &&
     (p.Description.Contains ("foo") || p.Description.Contains ("far"))
Let's say we wanted to build this dynamically. The question is, how do we deal with the parenthesis around the two expressions in the last line?
The answer is to build the parenthesised expression first, and then consume it in the outer expression as follows:
var inner = PredicateBuilder.False<Product>();
inner = inner.Or (p => p.Description.Contains ("foo"));
inner = inner.Or (p => p.Description.Contains ("far"));

var outer = PredicateBuilder.True<Product>();
outer = outer.And (p => p.Price > 100);
outer = outer.And (p => p.Price < 1000);
outer = outer.And (inner);
Notice that with the inner expression, we start with PredicateBuilder.False (because we're using the Or operator). With the outer expression, however, we start with PredicateBuilder.True (because we're using the And operator).

Generic Predicates

Suppose every table in your database has ValidFrom and ValidTo columns as follows:
create table PriceList
(
   ID int not null primary key,
   Name nvarchar(50) not null,
   ValidFrom datetime,
   ValidTo datetime
)
To retrieve rows valid as of DateTime.Now (the most common case), you'd do this:
from p in PriceLists
where (p.ValidFrom == null || p.ValidFrom <= DateTime.Now) &&
      (p.ValidTo   == null || p.ValidTo   >= DateTime.Now)
select p.Name
Of course, that logic in bold is likely to be duplicated across multiple queries! No problem: let's define a method in the PriceListclass that returns a reusable expression:
public static Expression<Func<PriceList, bool>> IsCurrent()
{
   return p => (p.ValidFrom == null || p.ValidFrom <= DateTime.Now) &&
               (p.ValidTo   == null || p.ValidTo   >= DateTime.Now);
}
OK: our query is now much simpler:
var currentPriceLists = db.PriceLists.Where (PriceList.IsCurrent());
And with PredicateBuilder's And and Or methods, we can easily introduce other conditions:
var currentPriceLists = db.PriceLists.Where (
                          PriceList.IsCurrent().And (p => p.Name.StartsWith ("A")));
But what about all the other tables that also have ValidFrom and ValidTo columns? We don't want to repeat our IsCurrentmethod for every table! Fortunately, we can generalize our IsCurrent method with generics.
The first step is to define an interface:
public interface IValidFromTo
{
   DateTime? ValidFrom { get; }
   DateTime? ValidTo   { get; }
}
Now we can define a single generic IsCurrent method using that interface as a constraint:
public static Expression<Func<TEntity, bool>> IsCurrent<TEntity>()
   where TEntity : IValidFromTo
{
   return e => (e.ValidFrom == null || e.ValidFrom <= DateTime.Now) &&
               (e.ValidTo   == null || e.ValidTo   >= DateTime.Now);
}
The final step is to implement this interface in each class that supports ValidFrom and ValidTo. If you're using Visual Studio or a tool like SqlMetal to generate your entity classes, do this in the non-generated half of the partial classes:
public partial class PriceList : IValidFromTo { }
public partial class Product   : IValidFromTo { }

Using PredicateBuilder within LINQPad

With LINQPad, you can write and test queries much faster than with Visual Studio's build/run/debug cycle. To use PredicateBuilder in LINQPad with LINQ to SQL:
  • Press F4 and check 'Include PredicateBuilder'
To use PredicateBuilder in LINQPad with Entity Framework:

понедельник, 6 января 2014 г.

Wcf Client Server Push using MSMQ

WCF push

Взято отсюда:   http://habrahabr.ru/post/128634/

Создание Push Notification сервиса на основе WCF RESTиз песочницы

В качестве вступления

Модель push-нотификаций является распространённой моделью для обмена сообщениями. Она подразумевает не получение информации по запросу, а немедленную её передачу отправителю при появлении этой информации на сервере.

Стандартный подход с ипользованием wsDualHttpBinding

Возможность создания push-механизма предоставляет и WCF. Этот фреймворк позволяет создать push-сервис с использованием wsDualHttpBinding контракта. Такой контракт позволяет для каждого запроса определить метод обратного вызова, который будет вызван при наступлении какого-либо события.
Если применить этот механизм к системе обмена сообщениями, то получим следующий алгоритм:

— Для каждого запроса на новые сообщения создаётся callback, который сохраняется в списке подписчиков на новые сообщения.
— При получении нового сообщения, система проходит по списку подписчиков и находит нужного нам получателя сообщения (а значит и нужный callback).
— Вызываем нужный нам callback-метод.
Ниже приведён пример использования wsDualHttpBinding для WCF сервиса:

— Создаём метод обратного вызова для запроса на новые сообщения
interface IMessageCallback
    { 
        [OperationContract(IsOneWay = true)]
        void OnMessageAdded(int senderId, string message, DateTime timestamp);
    }

— Создаём сервис контракт
    [ServiceContract(CallbackContract = typeof(IMessageCallback))]
    public interface IMessageService
    {
        [OperationContract]
        void AddMessage(int senderId, int recipientId, string message);

        [OperationContract]
        bool Subscribe();
    }

— Создаём сам сервис
Public class MessageService : IMessageService
{
     private static List<IMessageCallback> subscribers = new List<IMessageCallback>();

     public bool Subscribe(int id)
    {
         try
         {
               IMessageCallback callback =
               OperationContext.Current.GetCallbackChannel<IMessageCallback>();
               callback.id = id;
               if (!subscribers.Contains(callback))
                   subscribers.Add(callback);
               return true;
         }
          catch
         {
              return false;
         }
    }

     public void AddMessage(int senderId, int recipientId, string message)
    {
          subscribers.ForEach(delegate(IMessageCallback callback)
         {
              if ((((ICommunicationObject)callback).State == CommunicationState.Opened) && (callback.id ==     recipientId))
              {
                    callback.OnMessageAdded(recipientId, message, DateTime.Now);
              }
              else
              {
                    subscribers.Remove(callback);
               }
         });
     }
}

— Конфигурируем сервис в файле web.config
<system.serviceModel>
    <services>
      <service name="WCFPush.MessageService" behaviorConfiguration="Default">
        <endpoint address ="" binding="wsDualHttpBinding" contract="WCFPush.IMessage">
        </endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="Default">
          <serviceMetadata httpGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

Сервис готов.

Однако, эта модель работает только в том случае, когда и сервер, и подписчики являются .NET-приложениями.

Использование RESTful подхода

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

Итак, создадим сервис, аналогичный по функциональности предыдущему, только на основе REST.
В случае с асинхронной моделью запрос состоит из двух частей: BeginRequestName и EndRequestName.

— Определим ServiceContract для REST-сервиса
    [ServiceContract]
    public interface IMessageService
    {
        [WebGet(UriTemplate = "AddMessage?senderId={senderId}&recipientId={recipientId}&message={message}")]
        [OperationContract ]
        bool AddMessage(int senderId, int recipientId, string message);

        [WebGet(UriTemplate = "Subscribe?id={id}")]
        [OperationContract(AsyncPattern = true)]
        IAsyncResult BeginGetMessage(int id, AsyncCallback callback, object asyncState);

        ServiceMessage EndGetMessage(IAsyncResult result);
    }

Обратите внимание: EndGetMessage не помечен аттрибутом OperationContact.

— Создадим класс для асинхронного результата, реализующий интерфейс IAsyncResult
public class MessageAsyncResult : IAsyncResult
    {
        public AsyncCallback Callback { get; set; }

        private readonly object accessLock = new object();
        private bool isCompleted = false;
        private ServiceMessage result;

        private int recipientId;

        private object asyncState;

        public MessageAsyncResult(object state)
        {
            asyncState = state;
        }

        public int RecipientId
        {
            get
            {
                lock (accessLock)
                {
                    return recipientId;
                }
            }
            set
            {
                lock (accessLock)
                {
                    recipientId = value;
                }
            }
        }


        public ServiceMessage Result
        {
            get
            {
                lock (accessLock)
                {
                    return result;
                }
            }
            set
            {
                lock (accessLock)
                {
                    result = value;
                }
            }
        }

        public bool IsCompleted
        {
            get
            {
                lock (accessLock)
                {
                    return isCompleted;
                }
            }
            set
            {
                lock (accessLock)
                {
                    isCompleted = value;
                }
            }
        }

        public bool CompletedSynchronously
        {
            get
            {
                return false;
            }
        }

        public object AsyncState
        {
            get
            {
                return asyncState;
            }
        }

        public WaitHandle AsyncWaitHandle
        {
            get
            {
                return null;
            }
        }

}

Помимо реализации интерфейса, в этом классе также хранится Id получателя сообщения (recipientId), а также само сообщение, которое будет доставлено отправителю(result).

— Теперь реализуем сам сервис
[ServiceBehavior(
    InstanceContextMode = InstanceContextMode.PerCall,
    ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class MessageService : IMessageService
    {
        private static List<MessageAsyncResult> subscribers = new List<MessageAsyncResult>();

        public bool AddMessage(int senderId, int recipientId, string message)
        {
            
            subscribers.ForEach(delegate(MessageAsyncResult result)
            {
                if (result.RecipientId == recipientId)
                {
                
                    result.Result = new ServiceMessage(senderId, recipientId, message, DateTime.Now);
                    result.IsCompleted = true;
                    result.Callback(result);
                    subscribers.Remove(result);

                }
                
            });
            return true;
        }

        public IAsyncResult BeginGetMessage(int id, AsyncCallback callback, object asyncState)
        {
            MessageAsyncResult asyncResult = new MessageAsyncResult(asyncState);
            asyncResult.Callback = callback;
            asyncResult.RecipientId = id;
            subscribers.Add(asyncResult);
            return asyncResult;
        }

        public ServiceMessage EndGetMessage(IAsyncResult result)
        {
            return (result as MessageAsyncResult).Result;
        }
    }

Когда приходит запрос на получение нового сообщения, создаётся асинхронный результат, который добавляется в список подписчиков. Как только приходит сообщение для данного подписчика, свойство IsCompleted для данного IAsyncResult устанавливается в true, и вызывается метод EndGetMessage. В EndGetMessage отправляется ответ подписчику.

— Осталось сконфигурировать сервис в файле web.config
<system.serviceModel>
    <bindings>
      <webHttpBinding>
    <binding name="webBinding">
    </binding>
      </webHttpBinding>
    </bindings>
    <services>
      <service name=" WCFPush.MessageService" behaviorConfiguration="Default">
        <endpoint address="" contract="WCFPush.IMessageService" behaviorConfiguration="web" bindingConfiguration="webBinding" binding="webHttpBinding">
        </endpoint>
      </service>
    </services>
    <behaviors>
      <endpointBehaviors>
        <behavior name="web">
          <webHttp />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="Default">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>

Сервис готов.

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

Заключение

Таким образом можно реализовать Push-сервис для обмена сообщениями “в реальном времени”, основываясь на REST запросах. Такой сервис может использоваться с любого клиента, поддерживающиего RESTful реквесты, в том числе и из обычного браузера.