重构WCF服务

时间:2012-07-10 21:28:32

标签: c# .net wcf api api-design

我最近不得不为我编写的现有应用程序添加一个新功能,并查看这部分代码,意识到可能是重构和改进的时候了。

原始索引方法:

  • IsUserEnrolled < - WCF致电
    • 如果没有注册
      • 如果用户可以注册< - WCF Call ,则运行规则
        • 如果用户无效,则重定向到“无法访问”
        • 如果用户有效注册重定向注册
    • 如果用户已注册
      • 获取商品< - WCF致电
      • 显示索引页

对后端Web服务的三次wcf调用

随着新增加,我需要一些新信息,一些用户选项。

  • IsUserEnrolled < - WCF致电
    • 如果没有注册
      • 如果用户可以注册< - WCF Call ,则运行规则
        • 如果用户无效,则重定向到“无法访问”
        • 如果用户有效注册重定向注册
    • 如果用户已注册
      • 获取商品< - WCF致电
      • 获取用户选项< - WCF Call
      • 显示索引页

此功能导致新的WCF调用,这使得这一方法通过线路进行了4次调用。必须有更好的方法。

我的建议是将其包装成一个wcf调用,以收集有关用户的所有信息,他们是否已注册,项目,运行规则(如果需要)和用户选项。

  • 获取用户信息(简称结果对象)< - WCF Call
    • 如果不是result.IsEnrolled
      • 如果result.RulesResult.UserIsValid属性为false
        • 重定向到无法访问
      • 如果该属性为真
        • 重定向注册
    • 如果result.IsEnrolled
      • 使用result.UserOptions和result.Items
      • 填充ViewModel

我们只有一个好的电话,不过我的问题

  • 结果是否有一个对象有意义?

  • 如果IsEnrolled为true,则RulesResult将为null,在此实例中是否有null属性?也许提供一个结果,也表示用户已注册,因为以后会进行检查?

  • 如果IsEnrolled为false,将填充RulesResult(有意义)但是Items将为null(有意义)用户选项也将为null 在这种情况下,项目和用户选项的空列表是否更有意义,然后为null?

  • 从api设计的角度来看,第二个选项是否有意义,或者是与UI密切相关的结果?

两个版本的代码示例:
版本1:

public ActionResult Index()
{

    using (var client =ServiceFactory.CreateChannel())
    {
        var isMemberEnrolled = client.IsMemberEnrolled(User.Identity.Name);

        if (!isMemberEnrolled)
        {
            var accessResult = client.RunRules(User.Identity.Name);

            if (!accessResult.UserIsValid)
            {
                return RedirectToAction("NoAccess");
            }
            return RedirectToAction("Register");
        }

        var userOptions = client.GetUserOptions(User.Identity.Name);

        List<Item> items = client.GetUserItems(User.Identity.Name);

        var viewModel = new ViewModel(userOptions, items);

        return View(viewModel);
    }
}

版本2(重构):

public ActionResult Index()
{

    using (var client = ServiceFactory.CreateChannel())
    {
        var userInformation = client.GetUserInformation(User.Identity.Name);

        if (!userInformation.IsMemberEnrolled)
        {
            return RedirectToAction(!userInformation.RulesResult.UserIsValid ? "NoAccess" : "Register");
        }

        var viewModel = new ViewModel(userInformation.UserOptions, userInformation.Items);

        return View(viewModel);
    }
}

1 个答案:

答案 0 :(得分:2)

我认为对于API效率,选项#2肯定会表现得更好。

只要在一个结果对象中包含所有未使用的参数,就可以使用抽象结果类轻松解决这个问题,然后将两个不同的响应分成两个不同的具体子类型。

  [KnownType( typeof( UserInfoEnrolledResult ) )]
  [KnownType( typeof( UserInfoNotEnrolledResult ) )]
  [DataContract]
  public abstract class UserInfoResult
  {
  }

  [DataContract]
  public class UserInfoEnrolledResult : UserInfoResult
  {
    [DataMember]
    public string UserOptions { get; set; }

    [DataMember]
    public string[] Items { get; set; }
  }

  [DataContract]
  class UserInfoNotEnrolledResult : UserInfoResult
  {
    [DataMember]
    public bool UserIsValid { get; set; }
  }

然后您的客户端代码将变成类似......

  using ( var client = ServiceFactory.CreateChannel() )
  {
    var userInformation = client.GetUserInformation( User.Identity.Name );

    if ( userInformation is UserInfoNotEnrolledResult )
    {
      return RedirectToAction( ((UserInfoNotEnrolledResult)userInformation).UserIsValid ? "NoAccess" : "Register" );
    }

    var enrolledUserInformation = (UserInfoEnrolledResult)userInformation;

    var viewModel = new ViewModel( enrolledUserInformation.UserOptions, enrolledUserInformation.Items );

    return View( viewModel );
  }

这使客户清楚地知道两种不同的响应是可能的,并且清楚地说明了哪种参数被用于或需要什么类型的响应。

我认为这是一个非常好的方式。如果您发现自己创建了许多不同类型的函数,而这些函数大致相似,只有轻微的差异,例如...

UserInfoResult GetUserInformation( string name );
UserInfoResult GetUserInformationWithoutRuleCheck( string name );
UserInfoResult GetUserInformationWithDoubleSecretChecks( string name );

然后,将这些较大的函数分解为多个WCF调用可能是值得的,以确保您没有大量的API方法。