簡體   English   中英

使用DI在WPF應用程序中使用WCF服務的正確方法

[英]Proper way of consuming WCF Services in WPF Application using DI

問題在於,與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使用Abstract Factory模式,就像在這里被回答的那樣:

依賴注入wcf

但是,我不完全了解此解決方案。 我的理解是,它是這樣發生的:DI解析了我的ServiceClient的一個實例,執行執行后,使用該解析的實例的方法的作用域之外,DI容器將處置該實例-不需要Abstract工廠模式。 顯然,我的理解是錯誤的。

問題:在這種情況下,正確的做法是什么?

您很可能不想注入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的模擬進行IMyService 如果您的類直接創建自己的WCF客戶端,那是不可能的。

您沒有提到要使用哪個容器,但是其中許多容器將為您創建WCF客戶端以實現接口。

  • Autofac
  • 溫莎 -目前缺少他們的文檔。 如果您正在使用Windsor,我可以提供更多詳細信息。

關於Windsor的另一個不錯的細節是,它還將為您創建抽象工廠


以下是構成使用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());

這將執行以下操作:

  • 為容器添加“設施”或功能,以提供抽象工廠(我將繼續介紹)。
  • 添加了用於管理WCF服務的WCF工具
  • 使用在app.config / web.config中指定的名為“ MyServiceEndpointName”的終結點,通知容器IMyService的實現是WCF客戶端。
  • 告訴容器IMyServiceFactory的實現是溫莎創建的工廠。 再次是該文檔。 這意味着您實際上並沒有創建實現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將返回特定數據,如果您想對類進行單元測試,該IMyService辦? 現在,使用Moq或像這樣編寫測試double類真的很容易:

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客戶端-因此很容易用其他東西替換它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM