Cysharp / MagicOnion

Unified Realtime/API framework for .NET platform and Unity.
MIT License
3.8k stars 424 forks source link

Question: ObjectDisposedException on proxy side #720

Closed NissimOha closed 2 months ago

NissimOha commented 9 months ago

Hi,

I've created a singleton proxy (without dispose option), and all works fine but once I got an 'ObjectDisposedException', I wondered if someone got this also (I never call dispose on the client), the exeption:

ERROR Host.Grpc.Base.RpcProxy.ServiceProxyBase`1[[Host.Grpc.Interfaces.Scan.IPatientRegistrationService, Host.Grpc.Interfaces, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]] - Error status: Error starting gRPC call. HttpRequestException: An error occurred while sending the request. ObjectDisposedException: Cannot access a disposed object. Object name: 'System.Net.Sockets.NetworkStream'.

Proxy code:

public abstract class ServiceProxyBase<TSelf> : IService<TSelf>, IGrpcProxyInvoker
    where TSelf : IService<TSelf>
{
    private static readonly ILogger _logger = LogManager.GetLogger(typeof(ServiceProxyBase<TSelf>));

    public TSelf Client { get; private set; }

    protected ServiceProxyBase(string address)
    {
        _ = address ?? throw new ArgumentNullException(nameof(address));

        var channel = GrpcChannel.ForAddress(address);
        Client = MagicOnionClient.Create<TSelf>(channel, MessagePackSerializerHelper.Get());
    }

    public async UnaryResult<TResult> InvokeMethod<TResult>(Func<UnaryResult<TResult>> action, ILogger logger = null, [CallerMemberName] string methodName = "")
    {
        try
        {
            logger?.MethodTrace(methodName);
            return await action().ResponseAsync.ConfigureAwait(false);
        }
        catch (RpcException error)
        {
            throw CreateServerErrorException(error);
        }
    }

    public async UnaryResult<Nil> InvokeMethod(Func<UnaryResult<Nil>> action, ILogger logger = null, [CallerMemberName] string methodName = "")
    {
        try
        {
            logger?.MethodTrace(methodName);
            return await action().ResponseAsync.ConfigureAwait(false);
        }
        catch (RpcException error)
        {
            throw CreateServerErrorException(error);
        }
    }

    private FaultException<ServerError> CreateServerErrorException(RpcException error)
    {
        (int errorNumber, string message, List<string> formatArgs) rpcException = (default, default, default);

        try
        {
            rpcException.errorNumber = error.Trailers.GetValue("error_number") == null ? ErrorCode.Server.GeneralSoftwareError : Int32.Parse(error.Trailers.GetValue("error_number"));
            rpcException.message =
                string.IsNullOrEmpty(error.Trailers.GetValue("message")) ? null : error.Trailers.GetValue("message");
            var formatArg = error.Trailers.GetValue("format_args");
            rpcException.formatArgs = string.IsNullOrEmpty(formatArg) ? new List<string>() : formatArg.Split(',').ToList();

            _logger.Error($"Error status: {error?.Status.Detail}");
        }
        catch (Exception ex)
        {
            _logger.Error($"Could not parse RPC Exception to ServerError, Message {ex.Message}, Trace: {ex.StackTrace}");
            throw new FaultException<ServerError>(new ServerError(ErrorCode.Server.GeneralSoftwareError, 
                "Could not parse RPC Exception to ServerError"));
        }

        var serverError = new FaultException<ServerError>(new ServerError(rpcException.errorNumber,
            rpcException.message, rpcException.formatArgs));

        throw serverError;
    }
github-actions[bot] commented 3 months ago

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 30 days.