【问题标题】:How to pass extra data into a generic method in c#如何将额外的数据传递给c#中的泛型方法
【发布时间】:2014-01-14 12:11:20
【问题描述】:

我们有一个 wcf Web 服务 API,它有一些通用代码,我们将这些代码封装到一个通用方法中,以免在每个 Web 服务方法中编写相同的代码。看起来像这样:

TResult SafeMethodCall<T, TResult>(Func<T, TResult, TResult> body, T request)
        where TResult : ServiceResponse, new()
        where T : RequestBase
    {
        if (request == null)
            throw new ArgumentNullException("request");

        var response = new TResult();

        try
        {
            response = body(request, response);
        }
        catch (Exception ex)
        {
            AddServiceError(response, ex);
        }
        finally
        {
            AddAuditData(request, response);
        }

        return response;
    }

现在我正在尝试编写审计功能,并且有一个特定参数几乎总是请求或响应类的一部分,因此我可以使用反射获取此参数,以便将其记录到数据库中。

 private void AddAuditData(RequestBase request, ServiceResponse response)
    {
        string signinId = "";
        Type t = request.GetType();
        PropertyInfo info = t.GetProperty("SignInIdentifier");
        if (info != null)
        {
            signinId = info.GetValue(request).ToString();
        }

        Type r = response.GetType();
        info = r.GetProperty("SignInIdentifier");
        if (info != null)
        {
            signinId = info.GetValue(response).ToString();
        }

        //now log signinid, and method name, etc to the database
        //how do I pass signinid into this method if it isn't part of the request or response???


    }

每个 Web 服务方法都有自己版本的响应和请求类,它们继承自该方法中引用的基本类。

我的问题是,对于我无法访问要记录的参数的一两个 Web 服务方法,但我需要做一些工作来获取它,我不确定如何传递它进入泛型方法来处理它。

我可以通过使用全局变量或将其添加到响应类中来做到这一点,但是从编程风格的角度来看,这两种方法中的任何一种都显得相当劣质。

我想知道是否有人对处理此问题的“好”方式有任何其他建议?

【问题讨论】:

    标签: c# generics


    【解决方案1】:

    首先,您不应该使用反射来提取值。这可以使用通用接口很好地解决。

    现在回答你的问题。您可以将 signinId 作为可选参数传递给SafeMethodCall,这样您就不会违反现有合同。然后从那里传递给AddAuditData

    TResult SafeMethodCall<T, TResult>(Func<T, TResult, TResult> body, T request, string signinId = null)
        where TResult : ServiceResponse, new()
        where T : RequestBase
    {
            // ...
    
            AddAuditData(request, response, signinId);
    
            // ...
    }
    

    AddAuditData 中检查是否提供了signinId,如果没有,则从请求/响应中提取它:

    private void AddAuditData(RequestBase request, ServiceResponse response, string signinId)
    {
        if(signinId == null)
        {
          // extract from request or response
        }
    
        // still null? throw an exception or log error
    }
    

    或者,您可以传递一个返回 signinId 的函数或一个提供值的对象。

    【讨论】:

    • 嗨汤姆,是的,这就是我想要得到的 - 格式化方法调用以传递我需要的参数的最佳方法是什么(或者实际上是否有不同的委托结构body 方法需要)。我会看看能不能按照你的建议工作
    【解决方案2】:

    据我了解,您需要通过反射调用泛型方法。

    如果是,请使用 System.ReflectionMethodInfo.MakeGenericMethod(params type[])

    所以你必须做这样的事情

      typeof(TargetClass).GetMethod("TargetMethod").MakeGenericMethod(typeof(T1),typeof(T2)....).Invoke(obj,args);
    

    这里是相关帖子How do I use reflection to call a generic method?

    这里是关于这个主题的 msdn 页面http://msdn.microsoft.com/ru-ru/library/system.reflection.methodinfo.makegenericmethod(v=vs.110).aspx

    【讨论】:

      【解决方案3】:

      我想我不妨详细说明我的解决方案。问题实际上是我需要能够将更多数据从函数调用主体传递到我的审计过程中,而我不知道如何为通用函数执行此操作。

      所以,第一部分是意识到这一点

       Func<T, TResult, TResult> body
      

      只是一个内置的委托类型,所以我需要用我自己的版本替换它,它还包含一个输出参数。

      private delegate TResult MyFunc< T1,  T2,  T3,  TResult>(T1 arg1, T2 arg2, out T3 arg3);
      

      然后,我可以更改 SafeMethodCall 中的代码以使用此委托

      TResult SafeMethodCall<T, TResult>(MyFunc<T, TResult, string, TResult> body, T request)
              where TResult : ServiceResponse, new()
              where T : RequestBase
          {
              if (request == null)
                  throw new ArgumentNullException("request");
      
              var response = new TResult();
              string id = null;
              try
              {
                  LogServiceEntry(request);
      
                  response = body(request, response,out id); //from the delegate
              }
              catch (Exception ex)
              {
                  AddServiceError(response, ex);
              }
              finally
              {
                  AddAuditData(request, response, id);
                  LogServiceExit(response);
              }
      
              return response;
          }
      

      这意味着在我调用此函数的 Web 服务方法中,我可以编写一些代码来生成审计所需的额外参数

      public ResetPasswordResponse ResetPassword(ResetPasswordRequest resetPasswordRequest)
          {
      
              return SafeMethodCall<ResetPasswordRequest, ResetPasswordResponse>
                  ((ResetPasswordRequest request,
                      ResetPasswordResponse response, out string signInIdentifier) =>
              {
                  signInIdentifier = null;
                  if (!_validator.ValidateModel(request, response))
                      return response;
                  var signIn =
                      _manager.GetSignInByPasswordResetToken(request.PasswordResetToken);
                  if (signIn != null)
                  {
                      signInIdentifier = signIn.SignInIdentifier.ToString();              
                  }
      
                  var result = _manager.ResetPassword(request.SiteIdentifier,
                  request.PasswordResetToken, request.Password);
      
                  if (!result)
                  {
                      AddServiceError(response, "The password could not be reset",
                          ErrorType.GeneralError);
                  }
      
                  return response;
              }, resetPasswordRequest);
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-06
        • 2014-06-08
        • 1970-01-01
        • 1970-01-01
        • 2022-11-23
        相关资源
        最近更新 更多