【问题标题】:How to manage lifecycle of WCF clients in a WPF application [duplicate]如何在 WPF 应用程序中管理 WCF 客户端的生命周期 [重复]
【发布时间】:2016-03-01 13:45:48
【问题描述】:

在开发允许编辑物品和载体(托盘、货架)数据(以 CRUD 方式)的 WPF 应用程序时,我正在研究如何管理连接到包含实际数据的服务的 WCF 客户端的生命周期.

我更喜欢使用 Caliburn Micro 和 StructureMap 或 Castle Windsor 的 MVVM 方法。

我的主要问题不是创建 WCF 客户端通道或工厂,而是更重要的是使用后的清理。我打算在服务器端使用每个请求的生命周期,因此我需要在每个请求的基础上创建和处置我的客户端。因此,我有以下想法:

public class Article
{
    public int Id { get; set; }
    public string ArticleId { get; set; }
}

[ServiceContract]
public interface IArticleCrud
{
    [OperationContract]
    Article CreateArticle(string articleId);
    [OperationContract]
    void Delete(int articleId);
}

public class ArticlesViewModel
{
    private readonly Func<IArticleCrud> articleCrudFactory;

    public ArticlesViewModel(Func<IArticleCrud> articleCrudFactory)
    {
        this.articleCrudFactory = articleCrudFactory;
    }

    public void Delete(int articleId)
    {
        // Doesn't work since IArticleCrud is not IDisposable
        using (var crud = articleCrudFactory())
        {
            crud.Delete(articleId);
        }
    }
}

正如评论中所述,这不起作用,因为 IArticleCrud 不是 IDisposable。 IArticleCrud 用于在客户端创建一个 ChannelFactory 来为实现相同接口的服务生成代理。我很乐意将这段代码换成以下代码:

public class DeleteArticleCommand : IRequest
{
    public int Id { get; set; }
}

public class ArticlesViewModel
{
    private readonly IMediator mediator;

    public ArticlesViewModel(IMediator mediator)
    {
        this.mediator = mediator;
    }

    public void Delete(int articleId)
    {
        mediator.Send(new DeleteArticleCommand {Id = articleId});
    }
}

public class DeleteArticleCommandHandler : RequestHandler<DeleteArticleCommand>
{
    private readonly IArticleCrud articleCrud;

    public DeleteArticleCommandHandler(IArticleCrud articleCrud)
    {
        this.articleCrud = articleCrud;
    }

    protected override void HandleCore(DeleteArticleCommand message)
    {
        articleCrud.Delete(message.Id);
    }
}

但是,这并不能解决我的问题,因为我还没有处理 WCF 客户端的处置问题。但是,我可以让 IMediator 在 Send 操作上创建一个新的嵌套容器,并在 Send 操作完成后将其处理掉,但这似乎很麻烦。

我是不是全都搞错了,还是仅仅为了从 WPF 应用程序执行 WCF 调用就需要付出很多努力?

作为旁注,我将拥有比这几个 CRUD 服务更多的服务,因此在我的 CRUD 服务中修复此问题的实用解决方案可能不是一种选择。

【问题讨论】:

  • 这个问题是关于 WCF 会话生命周期的吗?
  • 不,我想通过会话 PerRequest 使 WCF 层尽可能薄。

标签: c# wpf wcf mvvm dependency-injection


【解决方案1】:

我已经处理了同样的问题(WCF 服务在 WPF 应用程序中使用)并且想使用 ServiceInterface 而不是 ServiceClient(它是 IDisposable 并且可以在 using-block 中使用)。

关闭连接的解决方案之一是将接口转换为客户端类型并调用.Close()-方法:

public class Article
{
    public int Id { get; set; }
    public string ArticleId { get; set; }
}

public interface IArticleCrud
{
    Article CreateArticle(string articleId);
    void Delete(int articleId);
}

public class ArticlesViewModel
{
    private readonly Func<IArticleCrud> articleCrudFactory;

    public ArticlesViewModel(Func<IArticleCrud> articleCrudFactory)
    {
        this.articleCrudFactory = articleCrudFactory;
    }

    public void Delete(int articleId)
    {
        //Using-Block doesn't work since IArticleCrud is not IDisposable
        var crud = articleCrudFactory();
        crud.Delete(articleId);

        if (crud is ArticleCrud)
            (crud as ArticleCrud).Close();
    }
}

您还可以在您的 articleCrudFactory 中创建一个静态方法来关闭您的 IArticleCrud:

public static void CloseInterface(IArticleCrud crud)
{
    if (crud is ArticleCrud)
        (crud as ArticleCrud).Close();
    else { ... } 
}

【讨论】:

  • 我想你的意思可能是投射到IChannelICommunicationObject 并关闭它,我在IArticleCrud 上没有Close(),我什至没有ArticleCrud在客户端中(请记住,它只是一个 WCF 代理)。另外,Close() 对我来说不够安全(需要尝试/捕获并在错误时中止)。
  • 请编辑您的问题并指定您仅使用 WCF 代理,否则其他人可能会认为您正在使用“右键单击 -> 添加新的 ServiceReference”来实现服务,然后生成接口和客户端(在您的情况下为 IArticleCrud 和 ArticleCrudClient)。
【解决方案2】:

我已经用 WCF 和 MVVM 完成了它,而且它真的很容易(如果我解决了你的问题):

public interface IRequest
{
}

public interface IRequestHandler<in TCommand> where TCommand : IRequest
{
    void HandleCore(TCommand command);
}

public class DeleteArticleCommand : IRequest
{
    public int Id { get; set; }
}

public class ArticlesViewModel
{
    private readonly IRequestHandler<DeleteArticleCommand> _handler;

    public ArticlesViewModel(IRequestHandler<DeleteArticleCommand> handler)
    {
        _handler = handler;
    }

    public void Delete(int articleId)
    {
        _handler.HandleCore(new DeleteArticleCommand { Id = articleId });
    }
}

//On client side
public sealed class WcfServiceCommandHandlerProxy<TCommand> 
    : IRequestHandler<TCommand> where TCommand : IRequest
{
    public void HandleCore(TCommand command)
    {
        using (var service = new ActuaclWcfServiceClient())
        {
            service.Send(command); //Or however you are working with you WCF client
        }
    }
}

//Somewhere on server side
public class DeleteArticleCommandHandler : IRequestHandler<DeleteArticleCommand>
{
    private readonly IArticleCrud _articleCrud;

    public DeleteArticleCommandHandler(IArticleCrud articleCrud)
    {
        _articleCrud = articleCrud;
    }

    public void HandleCore(DeleteArticleCommand message)
    {
        articleCrud.Delete(message.Id);
    }
}

只需注册您的IRequestHandler 接口以使用WcfServiceCommandHandlerProxy 类型实现即可:

//May vary :)
Register(typeof (ICommandHandler<>), typeof (WcfServiceCommandHandlerProxy<>))

【讨论】:

  • 我打算将 IArticleCrud 作为 WCF 服务的 ServiceContract,但更重要的是,您对 WcfServiceCommandHandlerProxy 的实现是控制服务创建,我宁愿从 IoC 获得从一个地方控制它,这让我回到了最初的问题。
  • @mycroes 这个WcfServiceCommandHandlerProxy 只做一件事——创建和处理 WCF 客户端。如果您需要更多责任,请装饰它并将其与另一个类包装起来。 WcfServiceCommandHandlerProxyclass 在实施后不应更改。 IoC 现在对生命周期无所事事。 IoC 只知道WcfServiceCommandHandlerProxyclass 实现了IRequestHandler
  • new ActualWcfServiceClient() 在其他地方施加约束,例如我需要为地址/绑定进行 XML 配置或将它们也放在这里。另外,我不使用生成的 WCF 客户端,而是使用服务接口生成代理。
  • @mycroes 我不明白你的问题到底是什么。如果您不想使用new 创建它,只需像往常一样通过 DI 来创建它。如果你有 5 个不同的服务,只需用一个接口实现 5 个不同的客户端。你想达到什么目的?
  • 问题是,如果我依赖客户端代理本身,那么我将再次失去对处理它的控制。然而,这让我觉得我可能能够 DI ChannelFactory 并用 channelFactory.CreateChannel() 替换 new ActualWcfServiceClient(),除了频道不能正确处置的小烦恼(可以在 Dispose() 中引发异常)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多