问题是与MVC中的控制器相反,WPF中的MVVM模型被实例化并且那里是好的。实际上,这对我来说意味着如果我的viemodel中有私有财产,我的代理将会开放很长时间,例如:
//Some example after googling for "consuming wcf services in wpf app~"
private FootballerServices.FootballerServiceClient footballersService = null;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
footballersService = new FootballerServices.FootballerServiceClient();
try
{
FootballerBox.ItemsSource = footballersService.GetFootballers();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
如何正确解决该问题?
第一个解决方案:
private void button_Click(object sender, RoutedEventArgs e)
{
MyServicesClient proxy = new MyServicesClient();
try
{
MyServicesData data = proxy.GetDataFromMyService();
proxy.Close();
}
catch (FaultException ex)
{
MessageBox.Show("Fault Exception: " + ex.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
然后我可以创建一个像ServiceWrapper这样的新类,我将封装所有方法调用: private void button_Click(object sender,RoutedEventArgs e) { var t = serviceWrapper.GetDataFromMyService(); (......) }
并在Service Wrapper中:
private void button_Click(object sender, RoutedEventArgs e)
{
MyServicesClient proxy = new MyServicesClient();
try
{
MyServicesData data = proxy.GetDataFromMyService();
proxy.Close();
}
catch (FaultException ex)
{
(...)
}
catch (Exception ex)
{
(...)
}
return data;
}
现在最后一件事就是将DI添加到该解决方案
第二个解决方案
我找到的另一种方法是使用抽象工厂模式和DI,就像在这里回答:
但是,我并不完全理解这个解决方案。 我的理解是它发生在这样: DI解析了我的ServiceClient的一个实例,并在执行后超出了使用已解析实例的方法的范围,DI容器处理该实例 - 不需要抽象工厂模式。 显然,我的理解是错误的。问题:这种情况下的正确做法是什么?
答案 0 :(得分:3)
您很可能不想注入WCF客户端,因为您只需要很短的时间。你可以注入的是一个工厂,它将在需要时返回服务。
工厂界面可能如下所示:
public interface IMyServiceFactory
{
IMyService Create();
void Release(IMyService created);
}
假设您通过构造函数注入此内容并向您的类添加私有只读字段,如下所示:
private readonly IMyServiceFactory _myServiceFactory;
然后,当您需要该服务时,您可以这样做:
var myService = _myServiceFactory.Create();
try
{
// do something with the service
}
finally
{
_myService.Release(myService);
}
一个很大的好处是你的课程完全取决于抽象。它不“知道”服务是由WCF客户端实现的,还是工厂正在调用DI容器。您可以使用模拟IMyService
来测试它。如果你的类直接创建自己的WCF客户端,这是不可能的。
您没有提到您正在使用的容器,但其中许多容器将为您创建WCF客户端以实现接口。
关于温莎的另一个不错的细节是它也create the abstract factory for you。
以下是使用Windsor构建示例实现的更多部分。这假设您有一个实现IMyService
的WCF服务。
这是服务接口,工厂接口以及使用它的类:
public interface IMyService
{
IEnumerable<string> GetSomeData();
}
public interface IMyServiceFactory
{
IMyService Create();
void Release(IMyService created);
}
public class ClassThatConsumesService
{
private readonly IMyServiceFactory _serviceFactory;
public ClassThatConsumesService(IMyServiceFactory myServiceFactory)
{
_serviceFactory = myServiceFactory;
}
public void MethodThatDoesSomething()
{
var service = _serviceFactory.Create();
try
{
var data = service.GetSomeData();
// do whatever
}
finally
{
_serviceFactory.Release(service);
}
}
}
然后,这是使用Windsor的一些实现细节的示例。但一个重要的细节是,以上任何事情都不取决于温莎。您可以使用不同的DI容器来完成此操作。你甚至根本不需要DI容器。您的所有类都知道它正在调用工厂来获取服务实例。
public class ClassThatConsumesService
{
private readonly IMyServiceFactory _serviceFactory;
public ClassThatConsumesService(IMyServiceFactory myServiceFactory)
{
_serviceFactory = myServiceFactory;
}
public void MethodThatDoesSomething()
{
var service = _serviceFactory.Create();
try
{
var data = service.GetSomeData();
// do whatever
}
finally
{
_serviceFactory.Release(service);
}
}
}
要使用Windsor,您需要添加Windsor WCF Facility nuget包,即Windsor以及一些用于处理WCF服务的其他类。
您的容器注册可能如下所示:
container.AddFacility<TypedFactoryFacility>();
container.AddFacility<WcfFacility>();
container.Register(Component.For<IMyService>()
.AsWcfClient(WcfEndpoint.FromConfiguration("MyServiceEndpointName")));
container.Register(Component.For<IMyServiceFactory>().AsFactory());
执行以下操作:
IMyService
的实现是使用app.config / web.config中指定名为“MyServiceEndpointName”的端点的WCF客户端。IMyServiceFactory
的实现是Windsor创建的工厂。 Here's that documentation again.。这意味着您实际上并没有创建一个类来实现IMyServiceFactory
。容器做到了。当您致电Create
时,工厂会创建客户端。当您致电Release
时,它会处理客户端。如果您愿意,可以编写自己的工厂实现,如下所示:
public class WcfClientMyServiceFactory : IMyServiceFactory
{
public IMyService Create()
{
return new MyServiceClient();
}
public void Release(IMyService created)
{
var client = (MyServiceClient) created;
try
{
try
{
client.Close();
}
catch
{
client.Abort();
}
}
finally
{
client.Dispose();
}
}
}
(不要引用我关于如何正确关闭WCF客户端的详细信息。已经有一段时间了,我生锈了。)
但是习惯使用温莎之后,它就容易多了。
这样做了,假设IMyService
将返回特定数据,你想要对你的类进行单元测试怎么办?现在使用Moq非常简单,或者只写这样的测试双类:
public class MyServiceDouble : IMyService
{
public IEnumerable<string> GetSomeData()
{
return new [] {"x", "y", "z"};
}
}
public class MyServiceFactoryDouble : IMyServiceFactory
{
public IMyService Create()
{
return new MyServiceDouble();
}
public void Release(IMyService created)
{
// Nothing to clean up.
}
}
因为你的班级对IMyService
一无所知 - 它不知道“正常”的实现是一个WCF客户端 - 用其他东西很容易替换它。