Implement gRPC client

This commit is contained in:
Serghei Cebotari 2023-10-27 23:36:53 +03:00
parent d999a0b98a
commit b5f70ac8d4
6 changed files with 68 additions and 114 deletions

View File

@ -1,5 +1,6 @@
using System.Net; using System.Net;
using ExcelDna.IntelliSense; using ExcelDna.IntelliSense;
using Grpc.Core;
#if !NET472 #if !NET472
using System.Runtime.Versioning; using System.Runtime.Versioning;
#endif #endif
@ -14,16 +15,19 @@ public sealed class RhSolutionsAddIn : IExcelAddIn
public static Application Excel { get; private set; } public static Application Excel { get; private set; }
public static ServiceProvider ServiceProvider { get; private set; } public static ServiceProvider ServiceProvider { get; private set; }
public static IAddInConfiguration Configuration { get; private set; } public static IAddInConfiguration Configuration { get; private set; }
public static ProductSearch.ProductSearchClient GrpcClient { get; private set; }
public void AutoOpen() public void AutoOpen()
{ {
IServiceCollection Services = new ServiceCollection(); IServiceCollection Services = new ServiceCollection();
var grpcChannel = new Channel(@"rh2.cebotari.ru", ChannelCredentials.SecureSsl);
GrpcClient = new ProductSearch.ProductSearchClient(grpcChannel);
Services.AddHttpClient() Services.AddHttpClient()
.AddMemoryCache() .AddMemoryCache()
.AddSingleton((Application)ExcelDnaUtil.Application) .AddSingleton((Application)ExcelDnaUtil.Application)
.AddSingleton<IAddInConfiguration, AddInConfiguration>() .AddSingleton<IAddInConfiguration, AddInConfiguration>()
.AddSingleton<IDatabaseClient, DatabaseClient>() .AddSingleton<IDatabaseClient, GrpcClient>()
.AddSingleton<ICurrencyClient, CurrencyClient>() .AddSingleton<ICurrencyClient, CurrencyClient>()
.AddSingleton<ISleevesCalculator, SleevesCalculator>() .AddSingleton<ISleevesCalculator, SleevesCalculator>()
.AddTransient<IFileDialog, FileDialog>(); .AddTransient<IFileDialog, FileDialog>();

View File

@ -0,0 +1,17 @@
syntax = "proto3";
option csharp_namespace = "RhSolutions.Services";
service ProductSearch {
rpc GetProduct (ProductRequest) returns (ProductReply);
}
message ProductRequest {
string query = 1;
}
message ProductReply {
string id = 1;
string name = 2;
double price = 3;
}

View File

@ -32,6 +32,12 @@
<PackageReference Include="ExcelDna.Integration" Version="1.6.0" /> <PackageReference Include="ExcelDna.Integration" Version="1.6.0" />
<PackageReference Include="ExcelDna.IntelliSense" Version="1.6.0" /> <PackageReference Include="ExcelDna.IntelliSense" Version="1.6.0" />
<PackageReference Include="ExcelDna.Interop" Version="15.0.1" /> <PackageReference Include="ExcelDna.Interop" Version="15.0.1" />
<PackageReference Include="Google.Protobuf" Version="3.24.4" />
<PackageReference Include="Grpc.Core" Version="2.46.6" />
<PackageReference Include="Grpc.Tools" Version="2.59.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="7.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="7.0.0" />
<PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.1" /> <PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.1" />
@ -44,4 +50,7 @@
<PackageReference Include="System.Buffers" Version="4.5.1" /> <PackageReference Include="System.Buffers" Version="4.5.1" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" /> <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Protobuf Include="Protos\product.proto" GrpcServices="Client" />
</ItemGroup>
</Project> </Project>

View File

@ -1,96 +0,0 @@
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json;
using System.Web;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Extensions;
namespace RhSolutions.Services;
public class DatabaseClient : IDatabaseClient
{
private readonly HttpClient _httpClient;
private readonly IMemoryCache _memoryCache;
public HttpStatusCode StatusCode { get; private set; }
public DatabaseClient(HttpClient httpClient, IMemoryCache memoryCache)
{
_httpClient = httpClient;
_memoryCache = memoryCache;
}
public async Task<IEnumerable<Product>> GetProducts(string line)
{
if (ProductSku.TryParse(line, out var skus))
{
ProductSku sku = skus.FirstOrDefault();
string request = @"https://rh.cebotari.ru/api/products/" + sku.ToString();
if (!_memoryCache.TryGetValue(sku, out IEnumerable<Product> products))
{
var response = await _httpClient.GetAsync(request);
try
{
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
products = JsonConvert.DeserializeObject<IEnumerable<Product>>(json) ?? Enumerable.Empty<Product>();
}
catch
{
StatusCode = response.StatusCode;
return Enumerable.Empty<Product>();
}
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromHours(1));
_memoryCache.Set(sku, products, cacheEntryOptions);
return products;
}
else
{
return products;
}
}
else
{
QueryBuilder qb = new()
{
{"query", line}
};
string request = @"https://rh.cebotari.ru/api/search" + qb.ToQueryString();
if (!_memoryCache.TryGetValue(line, out IEnumerable<Product> products))
{
var response = await _httpClient.GetAsync(request);
try
{
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
products = JsonConvert.DeserializeObject<IEnumerable<Product>>(json) ?? Enumerable.Empty<Product>();
}
catch
{
StatusCode = response.StatusCode;
return Enumerable.Empty<Product>();
}
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromHours(1));
_memoryCache.Set(line, products, cacheEntryOptions);
if (products.Any())
{
_memoryCache.Set(products.First(), products, cacheEntryOptions);
}
return products;
}
else
{
return products;
}
}
}
}

View File

@ -0,0 +1,13 @@
using System.Threading.Tasks;
namespace RhSolutions.Services;
public class GrpcClient : IDatabaseClient
{
public async Task<IEnumerable<Product>> GetProducts(string query)
{
var reply = await RhSolutionsAddIn.GrpcClient
.GetProductAsync(new ProductRequest() { Query = query });
return new[] { new Product(reply.Id) { Name = reply.Name, Price = (decimal)reply.Price } };
}
}

View File

@ -1,19 +1,26 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<runtime> <runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Text.Encoding.CodePages" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Text.Encoding.CodePages" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly> </dependentAssembly>
</assemblyBinding> <dependentAssembly>
</runtime> <assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" /></startup></configuration> <bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
</configuration>