【问题标题】:c# func parameter not being usedc# func参数没有被使用
【发布时间】:2016-11-03 13:33:07
【问题描述】:

我有一个记录服务输入/输出的旧版应用程序。目前,每个方法都有相同的行来记录请求和响应对象。我想使用 AOP,但不添加任何额外的工具(Postsharp、Castle 等),或者将每个服务类包装到另一个类(ServiceWrapper)中。

为了做到这一点,我正在尝试创建一个知道它应该记录请求和响应对象的通用类。这是我正在尝试的:

using System;

namespace ProxyTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var request = "request";
            var fooService = new FooService();

            ServiceProxy.Invoke(r => fooService.DoFoo(request), "abc");

            Console.Read();
        }
    }

    class ServiceProxy
    {
        public static void Invoke(Func<object, object> service, object request)
        {
            Console.WriteLine("input:" + request);

            var response = service(request);

            Console.WriteLine("output:" + response);
        }
    }

    class FooService
    {
        public string DoFoo(object a)
        {
            return a +  ": returning: Do Foo";
        }
    }

}

虽然它可以工作,但“abc”字符串只是用于编译应用程序,而不是用作请求参数。如果我删除它,代码将无法编译。我错过了什么吗?

更新

改成以下就行了:

static void Main(string[] args)
    {
        var request = "request";
        var fooService = new FooService();

        ServiceProxy.Invoke(r => fooService.DoFoo(r), request);

        Console.Read();
    }

【问题讨论】:

  • 您可以访问服务源吗?是 WCF 服务吗?
  • “但它没有被用作请求参数” - 目前在 ServiceProxy.Invoke 中。我不清楚你在这里问什么......
  • 为什么不这样称呼它ServiceProxy.Invoke(r =&gt; fooService.DoFoo(r), request); 或者只是ServiceProxy.Invoke(fooService.DoFoo, request);
  • ServiceProxy.Invoke(r =&gt; fooService.DoFoo(request), "abc"); fooService.DoFoo()是否返回Func&lt;object, object&gt;()?你的意思可能是:ServiceProxy.Invoke(r =&gt; fooService.DoFoo, request); (DoFoo without the parameter)
  • 如果您的方法是通用的并且实际上返回了响应public static TResponse Invoke&lt;TRequest, TResponse&gt;(Func&lt;TRequest, TResponse&gt; service, TRequest request),这似乎更有意义。那么你的DoFoo 可以使用string 而不是object

标签: c# design-patterns delegates aop func


【解决方案1】:

你应该这样称呼它:

class Program
{
    static void Main(string[] args)
    {
        var request = "request";
        var fooService = new FooService();

        ServiceProxy.Invoke(fooService.DoFoo, "abc"); // lose the DoFoo parameter.

        Console.Read();
    }
}

您应该将 DoFoo 作为 Func 传递,而不是调用它。此外,您应该将方法签名更改为:

class FooService
{
    public object DoFoo(object a)
    {
        return a +  ": returning: Do Foo";
    }
}

【讨论】:

  • 解释-1?
  • 你的意思是ServiceProxy.Invoke(fooService.DoFoo, "abc");,因为你所拥有的不会编译。
  • @juharr 谢谢,没注意到。修复了
  • 不是DoFoo 需要更改,而是ServiceProxy.Invoke 需要更改为@HimBromBreere 所说的通用签名。否则我同意你的看法。
【解决方案2】:

对于此任务,您只需在调度程序上添加日志记录行为。 首先,您使用此类内容创建 ServiceBehavior:

public class ServiceLoggingBehavior : Attribute, IServiceBehavior
{
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints)
        {
            foreach (OperationDescription operation in endpoint.Contract.Operations)
            {
                IOperationBehavior behavior = new LoggingOperationBehavior();
                operation.Behaviors.Add(behavior);
            }
        }
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }
}

然后你需要创建操作行为:

internal class LoggingOperationBehavior : IOperationBehavior
{
    public void Validate(OperationDescription operationDescription)
    {

    }

    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
    {
        dispatchOperation.Invoker = new LoggingOperationInvoker(dispatchOperation.Invoker, dispatchOperation);
    }

    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
    {

    }

    public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
    {
    }
}

最后为服务器端的所有方法创建调用程序:

internal class LoggingOperationInvoker : IOperationInvoker
{
    private readonly IOperationInvoker _baseInvoker;
    private readonly string _operationName;

    public LoggingOperationInvoker(IOperationInvoker baseInvoker, DispatchOperation operation)
    {
        _baseInvoker = baseInvoker;
        _operationName = operation.Name;
    }

    public bool IsSynchronous
    {
        get { return _baseInvoker.IsSynchronous; }
    }

    public object[] AllocateInputs()
    {
        return _baseInvoker.AllocateInputs();
    }

    public object Invoke(object instance, object[] inputs, out object[] outputs)
    {
        var sw = new Stopwatch();
        try
        {
            LogBegin();
            sw.Start();
            var response = _baseInvoker.Invoke(instance, inputs, out outputs);
            return response;
        }
        finally
        {
            sw.Stop();
            LogEnd(sw.Elapsed);
        }
    }

    private void LogBegin()
    {
        //you can log begin here.
    }

    private void LogEnd(TimeSpan elapsed)
    {
        //you can log end here.
    }

    public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
    {
        return _baseInvoker.InvokeBegin(instance, inputs, callback, state);
    }

    public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
    {
        return _baseInvoker.InvokeEnd(instance, out outputs, result);
    }
}

如果你想记录请求,你可以在 Invoke 方法中序列化和记录 inputs 变量。对于响应 - 只需序列化并记录 response 变量。

最后,最有趣的部分,只需像属性一样附加它:

[ServiceLoggingBehavior]
public MyService : IMyServiceContract
{
    ...
}

【讨论】:

    【解决方案3】:

    您的Invoke-方法明确要求Func- 和object-参数,因此您必须同时提供这两个参数。当您省略其中一个参数时,不知道您到底在期待什么。我假设您想让Func 返回由特定request 对象创建的响应。此外,让您的请求和响应参数通用化可能是个好主意:

    class Program
    {
        static void Main(string[] args)
        {
            var request = "request";
            var fooService = new FooService();
    
            ServiceProxy.Invoke(r => fooService.DoFoo(r), request);
    
            Console.Read();
        }
    }
    
    class ServiceProxy
    {
        public static void Invoke<TRequest, TResponse>(Func<TRequest, TResponse> service, TRequest request)
        {
            Console.WriteLine("input:" + request.ToString());
    
            var response = service(request);
    
            Console.WriteLine("output:" + response.ToString());
        }
    }
    

    Invoke-call 可以进一步简化为ServiceProxy.Invoke(fooService.DoFoo, request);

    【讨论】:

    • 您需要在方法或类上定义泛型类型才能使其工作。
    • 我需要的是消除在 DoFoo 中传递请求对象以及将相同参数传递给 Func 的需要。
    【解决方案4】:

    感谢所有回复。我能够实现我正在寻找的东西:

        static void Main(string[] args)
        {
            var request = "request";
            var fooService = new FooService();
    
            ServiceProxy.Invoke(fooService.DoFoo, request);
    
            Console.Read();
        }
    

    static void Main(string[] args)
        {
            var request = "request";
            var fooService = new FooService();
    
            ServiceProxy.Invoke(r => fooService.DoFoo(r), request);
    
            Console.Read();
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-03
      • 1970-01-01
      • 1970-01-01
      • 2016-11-19
      • 1970-01-01
      • 1970-01-01
      • 2016-09-25
      相关资源
      最近更新 更多