C# “重构”;“程序性”;WCF服务

C# “重构”;“程序性”;WCF服务,c#,wcf,dependency-injection,refactoring,cqrs,C#,Wcf,Dependency Injection,Refactoring,Cqrs,我正试图将一个庞大的WCF服务重构为更易于管理的服务。 在编写本文时,该服务通过构造函数接受了大约9个依赖项,这使得单元测试非常困难 该服务通过状态机处理本地状态,对参数进行验证,引发故障异常,执行实际操作,并通过发布/子通道触发发布事件。此代码与所有其他服务调用非常相似 我意识到我可以做一些不同的事情(参数验证、发布/订阅通知),可能是通过或WCF行为,但我的直觉告诉我,一般的方法是错误的——这感觉太“程序化” 我的目标是将实际操作的执行与发布/订阅通知(pub/sub notificatio

我正试图将一个庞大的WCF服务重构为更易于管理的服务。 在编写本文时,该服务通过构造函数接受了大约9个依赖项,这使得单元测试非常困难

该服务通过状态机处理本地状态,对参数进行验证,引发故障异常,执行实际操作,并通过发布/子通道触发发布事件。此代码与所有其他服务调用非常相似

我意识到我可以做一些不同的事情(参数验证、发布/订阅通知),可能是通过或WCF行为,但我的直觉告诉我,一般的方法是错误的——这感觉太“程序化”

我的目标是将实际操作的执行与发布/订阅通知(pub/sub notification,甚至可能是错误处理)分离开来

我想知道类似或其他技术的首字母缩略词在这里是否有帮助?不幸的是,我对那些超出定义的概念不是很熟悉

下面是一个此类WCF操作的(简化)示例:

public void DoSomething(DoSomethingData data)
{
    if (!_stateMachine.CanFire(MyEvents.StartProcessing))
    {
        throw new FaultException(...);
    }

    if (!ValidateArgument(data))
    {
        throw new FaultException(...);
    }

    var transitionResult =
        _stateMachine.Fire(MyEvents.StartProcessing);

    if (!transitionResult.Accepted)
    {
        throw new FaultException(...);
    }

    try
    {
        // does the actual something
        DoSomethingInternal(data);

        _publicationChannel.StatusUpdate(new Info
        {
            Status = transitionResult.NewState
        });
    }
    catch (FaultException<MyError> faultException)
    {
        if (faultException.Detail.ErrorType == 
            MyErrorTypes.EngineIsOffline)
        {
            TryFireEvent(MyServiceEvent.Error, 
                faultException.Detail);
        }
        throw;
    }
}
public void DoSomething(DoSomethingData数据)
{
if(!\u stateMachine.CanFire(MyEvents.StartProcessing))
{
抛出新的FaultException(…);
}
如果(!ValidateArgument(数据))
{
抛出新的FaultException(…);
}
var转换结果=
_stateMachine.Fire(MyEvents.StartProcessing);
如果(!transitionResult.Accepted)
{
抛出新的FaultException(…);
}
尝试
{
//实际情况如何
DoSomethingInternal(数据);
_publicationChannel.StatusUpdate(新信息
{
状态=transitionResult.NewState
});
}
捕获(FaultException FaultException)
{
如果(faultException.Detail.ErrorType==
MyErrorTypes.EngineIsOffline)
{
TryFireEvent(MyServiceEvent.Error,
例外情况(细节);
}
投掷;
}
}

这里有一个很好的伪装命令示例。您在这里所做的很好的事情是,您的服务方法已经接受了一个参数
DoSomethingData
。这是你的命令信息

这里缺少的是对命令处理程序的一般抽象:

公共接口ICommandHandler
{
无效句柄(TCommand命令);
}
通过一点重构,您的服务方法将如下所示:

//普通依赖项。
ICommandHandler-doSomethingHandler;
公共无效数据量(数据量)
{
this.doSomethingHandler.Handle(数据);
}
当然,您还需要
ICommandHandler
的实现。在您的情况下,它将如下所示:

公共类DoSomethingHandler:ICommandHandler
{
公共无效句柄(DoSomethingData命令)
{
//实际情况如何
DoSomethingInternal(命令);
}
}
现在,您可能想知道,您实现的那些交叉关注点(如参数验证、can-fire、发布通道状态更新和错误处理)如何。是的,它们都是交叉关注点,您的WCF服务类和业务逻辑(DoSomethingHandler)都不应该关注这一点

有几种方法可以应用面向方面编程。有些人喜欢使用PostSharp之类的代码编织工具。这些工具的缺点是,它们使单元测试变得更加困难,因为您将所有的横切关注点都编入其中

第二种方法是使用拦截。使用动态代理生成和一些反射。然而,我更喜欢这种方式的一种变体,那就是应用装饰器。好的方面是,根据我的经验,这是应用横切关注点的最干净的方式

让我们看一看用于验证的装饰器:

公共类WcfValidationCommandHandlerDecorator:ICommandHandler
{
专用IValidator验证器;
私有ICommandHandler;
公共验证CommandHandlerDecorator(IValidator验证程序,
ICommandHandler(已包装)
{
this.validator=验证程序;
this.wrapped=wrapped;
}
公共无效句柄(T命令)
{
如果(!this.validator.ValidateArgument(命令))
{
抛出新的FaultException(…);
}
//命令有效。让我们调用真正的处理程序。
this.wrapped.Handle(命令);
}
}
因为这个
WcfValidationCommandHandlerDecorator
是一个泛型类型,所以我们可以将它包装在每个命令处理程序中。例如:

var handler=新的WcfValidationCommandHandlerDecorator(
新的DoSomethingHandler(),
新的DoSomethingValidator());
您也可以轻松创建一个处理任何抛出异常的装饰器:

公共类WCFEExceptionHandlerCommandHandlerDecorator:ICommandHandler
{
私有ICommandHandler;
公共验证CommandHandlerDecorator(ICommandHandler包装)
{
this.wrapped=wrapped;
}
公共无效句柄(T命令)
{
尝试
{
//实际情况如何
this.wrapped.Handle(命令);
_publicationChannel.StatusUpdate(新信息
{ 
状态=transitionResult.NewState
});
}
捕获(FaultException FaultException)
{
if(faultException.Detail.ErrorType==MyErrorTypes.EngineIsOffline)
{
TryFireEvent(MyServiceEvent.Error,faultException.Detail);
}
投掷;
}
}
}
你看到我是如何把你的代码包装在这个装饰器里的了吗?我们可以再次使用此装饰器来包装原始:

var处理程序=
新的WcfValidationCommandHandlerDecorator(
新的WCFEExceptionHandlerCommandHandlerDecorator(
新的DoSomethingHandler()),