diff --git a/Nats.Services.KeyValueStoreDemo/StoreClient/ProductInfo.cs b/Nats.Services.KeyValueStoreDemo/StoreClient/ProductInfo.cs index bdbf5d7..bda7041 100644 --- a/Nats.Services.KeyValueStoreDemo/StoreClient/ProductInfo.cs +++ b/Nats.Services.KeyValueStoreDemo/StoreClient/ProductInfo.cs @@ -4,9 +4,13 @@ namespace StoreClient { - public class ProductInfo + public class ProductInfo : StoreRowItem { public Product Product { get; private set; } + public ProductInfo() + { + + } public ProductInfo(Product product) { Product = product; @@ -14,6 +18,11 @@ public ProductInfo(Product product) } public void Update(Product product) { + if( Product == null) + { + Product = product; + return; + } Product.Price = product.Price; Product.Quantity= product.Quantity; LastUpdate = DateTime.Now; diff --git a/Nats.Services.KeyValueStoreDemo/StoreClient/StoreClient.csproj b/Nats.Services.KeyValueStoreDemo/StoreClient/StoreClient.csproj index 1a067a0..4f81104 100644 --- a/Nats.Services.KeyValueStoreDemo/StoreClient/StoreClient.csproj +++ b/Nats.Services.KeyValueStoreDemo/StoreClient/StoreClient.csproj @@ -69,6 +69,8 @@ + + ResXFileCodeGenerator Resources.Designer.cs diff --git a/Nats.Services.KeyValueStoreDemo/StoreClient/StoreClientForm.Designer.cs b/Nats.Services.KeyValueStoreDemo/StoreClient/StoreClientForm.Designer.cs index 47fa394..e649153 100644 --- a/Nats.Services.KeyValueStoreDemo/StoreClient/StoreClientForm.Designer.cs +++ b/Nats.Services.KeyValueStoreDemo/StoreClient/StoreClientForm.Designer.cs @@ -28,7 +28,7 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - this.objectListView1 = new BrightIdeasSoftware.ObjectListView(); + this.objectListView1 = new BrightIdeasSoftware.FastObjectListView(); ((System.ComponentModel.ISupportInitialize)(this.objectListView1)).BeginInit(); this.SuspendLayout(); // diff --git a/Nats.Services.KeyValueStoreDemo/StoreClient/StoreClientForm.cs b/Nats.Services.KeyValueStoreDemo/StoreClient/StoreClientForm.cs index 7442791..1326c9a 100644 --- a/Nats.Services.KeyValueStoreDemo/StoreClient/StoreClientForm.cs +++ b/Nats.Services.KeyValueStoreDemo/StoreClient/StoreClientForm.cs @@ -1,12 +1,9 @@ -using BrightIdeasSoftware; -using Nats.Services.Core; +using Nats.Services.Core; using Nats.Services.Core.DiscoveryService; using NATS.Client; using NLog; using StoreServices; using System; -using System.Collections.Generic; -using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; @@ -20,12 +17,11 @@ public partial class StoreClientForm : Form IConnection connection; NatsServiceFactory serviceFactory; IProductStoreService storeService; - private List productInfos; + StoreClientModel model = new StoreClientModel(); public StoreClientForm() { InitializeComponent(); - Generator.GenerateColumns(objectListView1, typeof(ProductInfo)); } private async void StoreClientForm_LoadAsync(object sender, EventArgs e) @@ -39,36 +35,16 @@ private async void StoreClientForm_LoadAsync(object sender, EventArgs e) string serverName = await Task.Run(() => DiscoverServer(connection, logger)); logger.Info($"Server found: {serverName}."); Text += serverName; - var products = await Task.Run(()=>InitServices(serverName)); - logger.Info($"Store initialized: {products.Count}"); - InitData(products); + await InitServicesAsync(serverName); + logger.Info($"Store initialized: {objectListView1.GetItemCount()}"); } - private List InitServices(string serverName) + private async Task InitServicesAsync(string serverName) { serviceFactory = new NatsServiceFactory(connection, serverName); storeService = serviceFactory.BuildServiceClient(); - storeService.ValueUpdated += OnValueUpdated; - var products = storeService.GetAllValues(); - return products; - } - - private void OnValueUpdated(Product product) - { - if (productInfos == null) return; - var productInfo = productInfos.FirstOrDefault(pI => pI.Name == product.Name); - if( productInfo != null) - { - productInfo.Update(product); - objectListView1.RefreshObject(productInfo); - } - } - - private void InitData(List products) - { - Text += $", {products.Count} products"; - var tmp = products.Select(product => new ProductInfo(product)).ToList(); - productInfos = tmp; + await model.InitAsync(storeService, objectListView1); + Text += $", {objectListView1.GetItemCount()} products"; } public static string DiscoverServer(IConnection connection, ILogger logger=null, int periodMs=1000) diff --git a/Nats.Services.KeyValueStoreDemo/StoreClient/StoreClientModel.cs b/Nats.Services.KeyValueStoreDemo/StoreClient/StoreClientModel.cs new file mode 100644 index 0000000..fe042b2 --- /dev/null +++ b/Nats.Services.KeyValueStoreDemo/StoreClient/StoreClientModel.cs @@ -0,0 +1,89 @@ +using BrightIdeasSoftware; +using Nats.Services.Core.KeyValueStoreService; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace StoreClient +{ + public class StoreClientModel + where T_Value : IKeyIdentifiable + where T_RowItem : class, StoreRowItem, new() + { + IKeyValueStoreService service; + + Dictionary dicoKeyRows = new Dictionary(); + Dictionary updatedRows = new Dictionary(); + Timer timer; + ObjectListView view; + + public async Task InitAsync(IKeyValueStoreService service, ObjectListView view) + { + this.service = service; + this.view = view; + + var items= await Task.Run(() => service.GetAllValues()); + foreach(var item in items) + { + var rowItem = new T_RowItem(); + rowItem.Update(item); + dicoKeyRows[item.Key] = rowItem; + } + Generator.GenerateColumns(view, typeof(T_RowItem)); + + view.Objects = dicoKeyRows.Values; + timer = new Timer(); + timer.Tick += UpdateBatch; + timer.Start(); + service.ValueUpdated += OnValueUpdated; + } + + private void UpdateBatch(object sender, EventArgs evt) + { + List values; + lock (updatedRows) + { + if(updatedRows.Count == 0) + { + return; + } + values = updatedRows.Values.ToList(); + updatedRows.Clear(); + } + view.Invoke( (Action)(() => RefreshRows(values))); + } + + Stopwatch sw = new Stopwatch(); + long n = 0; + private void RefreshRows(List values) + { + n++; + sw.Start(); + view.RefreshObjects(values); + sw.Stop(); + if( n % 10 == 0) + { + Debug.WriteLine("n: " + n + ", t=" + sw.ElapsedMilliseconds + ", avg: " + (sw.ElapsedMilliseconds/ (double)n)); + } + } + + private void OnValueUpdated(T_Value obj) + { + lock(updatedRows) + { + T_RowItem rowItem; + if(! dicoKeyRows.TryGetValue(obj.Key, out rowItem)) + { + rowItem = new T_RowItem(); + dicoKeyRows[obj.Key] = rowItem; + } + + rowItem.Update(obj); + updatedRows[obj.Key] = rowItem; + } + } + } +} diff --git a/Nats.Services.KeyValueStoreDemo/StoreClient/StoreRowItem.cs b/Nats.Services.KeyValueStoreDemo/StoreClient/StoreRowItem.cs new file mode 100644 index 0000000..63e61c1 --- /dev/null +++ b/Nats.Services.KeyValueStoreDemo/StoreClient/StoreRowItem.cs @@ -0,0 +1,7 @@ +namespace StoreClient +{ + public interface StoreRowItem + { + void Update(T_Value value); + } +}