【问题标题】:Model - View - ViewModel & WCF - is WCF the model?模型 - 视图 - ViewModel & WCF - WCF 是模型吗?
【发布时间】:2009-01-07 15:27:17
【问题描述】:

我只是在学习 Model/View/ViewModel 模式及其变体(DataModel/View/ViewModel,或 Model/View/Presenter)。

我想知道的是:如果我将此模式与 WCF 服务一起使用,该服务是模型(DataModel),还是我需要一个单独的模型来封装 WCF 服务层??

当我使用 WCF 作为 DataModel 时,如果不模拟整个 WCF 服务,我的 ViewModel 是不可测试的,因为对 WCF 的调用需要管理连接。此 ViewModel 中的调用如下所示:

List<Sam.Alyza.WcfInterface.Website> rc = null;
Service<Sam.Alyza.WcfInterface.IServiceWebsites>.Use(alyzaSvc =>
{
  rc = new List<Sam.Alyza.WcfInterface.Website>(alyzaSvc.GetSites());
});

为了让我的 ViewModel 可测试,我尝试添加一个单独的 DataModel 来抽象 WCF 连接。在此之后 ViewModel 是可测试的,调用看起来像这样:

List<Sam.Alyza.WcfInterface.Website> rc = new List<Sam.Alyza.WcfInterface.Website>(_datamodel.GetSites());

问题:现在需要测试的大部分代码都移到了 DataModel 中,这同样需要测试 WCF。 ViewModel 中只剩下一个可以测试的薄壳。但是由于主要代码移到了 DataModel 中,所以测试 ViewModel 就没什么用了。

所以对我来说,使用 WCF 向 View / ViewModel 应用程序添加单独的 DataModel 层似乎确实增加了很多工作,但可测试性并没有变得更好。

【问题讨论】:

    标签: wcf mvvm stress-testing


    【解决方案1】:

    Woot,在解决这个问题两天后,我找到了一个我可以接受的解决方案:

    如上面的代码示例所示,我使用这个帮助程序类来管理我的 WCF 连接(因为正确处理了 Close vs Abort):

    public delegate void UseServiceDelegate<T>(T proxy);
    
    public static class Service<T>
    {
      public static ChannelFactory<T> _channelFactory;
    
      public static void Use(UseServiceDelegate<T> codeBlock)
      {
        if (_channelFactory == null)
          _channelFactory = new ChannelFactory<T>("AlyzaServiceEndpoint");
        IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel();
        bool success = false;
        try
        {
          codeBlock((T)proxy);
          proxy.Close();
          success = true;
        }
        finally
        {
          if (!success)
          {
            proxy.Abort();
          }
        }
      }
    }
    

    正如我的问题所见,这是我的 ViewModel 中使用此类的方式:

    Service<Sam.Alyza.WcfInterface.IServiceWebsites>.Use(alyzaSvc =>
    {
      rc = new List<Sam.Alyza.WcfInterface.Website>(alyzaSvc.GetSites());
    });
    

    要模拟 WCF 接口,我需要创建和托管模拟 WCF 服务,更改所有连接字符串。很多工作只是为了添加一些测试。

    我找到了一个更简单的方法: 创建一个实现接口的模拟服务很简单:

    public class MockWebsiteService : WcfInterface.IServiceWebsites
    {
      internal List<Sam.Alyza.WcfInterface.Website> _websites = new List<Sam.Alyza.WcfInterface.Website>();
      internal int _GetSitesCallCount;
    
      IEnumerable<Sam.Alyza.WcfInterface.Website> Sam.Alyza.WcfInterface.IServiceWebsites.GetSites()
      {
        _GetSitesCallCount++;
        return _websites;
      }
    }
    

    唯一的问题是:如何让 ViewModel 调用这个模拟类而不是服务?
    解决方案:Service.Use() 确实管理连接。通过添加覆盖连接管理的功能,我可以将自己的 WCF 模拟对象潜入 Service.Use()。
    为此,我需要一种方法让 Service.Use() 调用 WCF 以外的东西(查看#DEBUG 部分):

    public static class Service<T>
    {
    #if DEBUG
      public static T DebugOverride = default(T);
    #endif
      public static ChannelFactory<T> _channelFactory;
    
      public static void Use(UseServiceDelegate<T> codeBlock)
      {
    #if DEBUG
        if (!Object.Equals(DebugOverride, default(T)))
        {
          codeBlock(DebugOverride);
          return;
        }
    #endif
        if (_channelFactory == null)
          _channelFactory = new ChannelFactory<T>("AlyzaServiceEndpoint");
        IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel();
        bool success = false;
        try
        {
          codeBlock((T)proxy);
          proxy.Close();
          success = true;
        }
        finally
        {
          if (!success)
          {
            proxy.Abort();
          }
        }
      }
    }
    

    通过将这个测试钩子添加到服务中,我可以在我的测试中潜入任何实现 T 的对象:

    MockWebsiteService mockmodel = new MockWebsiteService();
    Service<WcfInterface.IServiceWebsites>.DebugOverride = mockmodel;
    // run my tests here
    

    对我来说,这是模拟 WCF 服务的好方法!

    PS:我知道由于#if DEBUG,测试不会在发行版中编译。如果你在乎,就把他们踢出去。

    【讨论】:

      【解决方案2】:

      我们使用依赖注入来解决这个问题,将服务客户端(或者服务客户端的工厂)注入到 ViewModel 中。像这样的:

      interface IClientFactory
      {
          TClient CreateClient<TClient>();
      }
      
      class ClientFactory : IClientFactory
      {
          TClient CreateClient<TClient>() 
          {
             var channelFactory = new ChannelFactory<TClient>("AlyzaServiceEndpoint");
             var proxy = (TClient)channelFactory.CreateChannel();
             return proxy;
          }
      }
      
      public ViewModel 
      {
          public ViewModel(IClientFactory clientFactory)
          {
             _clientFactory = clientFactory;
          }
      
          private void DoWcfStuff()
          {
              using (var proxy = _clientFactory.CreateClient<IClientChannel>())
              {
                 var result = proxy.GetThings();
              }
          }
      }
      
      public ViewModelTests
      {
          public void Setup()
          {
             _mockFactory = new MockClientFactory();
             _viewModel = new ViewModel(_mockFactory);
          }
      
          [Test]
          public void Test() 
          {
             var testResult = new Result();
             var mockClient = _mockFactory.CreateClient<IClientChannel>();
      
             mockClient.SetResultForGetThings(testResult);
      
             // put the viewmodel through its paces.
          }
      
          private class MockClientFactory : IClientFactory
          {
              MockClient _mockClient;
      
              public MockClientFactory()
              {
                _mockClient = new MockClient();
              }
      
              public TClient CreateClient<TClient>()
              {
                 if (typeof(TClient) == typeof(IClientChannel))
                 {
                    return _mockClient;
                 }
              }
          }
      
          private class MockClient : IClientChannel
          {
              void SetupGetThingsResult(Result result)
              {
                 _result = result;
              }
      
              Result GetThings() 
              {
                 return _result;
              }
          }
      }
      

      我展示了一个使用手动代码模拟的示例。通常我会使用像 Moq 这样的 Mocking 框架。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-23
        • 2011-09-09
        • 2010-11-17
        相关资源
        最近更新 更多