处理方法中的事件,以便在事件中完成时可以获取值,因此每次都不返回null

时间:2016-12-15 19:47:38

标签: c# .net design-patterns event-handling

我正在尝试找出处理和正确处理以下内容的最佳做法: 我有一个内联方法,在方法的过程中添加一个事件处理程序进程,但我需要弄清楚如何处理我的方法的其余部分,等待它继续之前接收数据到该变量(或者如果有更好的可能的做法)

目前我关注的方法是一个内联方法,所以它完成并尝试在分配给它之前从预期变量返回数据,因此它返回'null',因为它试图在之前返回它该事件处理程序获取数据。

请让我知道处理/完成此操作的最佳做​​法/模式。

谢谢。

[代码示例]我正在调用的方法,并且正在完成并尝试在分配之前返回'recievedMsg'变量值...

    public T Recieve<T>(string routingKey)
    {
        T recievedMsg = default(T);

        try
        {
            _channel.QueueDeclare(queue: routingKey,
                                    durable: false,
                                    exclusive: false,
                                    autoDelete: false,
                                    arguments: null);

            var consumer = new EventingBasicConsumer(_channel);

            consumer.Received += (model, ea) =>
            {
                var body = ea.Body;
                var message = Encoding.UTF8.GetString(body);

                recievedMsg = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(message);
            };

            _channel.BasicConsume(queue: routingKey,
                                    noAck: true,
                                    consumer: consumer);


        }
        catch (Exception ex)
        {
            // Error Handling
        }

        return recievedMsg;
    }

[调用方法调用]

        var obj = new testObj()
        {
            Id = 123,
            Name = "Test McTesting",
            Dexcription = "Employee of the Month"
        };


        using (var rbt= new proxyClass("localhost"))
        {
           // .Recieve() method is completing too quickly so always returns null to employee 
           var employee = rbt.Recieve<testObj>("Test McTesting");
        }

1 个答案:

答案 0 :(得分:2)

直接回答问题(关于模式);我建议使用任务异步模式(TAP)。 https://msdn.microsoft.com/en-us/library/hh873175(v=vs.110).aspx

TAP不仅可以处理成功的结果,还可以简化异常处理;使用简单的AutoResetEventManualResetEvent,这不是一件轻而易举的事。

TAP还允许您轻松执行诸如为操作添加超时或允许用户取消等操作。

    public async Task<T> Recieve<T>(string routingKey)
    {
        var tcs = new TaskCompletionSource<T>();

        _channel.QueueDeclare(queue: routingKey,
                                durable: false,
                                exclusive: false,
                                autoDelete: false,
                                arguments: null);

        var consumer = new EventingBasicConsumer(_channel);

        consumer.Received += (model, ea) =>
        {
            try
            {
                var body = ea.Body;
                var message = Encoding.UTF8.GetString(body);

                var recievedMsg = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(message);

                // Tries to set the result.  Will fail if the task has been cancelled
                tcs.TrySetResult(recievedMsg);
            }
            catch (Exception ex)
            {
                // Tries to set the exception.  Will fail if the task has been cancelled
                tcs.TrySetException(ex);
            }
        };

        _channel.BasicConsume(queue: routingKey,
                                noAck: true,
                                consumer: consumer);

        return await tcs.Task;
    }

示例用法(在async方法内)

    public async Task TestIt()
    {
        using (var rbt = new ProxyClass("localhost"))
        {
            var employee = await rbt.Recieve<TestObj>("Test McTesting");
            Console.WriteLine($"{employee.Id} / {employee.Name} / {employee.Dexcription}");
        }
    }

(奖励:超时使用示例 - 这是TAP的优势之一)

    public async Task TestItWithTimeout()
    {
        using (var rbt = new ProxyClass("localhost"))
        {
            var task = rbt.Recieve<TestObj>("Test McTesting");

            if (await Task.WhenAny(task, Task.Delay(TimeSpan.FromSeconds(30))) == task)
            {
                var employee = await task;
                Console.WriteLine($"{employee.Id} / {employee.Name} / {employee.Dexcription}");
            }
        }
    }

注意:将try..catch置于EventingBasicConsumer.Received处理程序中是个好主意,因为RabbitMq.Client的使用者线程不会处理异常 - 线程将会死掉。

如果您的Rabbit Consumer线程意外死亡而您没有处理错误,那么您的应用程序将无法知道它 - 所有后续消息只是在代理的队列中未被传递,并且随后调用您的{{1只会永远等待。