簡體   English   中英

通過WebSocket連接同步集合

[英]Synchronising a collection over a websocket connection

目前,我正在開發一個客戶端-服務器系統,並且正在嘗試獲取一個集合以在WebSocket之間進行同步。 一切都在C#+ .Net 4.5中,我想知道是否存在通過websocket同步數據的最佳實踐。 這是一種同步方式:

服務器:BindingCollection <MyClass> ----- Websocket ----->客戶端:BindingCollection <MyClass>

該集合最多可以包含1000個對象,每個對象具有20個字段,因此每次發送全部批次似乎有點浪費。

我將使用觀察者模式,僅發送更改的對象進行同步。

因此,我終於花時間寫了一個小例子。 我正在使用一個內存通用存儲庫,該存儲庫在更改時調用事件。 然后將更改發送給所有客戶端,這樣您就不必發送完整的列表/集合。

監控簡單模型

using System;

namespace SynchronizingCollection.Common.Model
{
    public class MyModel
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

通用存儲庫

注意添加/更新/刪除某些東西時調用的事件OnChange。 該事件在XSockets長時間運行的控制器(單例)中被“訂閱”。請參見“ RepoMonitor”類

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

namespace SynchronizingCollection.Server.Repository
{
    /// <summary>
    /// A static generic thread-safe repository for in-memory storage
    /// </summary>
    /// <typeparam name="TK">Key Type</typeparam>
    /// <typeparam name="T">Value Type</typeparam>
    public static class Repository<TK, T>
    {
        /// <summary>
        /// When something changes
        /// </summary>
        public static event EventHandler<OnChangedArgs<TK,T>> OnChange;

        private static ConcurrentDictionary<TK, T> Container { get; set; }

        static Repository()
        {
            Container = new ConcurrentDictionary<TK, T>();
        }

        /// <summary>
        /// Adds or updates the entity T with key TK
        /// </summary>
        /// <param name="key"></param>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static T AddOrUpdate(TK key, T entity)
        {
            var obj = Container.AddOrUpdate(key, entity, (s, o) => entity);
            if(OnChange != null)
                OnChange.Invoke(null,new OnChangedArgs<TK, T>(){Key = key,Value = entity, Operation =  Operation.AddUpdate});
            return obj;
        }

        /// <summary>
        /// Removes the entity T with key TK
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool Remove(TK key)
        {
            T entity;
            var result = Container.TryRemove(key, out entity);
            if (result)
            {
                if (OnChange != null)
                    OnChange.Invoke(null, new OnChangedArgs<TK, T>() { Key = key, Value = entity, Operation = Operation.Remove});
            }
            return result;
        }

        /// <summary>
        /// Removes all entities matching the expression f
        /// </summary>
        /// <param name="f"></param>
        /// <returns></returns>
        public static int Remove(Func<T, bool> f)
        {
            return FindWithKeys(f).Count(o => Remove(o.Key));
        }        

        /// <summary>
        /// Find all entities T matching the expression f
        /// </summary>
        /// <param name="f"></param>
        /// <returns></returns>
        public static IEnumerable<T> Find(Func<T, bool> f)
        {
            return Container.Values.Where(f);
        }

        /// <summary>
        /// Find all entities T matching the expression f and returns a Dictionary TK,T
        /// </summary>
        /// <param name="f"></param>
        /// <returns></returns>
        public static IDictionary<TK, T> FindWithKeys(Func<T, bool> f)
        {
            var y = from x in Container
                    where f.Invoke(x.Value)
                    select x;
            return y.ToDictionary(x => x.Key, x => x.Value);
        }

        /// <summary>
        /// Returns all entities as a Dictionary TK,T
        /// </summary>
        /// <returns></returns>
        public static IDictionary<TK, T> GetAllWithKeys()
        {
            return Container;
        }

        /// <summary>
        /// Returns all entities T from the repository
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<T> GetAll()
        {
            return Container.Values;
        }

        /// <summary>
        /// Get a single entity T with the key TK
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T GetById(TK key)
        {
            return Container.ContainsKey(key) ? Container[key] : default(T);
        }

        /// <summary>
        /// Get a single entity T as a KeyValuePair TK,T with the key TK
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static KeyValuePair<TK, T> GetByIdWithKey(TK key)
        {
            return Container.ContainsKey(key) ? new KeyValuePair<TK, T>(key, Container[key]) : new KeyValuePair<TK, T>(key, default(T));
        }

        /// <summary>
        /// Checks if the repository has a key TK
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool ContainsKey(TK key)
        {
            return Container.ContainsKey(key);
        }
    }
}

事件參數和一個枚舉,用於了解發生了什么變化

using System;

namespace SynchronizingCollection.Server.Repository
{
    /// <summary>
    /// To send changes in the repo
    /// </summary>
    /// <typeparam name="TK"></typeparam>
    /// <typeparam name="T"></typeparam>
    public class OnChangedArgs<TK,T> : EventArgs
    {
        public Operation Operation { get; set; }
        public TK Key { get; set; }
        public T Value { get; set; }
    }
}

namespace SynchronizingCollection.Server.Repository
{
    /// <summary>
    /// What kind of change was performed
    /// </summary>
    public enum Operation
    {
        AddUpdate,
        Remove
    }
}

將更改發送到客戶端的控制器...

using System;
using SynchronizingCollection.Common.Model;
using SynchronizingCollection.Server.Repository;
using XSockets.Core.XSocket;
using XSockets.Core.XSocket.Helpers;
using XSockets.Plugin.Framework;
using XSockets.Plugin.Framework.Attributes;

namespace SynchronizingCollection.Server
{
    /// <summary>
    /// Long running controller that will send information to clients about the collection changes
    /// </summary>
    [XSocketMetadata(PluginRange = PluginRange.Internal, PluginAlias = "RepoMonitor")]
    public class RepositoryMonitor : XSocketController
    {
        public RepositoryMonitor()
        {
            Repository<Guid, MyModel>.OnChange += RepositoryOnChanged;
        }

        private void RepositoryOnChanged(object sender, OnChangedArgs<Guid, MyModel> e)
        {
            switch (e.Operation)
            {
                case Operation.Remove:
                    this.InvokeTo<Demo>(p => p.SendUpdates, e.Value,"removed");
                break;                    
                case Operation.AddUpdate:
                    this.InvokeTo<Demo>(p => p.SendUpdates, e.Value, "addorupdated");
                break;                    
            }
        }       
    }
}

客戶端調用以添加/刪除/更新集合的XSockets控制器。

using System;
using SynchronizingCollection.Common.Model;
using SynchronizingCollection.Server.Repository;
using XSockets.Core.XSocket;

namespace SynchronizingCollection.Server
{
    public class Demo : XSocketController
    {
        public bool SendUpdates { get; set; }

        public Demo()
        {
            //By default all clients get updates
            SendUpdates = true;
        }

        public void AddOrUpdateModel(MyModel model)
        {
            Repository<Guid, MyModel>.AddOrUpdate(model.Id, model);
        }

        public void RemoveModel(MyModel model)
        {
            Repository<Guid, MyModel>.Remove(model.Id);
        }        
    }
}

還有一個用C#編寫的演示客戶端,它添加和刪除了10個不同的對象...但是使用JavaScript API也很容易。 尤其是與可在客戶端上操作集合的kickoutjs。

using System;
using System.Threading;
using SynchronizingCollection.Common.Model;
using XSockets.Client40;

namespace SynchronizingCollection.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            var c = new XSocketClient("ws://127.0.0.1:4502","http://localhost","demo");

            c.Controller("demo").OnOpen += (sender, connectArgs) => Console.WriteLine("Demo OPEN");

            c.Controller("demo").On<MyModel>("addorupdated", model => Console.WriteLine("Updated " + model.Name));
            c.Controller("demo").On<MyModel>("removed", model => Console.WriteLine("Removed " + model.Name));

            c.Open();

            for (var i = 0; i < 10; i++)
            {
                var m = new MyModel() {Id = Guid.NewGuid(), Name = "Person Nr" + i, Age = i};
                c.Controller("demo").Invoke("AddOrUpdateModel", m);

                Thread.Sleep(2000);

                c.Controller("demo").Invoke("RemoveModel", m);
                Thread.Sleep(2000);
            }

            Console.ReadLine();
        }
    }
}

您可以從我的保管箱下載該項目: https ://www.dropbox.com/s/5ljbedovx6ufkww/SynchronizingCollection.zip ? dl =0

問候烏菲

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM