我有一个记录服务输入/输出的遗留应用程序。目前,每个方法都有相同的行来记录请求和响应对象。我想使用AOP,但不添加任何额外的工具(Postsharp,Castle等),或将每个服务类包装到另一个类(ServiceWrapper)。
为了做到这一点,我试图创建一个Generic类,它知道它应该记录请求和响应对象。这是我正在尝试的内容:
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";
}
}
}
虽然它在工作,但是&#34; abc&#34; string只是编译应用程序,但它没有被用作请求参数。如果我删除它,代码不会编译。我错过了什么吗?
更新
更改为以下功能:
static void Main(string[] args)
{
var request = "request";
var fooService = new FooService();
ServiceProxy.Invoke(r => fooService.DoFoo(r), request);
Console.Read();
}
答案 0 :(得分: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 :(得分:1)
对于此任务,您只需在调度程序上添加日志记录行为即可。 首先,使用以下内容创建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方法中序列化并记录输入变量。对于响应 - 只需序列化并记录响应变量。
最终,最愉快的部分,只需附加属性:
[ServiceLoggingBehavior]
public MyService : IMyServiceContract
{
...
}
答案 2 :(得分:1)
您的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
- 调用可以进一步简化为ServiceProxy.Invoke(fooService.DoFoo, request);
答案 3 :(得分:0)
感谢所有回复。我能够实现我正在寻找的东西:
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();
}