Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document Pipe Usage #4

Open
zachrybaker opened this issue Jun 22, 2023 · 8 comments
Open

Document Pipe Usage #4

zachrybaker opened this issue Jun 22, 2023 · 8 comments

Comments

@zachrybaker
Copy link

@mgravell The "how to use it" section could IMO use a little hand-holding for folks like myself who are trying to use the pipeline version.

In my case I have a large old net472 webforms app that I uses hundreds of bunch of ObjectDataSource DataObjects that are generated code. I'm creating a shim across IPC named pipe to a net6.0 server process that I want to actually execute the data retrieval from. This net6.0 process is just a console app and will listen on a pipe. I was able to PoC it with GrpcDotNetNamedPipes but I thought I'd do the same with your package so that I can avoid writing codegen to create proto files (that would cause codegen... :)) It's codegen all the way down.

Anyway, being the non-async, ObjectDataSource-oriented .Net 4.72 webforms app that 2006 would be proud of, I'm struggling with connecting the dots on the client side. I stumbled upon ConnectionFactory, but it doesn't get me far as it never completes.

liteChannel = ConnectionFactory.ConnectNamedPipe("LITE_PIPE", ".")
    .AsFrames().CreateChannelAsync()
    .GetAwaiter().GetResult();

Similar question on the server side. Perhaps once I get the client happy, I'll see that this actually works?

_liteServer = new LiteServer();
_liteChannel =  _liteServer.CreateLocalClient("LITE_PIPE");
_liteServer.ServiceBinder.AddCodeFirst(serviceA);
_liteServer.ServiceBinder.AddCodeFirst(serviceB);
@mgravell
Copy link
Member

Your server needs to use one of the "listen" APIs to accept clients - the CreateLocalClient method does something very different: it returns a client that has direct in-process access to the server.

@zachrybaker
Copy link
Author

Thanks @mgravell
With that hint, I have a client connecting to a server, sending a request, server processes request and sends response...which never materializes back on the client.

My suspicion is that there's something I need to do with the response data contract to deal with the base list class - is that even supported?

 [DataContract]
    public class FamisUserIdsResp
    {
        
        [DataMember(Order = 1)]
        public int ErrNbr { get; set; }
        [DataMember(Order = 2)]
        public string ErrMsg { get; set; }
        [DataMember(Order = 3)]
        public string Id { get; set; }
    }

    [DataContract]
    public class GetFamisUserIdsCollectionResp : List<FamisUserIdsResp>
    {
        public GetFamisUserIdsCollectionResp() { }
        public GetFamisUserIdsCollectionResp(int count) : base(count) { }
        [DataMember(Order = 1)]
        public string uin { get; set; }
        [DataMember(Order = 2)]
        public string sortExpression { get; set; }
    }

@mgravell
Copy link
Member

mgravell commented Jun 22, 2023 via email

@zachrybaker
Copy link
Author

Got it, thanks for confirming that!

@zachrybaker
Copy link
Author

zachrybaker commented Jun 23, 2023

@mgravell if I can ask one other question. I'm trying both the protobuf-net and the grpclite attributes trying to get this response shape read at the client, still not there. Do you see what I'm missing here? I saw SO articles of more complex cases (generics, or inheritance) but seems to me this should just work...

    [ProtoContract]
    public class GetFamisUserIdsRqst
    {
        [ProtoMember(1)] public string Uin { get; set; }
    }

    [ProtoContract]
    public class GetFamisUserIdsCollectionResp
    {
        [ProtoMember(1, IsRequired =true)] public List<GetFamisUserIdsResp> Records { get; set; } = new List<GetFamisUserIdsResp>();
    }
    
    
    [ProtoContract]
    public class GetFamisUserIdsResp
    {
        [ProtoMember(1)] public int    ErrNbr { get; set; }
        [ProtoMember(2)] public string ErrMsg { get; set; }
        [ProtoMember(3)] public string Id     { get; set; }
    }

service interface and implementation:

   [Service]
    public interface IGetFamisUserIds
    {
        [Operation]
        Task<GetFamisUserIdsCollectionResp> GetFamisUserIdsCollection(GetFamisUserIdsRqst request);
    }

    public class GetFamisUserIds : IGetFamisUserIds
    {
        public Task<GetFamisUserIdsCollectionResp> GetFamisUserIdsCollection(GetFamisUserIdsRqst request)
        {
            var dataObject = new FamisUserIdsCollection();
            dataObject.Fill(
              
                request.Uin
            );
            var resp = new GetFamisUserIdsCollectionResp();
            foreach(var item in dataObject)
            {
                resp.Records.Add(new GetFamisUserIdsResp()
                {
                    ErrNbr = 1,//item.ErrNbr,
                    ErrMsg = "-",//item.ErrMsg,
                    Id     = item.Id    
                }
                );
            }
            
            return Task.FromResult(resp);
        }
    } 

client caller:

    public static async Task<GetFamisUserIdsCollectionResp> GetFamisUserIdsCollection(GetFamisUserIdsRqst request)
    {
            var svc = liteChannel.CreateGrpcService<IGetFamisUserIds>();
            var result = await svc.GetFamisUserIdsCollection(request);
            return result;
     } 

@zachrybaker zachrybaker reopened this Jun 23, 2023
@zachrybaker
Copy link
Author

zachrybaker commented Jun 23, 2023

@mgravell or anyone able to tell me if I'm missing something here for the below working service (client gets a response) vs above (client is sent a response but never reads it off the pipe).

Both services are wired the same. The test request, response (list), and service interface and implementation almost identically similar in the same app, and that call DOES return the result.

    [ProtoContract]
    public class GetTestRqst
    {
        [ProtoMember(1)] public string Name { get; set; }
    }

    [ProtoContract]
    public class GetTestResp
    {
        [ProtoMember(1, IsRequired =true)] public List<GetTestRespRow> Responses { get; set; } = new List<GetTestRespRow>();
    }
     [ProtoContract]
    public class GetTestRespRow
    {
        [ProtoMember(1)] public string Sentence { get; set; }
        [ProtoMember(2)] public int Status { get;set; }
    }

    [Service]
    public interface ITestService
    {
        [Operation]
        Task<GetTestResp> GetTestResult(GetTestRqst request);
    }
    
    public class TestService : ITestService
    {
        public Task<GetTestResp> GetTestResult(GetTestRqst request)
        {
            var result =  new GetTestResp();
            result.Responses.Add(new GetTestRespRow()
            {
                Status = 1,
                Sentence= string.Format("{0}, huh? Hi {0}, nice to meet you. I'm server.", request.Name)
            });
            result.Responses.Add(new GetTestRespRow()
            {
                Status = 2,
                Sentence= string.Format("{0}, I can't really hear you. As I said, I'm a server.", request.Name)
            });
                     
            return Task.FromResult(result);
        }
    }
    
    public static async Task<GetTestResp> GetTestResult()
        {
            var rqst = new GetTestRqst() {  Name = "Zach"};
            var svc = liteChannel.CreateGrpcService<ITestService>();
            var result = await svc.GetTestResult(rqst);
            return result;
        }
    

@zachrybaker
Copy link
Author

Answering my own question....if I call the failing one first thing...it works. So something falls over after its been running a bit. I did have GrpcDotNetNamedPipes also in play and thought that could be an issue, but now I'm thinking I'm running into this known gap: "per-stream service activation (rather than singleton)"

@mgravell
Copy link
Member

Honestly, I'd blame grpclite here; it is experimental, and now that IIRC named pipe support is being added to core gRPC (at least in .net), it may have outlived it's usefulness. It may be advisable to look into the named pipe gRPC support, using protobuf-net.Grpc but not GrpcLite

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants