WPF consume data generated by grpc services

Install-Package Google.Protobuf
Install-Package Grpc.Net.Client
Install-Package Grpc.Tools
Install-Package Grpc.AspnetCore

 

Add book.proto both in service and client, set the build action as ProtoBuffer Compiler of project properties then rebuild and it will generate related files automatically

syntax="proto3";

option csharp_namespace="GrpcBookServer";

package book;

message  BookProto{
    int64 id=1;
    string name=2;
    string isbn=3;
    string abstract=4;
    string author=5;
    string comment=6;
    string content=7;
    string summary=8;
    string title=9;
    string topic=10;
}

message GetBooksListRequest{
    int32 cnt=1;
}

message GetBooksListResponse{
    repeated BookProto books=1;
}

service BookService
{
    rpc GetBooksList(GetBooksListRequest) returns (GetBooksListResponse);
}
 

 

Service

using Google.Protobuf;
using Google.Protobuf.Collections;
using Grpc.Core;
using GrpcBookServer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;
using System.Runtime.Serialization;
using System.Threading.Tasks.Dataflow;

namespace ConsoleApp19
{
    internal class Program
    {
        static void Main(string[] args)
        {
            GrpcDemo();
            Console.ReadLine();
        }

        static async Task GrpcDemo()
        {
            var builder = WebApplication.CreateBuilder();
            builder.WebHost.ConfigureKestrel(serviceOptions =>
            {
                serviceOptions.ListenLocalhost(8888, x => x.Protocols = HttpProtocols.Http2);
            });

            builder.Services.AddGrpc(options =>
            {
                options.MaxSendMessageSize = null;
                options.MaxSendMessageSize = null;
            }); 

            var app = builder.Build();
            app.MapGrpcService<BookServiceImpl>();
            
            Console.WriteLine($"{DateTime.Now},the grpc service started at http://localhost:8888");
            await app.RunAsync("http://localhost:8888");
        }
    }

    public class BookServiceImpl:BookService.BookServiceBase
    {
        private static long id = 0;
        private static long GetIncrementId()
        {
            return Interlocked.Increment(ref id);
        }

        public List<Book> GetBooksList(int cnt=1000000)
        {
            var bksList = new List<Book>();
            for(int i=0;i<cnt;i++)
            {
                var a = GetIncrementId();
                bksList.Add(new Book()
                {
                    Id=a,
                    Name=$"Name_{a}",
                    ISBN=$"ISBN_{a}_{Guid.NewGuid():N}",
                    Abstract=$"Abstract_{a}",
                    Author=$"Author_{a}",
                    Comment=$"Comment_{a}",
                    Content=$"Content_{a}",
                    Summary=$"Summary_{a}",
                    Title=$"Title_{a}",
                    Topic=$"Topic_{a}"
                });
            }
            return bksList;
        }


        public override Task<GetBooksListResponse> GetBooksList(GetBooksListRequest request, ServerCallContext context)
        {
            var count = request.Cnt is 0 ? 1000000 : request.Cnt;
            var bksList = GetBooksList(count);

            var response=new GetBooksListResponse();
            response.Books.AddRange(bksList.ToProtoList());
            return Task.FromResult(response);
        }
    }

    public static class BookMapper
    {
        public static BookProto ToProtoBook(this Book bk)
        {
            return new BookProto
            {
                Id = bk.Id,
                Name = bk.Name ?? "",
                Isbn = bk.ISBN ?? "",
                Author=bk.Author??"",
                Abstract = bk.Abstract ?? "",
                Comment = bk.Comment ?? "",
                Content = bk.Content ?? "",
                Summary = bk.Summary ?? "",
                Title = bk.Title ?? "",
                Topic = bk.Topic ?? ""
            };
        }

        public static Book ToModel(this BookProto pt)
        {
            return new Book
            {
                Id = pt.Id,
                Name = pt.Name,
                ISBN = pt.Isbn,
                Abstract = pt.Abstract,
                Author = pt.Author,
                Comment=pt.Comment,
                Content=pt.Content,
                Summary=pt.Summary,
                Title=pt.Title,
                Topic=pt.Topic
            };
        }

        public static List<Book> ToModelList(this RepeatedField<BookProto> list)
        {
            return list.Select(ToModel).ToList();
        }

        public static List<BookProto> ToProtoList(this List<Book> list)
        {
            return list.Select(ToProtoBook).ToList();
        }
    }


     
    public class Book
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public string ISBN { get; set; }
        public string Abstract { get; set; }
        public string Author { get; set; }
        public string Comment { get; set; }
        public string Content { get; set; }
        public string Summary { get; set; }
        public string Title { get; set; }
        public string Topic { get; set; }
    }

}

 

 

Client

<Window x:Class="WpfApp36.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp36"
        mc:Ignorable="d"
        Title="{Binding MainTitle}" WindowState="Maximized">
    <Window.DataContext>
        <local:MainVM/>
    </Window.DataContext>
    <Grid>
        <DataGrid ItemsSource="{Binding BooksCollection,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
                  AutoGenerateColumns="True"
                  CanUserAddRows="False"
                  VirtualizingPanel.IsVirtualizing="True"
                  VirtualizingPanel.VirtualizationMode="Recycling"
                  VirtualizingPanel.CacheLengthUnit="Item"
                  VirtualizingPanel.CacheLength="5,5"
                  ScrollViewer.IsDeferredScrollingEnabled="True"
                  ScrollViewer.CanContentScroll="True"
                  EnableRowVirtualization="True"
                  EnableColumnVirtualization="True"
                  UseLayoutRounding="True"
                  SnapsToDevicePixels="True">
            <DataGrid.Resources>
                <Style TargetType="DataGridRow">
                    <Setter Property="FontSize" Value="30"/>
                    <Style.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="FontSize" Value="40"/>
                            <Setter Property="Foreground" Value="Red"/>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </DataGrid.Resources>
        </DataGrid>
    </Grid>
</Window>


using Google.Protobuf.Collections;
using Grpc.Net.Client;
using GrpcBookServer;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace WpfApp36
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }

    public class MainVM : INotifyPropertyChanged
    {
        private DispatcherTimer tmr;
        private string requestUrl = "http://localhost:8888";
        GrpcChannel channel;
        BookService.BookServiceClient client;
        public MainVM()
        {
            if (!DesignerProperties.GetIsInDesignMode(new DependencyObject()))
            {
                var httpHandler = new HttpClientHandler();
                httpHandler.ServerCertificateCustomValidationCallback = (_, _, _, _) => true;
                channel = GrpcChannel.ForAddress(requestUrl, new GrpcChannelOptions
                {
                    MaxSendMessageSize = null,
                    MaxReceiveMessageSize = null,
                    HttpHandler = httpHandler
                });

                client = new BookService.BookServiceClient(channel);
                _ = LoadBooksCollectionAsync();
                InitTmr();
            }
        }

        private void InitTmr()
        {
            tmr = new DispatcherTimer();
            tmr.Interval = TimeSpan.FromSeconds(10);
            tmr.Tick += async (s, e) =>
            {
                await LoadBooksCollectionAsync();
            };
            tmr.Start();
        }

        private async Task LoadBooksCollectionAsync(int cnt = 1000000)
        {
            MainTitle = $"{DateTime.Now},loading from {requestUrl},cnt={cnt}";
            var resp = await client.GetBooksListAsync(new GetBooksListRequest { Cnt = cnt });
            var bksList = resp.Books.ToModelList();
            if (bksList != null && bksList.Any())
            {
                BooksCollection = new ObservableCollection<Book>(bksList);
                MainTitle = $"{DateTime.Now},loaded {BooksCollection.Count} books,FirstId:{BooksCollection.FirstOrDefault()?.Id},Last Id:{BooksCollection.LastOrDefault()?.Id}";
            }
        }

        private string mainTitle = $"{DateTime.Now},loading...";
        public string MainTitle
        {
            get
            {
                return mainTitle;
            }
            set
            {
                if (value != mainTitle)
                {
                    mainTitle = value;
                    OnPropertyChanged();
                }
            }
        }

        private ObservableCollection<Book> booksCollection;
        public ObservableCollection<Book> BooksCollection
        {
            get
            {
                return booksCollection;
            }
            set
            {
                if (value != booksCollection)
                {
                    booksCollection = value;
                    OnPropertyChanged();
                }
            }
        }

        public event PropertyChangedEventHandler? PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propName = "")
        {
            var handler = PropertyChanged;
            handler?.Invoke(this, new PropertyChangedEventArgs(propName));
        }
    }

    public static class BookMapper
    {
        public static BookProto ToProtoBook(this Book bk)
        {
            return new BookProto
            {
                Id = bk.Id,
                Name = bk.Name ?? "",
                Isbn = bk.ISBN ?? "",
                Author = bk.Author ?? "",
                Abstract = bk.Abstract ?? "",
                Comment = bk.Comment ?? "",
                Content = bk.Content ?? "",
                Summary = bk.Summary ?? "",
                Title = bk.Title ?? "",
                Topic = bk.Topic ?? ""
            };
        }

        public static Book ToModel(this BookProto pt)
        {
            return new Book
            {
                Id = pt.Id,
                Name = pt.Name,
                ISBN = pt.Isbn,
                Abstract = pt.Abstract,
                Author = pt.Author,
                Comment = pt.Comment,
                Content = pt.Content,
                Summary = pt.Summary,
                Title = pt.Title,
                Topic = pt.Topic
            };
        }

        public static List<Book> ToModelList(this RepeatedField<BookProto> list)
        {
            return list.Select(ToModel).ToList();
        }

        public static List<BookProto> ToProtoList(this List<Book> list)
        {
            return list.Select(ToProtoBook).ToList();
        }
    }



    public class Book
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public string ISBN { get; set; }
        public string Abstract { get; set; }
        public string Author { get; set; }
        public string Comment { get; set; }
        public string Content { get; set; }
        public string Summary { get; set; }
        public string Title { get; set; }
        public string Topic { get; set; }
    }
}

 

 

 

 

 

image

 

 

 

 

 

 

 

image

 

 

 

 

 

image

 

 

image

 

posted @ 2026-05-24 15:43  FredGrit  阅读(6)  评论(0)    收藏  举报