问题在于,与 MVC 中的 Controller 相反,WPF 中的 MVVM 模型是实例化的并且永远存在。实际上,这对我来说意味着如果我的 viemodel 中有私有(private)属性(property),我的代理将开放很长时间,例如:
//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();
(...)
}
在服务包装器中:
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 一起使用,就像这里回答的那样:
Dependency Injection wcf
但是,我并不完全理解这个解决方案。
我的理解是它是这样发生的:
DI 解析我的 ServiceClient 的一个实例,并且在执行超出使用解析实例的方法的范围后,DI 容器处理该实例 - 不需要抽象工厂模式。
显然,我的理解是错误的。
问题:在这种情况下,正确的做法是什么?
最佳答案
您很可能不想注入(inject) WCF 客户端,因为您只需要很短的时间。你可以注入(inject)的是一个工厂,它会在需要时返回服务。
工厂界面可能如下所示:
public interface IMyServiceFactory
{
IMyService Create();
void Release(IMyService created);
}
假设您通过构造函数注入(inject)它并向您的类添加私有(private)只读字段,如下所示:
private readonly IMyServiceFactory _myServiceFactory;
然后,当您需要该服务时,请执行以下操作:
var myService = _myServiceFactory.Create();
try
{
// do something with the service
}
finally
{
_myService.Release(myService);
}
一个很大的好处是你的类完全依赖于抽象。它不“知道”服务是由 WCF 客户端实现的,或者工厂正在调用 DI 容器。您可以使用
IMyService
的模拟来测试它。 .如果您的类(class)直接创建自己的 WCF 客户端,这是不可能的。您没有提及您使用的是哪个容器,但其中许多会为您创建 WCF 客户端来实现接口(interface)。
关于温莎的另一个不错的细节是它还将 create the abstract factory for you .
这里还有一些组成使用 Windsor 的示例实现的部分。这假设您有一个实现
IMyService
的 WCF 服务。 .这是服务接口(interface)、工厂接口(interface)和使用它的类:
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 容器。您的类(class)所知道的是,它正在调用工厂来获取服务实例。
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
的实现是使用名为“MyServiceEndpointName”的 app.config/web.config 中指定的端点的 WCF 客户端。 IMyServiceFactory
的实现是温莎创建的工厂。 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 客户端的详细信息。已经有一段时间了,我生疏了。)
但是习惯了使用 Windsor 之后,它就容易多了。
以这种方式完成后,如果假设
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.
}
}
因为你的类(class)对
IMyService
一无所知- 它不知道“正常”实现是 WCF 客户端 - 很容易用其他东西替换它。
关于c# - 使用 DI 在 WPF 应用程序中使用 WCF 服务的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47640718/