2

I'm trying using gRPC dynamically typed values but with the little information about their usefulness, It's almost impossible to do this... So I will show the image/code that I have problems and the questions that are eating my brain

HelpExample

gRPC Method I'm doing:

public override Task<HelloReply2> TestObject(Status request, ServerCallContext context) {

    
    //The part I may have problems
  
    var status = new Status();
     
    //here I want a User that corresponds to my request.Data

    //example -> request.Data = User1 (gives me null if User1 don`t exist in db)             
    //           request.Data = 14 (gives me null if 14 don`t exist in db)
    //           request.Data = true (gives me null if true don`t exist in db)
       
    var a1 = _context.Users_5.FirstOrDefault(x => x.Username.Equals(request.Data));
    var b1 = _context.Users_5.FirstOrDefault(x => x.Email.Equals(request.Data));
    var c1 = _context.Users_5.FirstOrDefault(x => x.Age.Equals(request.Data));
    var d1 = _context.Users_5.FirstOrDefault(x => x.Test.Equals(request.Data));
                                                    //is a bool
    
    //here i want too Create dynamic values
    status.Data = Value.ForStruct(new Struct { 
    
        Fields =
        {
            ["Integer"] = Value.ForNumber(c1!.Age),
            ["StringName"] =  Value.ForString(a1!.Username),
            ["StringEmail"] =  Value.ForString(b1!.Email),
            ["Boolean"] = Value.ForBool(d1!.Test)
        }
    
    });
    
    //Below is just a simple string who gives different string (depending on the 
    //data Status (also how to read the message from the status.Data ?)
    HelloReply2 hello = new();

    if(a1 != null)
    {
        hello.Message = "There is a User with the Username " + request.Data + ". His Email is " + a1.Email;
    } else if (b1 != null) {
        hello.Message = "There is a User with the Email " + request.Data + ". His Username is " + b1.Username;
    }
    else if (c1 != null)
    {
        hello.Message = "There is at least one User with that Age of " + request.Data + ". His Username is " + c1.Username;
    }
    else if (d1 != null)
    {
        if(d1.Test == true)
        {
            hello.Message = "There is at least one User who dislikes chocolate: " + request.Data + ". His Username is " + d1.Username;
        } else
        {
            hello.Message = hello.Message = "There is at least one User who likes chocolate: " + request.Data + ". His Username is " + d1.Username;
        }
    }
    else
    {
        hello.Message = "We didn't find something with the value that the User put in. Value:" + request.Data;
    }

    return Task.FromResult(hello);
}

Questions: How to Get the one Value from my gRPC? How to convert a "Object" in c# (one string, one integer or one List) into a ONE value of google.protobuf.Value (so it not give me errors like this Controller from a Web Api below)? Is something wrong with my gRPC Service Method (is something wrong reading the dynamic values? Can I do that calls for getting a User for a DB? How to read dynamic values?)

// I try using Google.Protobuf.WellKnownTypes.Value obj but 
//not workings because gives me a lot of values to put
[HttpGet("TypeObject/{obj}")]
public async Task<ActionResult<HelloReply2>> TypeObject([FromRoute] Object obj){
                                              
    Status objRequest = new Status { Data = (Google.Protobuf.WellKnownTypes.Value) 
    obj }; 
    //cannot do this (gives me error of casting but the ideia is doing something 
    //similar to this)

    var hello = await _greetClient.TestObjectAsync(objRequest);

    return Ok(hello);
}

Any help on how to resolve this error of using Value gRPC or if is something wrong with the code is always welcome.

Edit:

One day after this question I don't have any solutions / progress. I was think of doing Any or OneOf for testing but it also gives me errors (who don't make sense at all). This code from Microsoft (C# Format part is not recognize) doesn't work in my project with the protos reload (the problem is not in the protos)

Link: https://learn.microsoft.com/en-us/dotnet/architecture/grpc-for-wcf-developers/protobuf-any-oneof

How I can use Any / OneOf without give me error in the Formating? What is the difference between Value and this two? Can the three (Value, Any, OneOf) be dynamic/Object values (if yes how to convert the types)?

Edit 2:

Still have problems, I'm trying using gRPC Any , and maybe have some progress (not all).

So with Any I have my method in the server gRPC and it is like this

public override Task<HelloReply2> TestObject3(AnyMessage request, ServerCallContext context){
    HelloReply2 anyMessageResponse;

    var y = request.TypeUrl;
            
       switch (request.TypeUrl)
       {
          case "type.googleapis.com/any.HelloRequest":
            var string_1 = request.Unpack<HelloRequest>();
            anyMessageResponse = new HelloReply2{
             Message = "You type String: " + $"{string_1.Name}"
            };
            break;
          case "type.googleapis.com/any.TestInteger1":
            var integer_1 = request.Unpack<TestInteger1>();
            anyMessageResponse = new HelloReply2{
             Message = "You type Integer: " + $"{integer_1.Message}"
            };
            break;
          case "type.googleapis.com/any.TestBool1":
            var bool_1 = request.Unpack<TestInteger1>();
            anyMessageResponse = new HelloReply2{
             Message = "You type Bool: " + $"{bool_1.Message}"
            };
            break;
          default:
            throw new InvalidOperationException("Unexpected type URL.");}
            
        
    return Task.FromResult(anyMessageResponse);
}


This ideia comes from here (https://github.com/grpc/grpc-dotnet/issues/917), but the client part their don't have any much info or I don't understand that part

This is what I did in the WebApi (who is my client and the code is similar to the above one)

using AnyMessage = Google.Protobuf.WellKnownTypes.Any;

[HttpGet("TypeObject3/{obj3}")]
        public async Task<ActionResult<HelloReply2>> TypeObject3([FromRoute] string obj3)
        {

            AnyMessage objRequest = new() { TypeUrl = obj3 };
            
            var hello = await _greetClient.TestObject3Async(objRequest);

            var l = hello.Message;

            return Ok(hello);
        }

First I had the variable Any declared in the method instead of string but as you can only put string and stringBytes so I preferred to put it like this (with the string as an obj3 variable) but my goal is to see if the variable is of type TestBool1 or TestInteger1 as I have declared in the protos and not be a string that I will be able to see, and the biggest problem was if I had more variables inside the messages how to proceed? So my secondary question is how to use Any on the client side via the Web-Api? I forgot to say but I'm using .Net 6 Core and for testing I'm using Swagger, where at this moment my error hits the Exception dictated by the Server method.

Questions: Why TypeUrl is a string and not object? How to fix my problem? How to test the object type (or string) for more values if the messages was with 1 more types?

Also I will show my test proto too show how I'm doing this

import "google/protobuf/struct.proto";
import "google/protobuf/any.proto";

package greet;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayNormalHello (Empty_2)  returns (HelloReply);
  rpc SayHello (HelloRequest) returns (HelloReply2);
  rpc TestInt (TestInteger1) returns (HelloReply2);
  rpc TestBoolean (TestBool1) returns (HelloReply2);
  rpc TestObject (Status) returns (HelloReply2); //Not working
  rpc TestObject2 (Status2) returns (HelloReply2); //Not working
  rpc TestObject3 (google.protobuf.Any) returns (HelloReply2); //Also 
                                                          //Not working

}

message Empty_2{

}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings.
message HelloReply {
  string message = 1;
}

// The response message containing the greetings.
message HelloReply2 {
  string message = 1;
}

message TestInteger1 {
  int32 message = 1;
}

message TestBool1 {
  bool message = 1;
}

message Status {
  google.protobuf.Value data = 1;
}

message Status2 {
  google.protobuf.Any data = 1;
}

Any help is welcome.

José Leal
  • 75
  • 11
  • You might want to make the question clearer, I'm willing to help but I don't even know where to start here – Clément Jean Jun 19 '22 at 08:26
  • Simply put, I need to have a dynamic object in gRPC and this object will be consumed by a web api. Now the question is how to do this (if it's a string, if it's bytes, if it's Any or OneOf) because there are no dynamic objects in gRPC easily, and the documents that exist don't have enough content to explain this – José Leal Jun 20 '22 at 07:47
  • Did you check the Well Known type called `Struct` ? – Clément Jean Jun 20 '22 at 07:49
  • I check, if you check here https://learn.microsoft.com/en-us/aspnet/core/grpc/protobuf?view=aspnetcore-6.0, they give a example of ```Value``` using a ```Struct``` but the example they give is not the best, especially in the part to read the dynamic values – José Leal Jun 20 '22 at 08:00
  • what do you expect to have for reading values? a simple property access like`obj.property`? – Clément Jean Jun 20 '22 at 08:45
  • Yes, something that acess the obj and can read normally (if the object is a string or a int or a boolean or ...), something that will connect to a string a print the value of the object, because i wanna start simple, because in the future I wanna create new values using obj to the database, but first I wanted to do something simpler like a simple "Hello World" program that prints the value of the object – José Leal Jun 20 '22 at 08:55
  • If you go with a dynamic object, I don't think there is any way to have a simple obj.property way of accessing the data. I think you can probably map your `Struct` to something like [this](https://learn.microsoft.com/en-us/dotnet/api/system.dynamic.dynamicobject?view=net-6.0) – Clément Jean Jun 20 '22 at 09:31
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/245746/discussion-between-jose-leal-and-clement-jean). – José Leal Jun 20 '22 at 09:40
  • I'm pretty new to C# and .NET, but what about using the built-in type `dynamic`? https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/reference-types#the-dynamic-type – carloswm85 Jun 21 '22 at 11:29
  • I'm using that, the ```Value``` in the Status message is the same as a dynamic. My first idea will be always the dynamic thing but my problem is using that in the services, how to read the dynamic value in gRPC? I need to create a own class for that? I should have been more explicit about what Value was, and also I did a ```Struct``` in the code to create the values, now the problem is more how to read that values? something that Microsoft doesn't explain well in the link above. Also thanks for the answer for someone to is new to C# and .NET (in this case is more like .NET Core) – José Leal Jun 21 '22 at 11:56

1 Answers1

0

In the project I developed, I needed an infrastructure where I could perform dynamic operations such as REST service using gRPC. As I understand it, what you want is something similar to this.

I have developed a solution for this. It might work for you too.

You can gain some flexibility by making a definition with a single String field for Requests and Responses that you want to be Dynamic, and then using this field to hold JSON data as strings.

For example, instead of defining different responses for different types, you can solve it by making one definition like this.

message HelloReply {
      // Stringified JSON Data   
      string data = 1;
}