How to do long polling correctly in MVC 3

I am trying to connect AsyncController so that when a user clicks the save button on an order on the order page, all users viewing the same order should be notified that the order has changed. My approach to implementing this is to do long polls of ajax requests on the order page, however, how to make a scalable AsyncController to solve this problem is not obvious to me.

So this is what I have so far, the identifier is the order identifier that is signaled as changed or polled for changes.

public class MessageController : AsyncController
{
    static readonly ConcurrentDictionary<int, AutoResetEvent> Events = new ConcurrentDictionary<int, AutoResetEvent>();

    public ActionResult Signal(int id)
    {
        AutoResetEvent @event;
        if (Events.TryGetValue(id, out @event))
            @event.Set();

        return Content("Signal");
    }

    public void WaitAsync(int id)
    {
        Events.TryAdd(id, new AutoResetEvent(false));

        // TODO: This "works", but I should probably not block this thread.
        Events[id].WaitOne();
    }

    public ActionResult WaitCompleted()
    {
        return Content("WaitCompleted");
    }
}

AJAX ASP.NET MVC?. , , , , , .

, , ? , , , .

+5
1

, , , , ThreadPool.RegisterWaitForSingleObject.

public class ConcurrentLookup<TKey, TValue>
{
    private readonly Dictionary<TKey, List<TValue>> _lookup = new Dictionary<TKey, List<TValue>>();

    public void Add(TKey key, TValue value)
    {
        lock (_lookup)
        {
            if (!_lookup.ContainsKey(key))
                _lookup.Add(key, new List<TValue>());

            _lookup[key].Add(value);
        }
    }

    public List<TValue> Remove(TKey key)
    {
        lock (_lookup)
        {
            if (!_lookup.ContainsKey(key))
                return new List<TValue>();

            var values = _lookup[key];
            _lookup.Remove(key);

            return values;
        }
    }
}

[SessionState(SessionStateBehavior.Disabled)]
public class MessageController : AsyncController
{
    static readonly ConcurrentLookup<int, ManualResetEvent> Events = new ConcurrentLookup<int, ManualResetEvent>();

    public ActionResult Signal(int id)
    {
        foreach (var @event in Events.Remove(id))
            @event.Set();

        return Content("Signal " + id);
    }

    public void WaitAsync(int id)
    {
        AsyncManager.OutstandingOperations.Increment();

        var @event = new ManualResetEvent(false);

        Events.Add(id, @event);

        RegisteredWaitHandle handle = null;
        handle = ThreadPool.RegisterWaitForSingleObject(@event, (state, timeout) => 
        {
            handle.Unregister(@event);
            @event.Dispose();

            AsyncManager.Parameters["id"] = id;
            AsyncManager.Parameters["timeout"] = timeout;
            AsyncManager.OutstandingOperations.Decrement();
        }, null, new TimeSpan(0, 2, 0), false);
    }


    public ActionResult WaitCompleted(int id, bool timeout)
    {
        return Content("WaitCompleted " + id + " " + (timeout? "Timeout" : "Signaled"));
    }
}
+3

All Articles