背景
某位大牛说过,采用命名模式的好处是,你可以将命令按照不同的方式执行,如:排队、异步、远程和拦截等等。今天我介绍一下如何拦截命令的执行,这有些AOP的味道。
思路
就是一个管道过滤器而已
实现
先不考虑处理器的实例化和过滤器列表的实例化,如果给你一个命令、一些过滤器和一个处理器,让你组装为一个管道应该不是啥大问题。
这部分概念虽然简单,可是也不见得好理解,因此我基本把全部代码都贴上了,建议不太明白的同学,自己重写一遍,加深对管道过滤器的理解。
核心代码
命令接口
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Happy.Command 8 { 9 ///10 /// 命令接口。11 /// 12 public interface ICommand13 {14 }15 }
命令处理器接口,一个命令只能有一个命令处理器。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Happy.Command 8 { 9 ///10 /// 命令处理器接口,一个命令只能有一个命令处理器。11 /// 12 public interface ICommandHandler13 where TCommand : ICommand14 {15 /// 16 /// 处理命令。17 /// 18 void Handle(TCommand command);19 }20 }
命令拦截器,拦截正在被执行的命令。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Happy.Command 8 { 9 ///10 /// 命令拦截器,拦截正在被执行的命令。11 /// 12 [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]13 public abstract class CommandInterceptorAttribute : Attribute14 {15 ///16 /// 构造方法。17 /// 18 /// 指示拦截器在管道中的位置19 protected CommandInterceptorAttribute(int order)20 {21 this.Order = order;22 }23 24 ///25 /// 拦截正在被执行的命令。26 /// 27 /// 命令执行上下文28 public abstract void Intercept(ICommandExecuteContext context);29 30 ///31 /// 拦截器在管道中的位置。32 /// 33 public int Order { get; protected set; }34 }35 }
命令执行上下文接口,代表了一次命令的执行过程。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Happy.Command 8 { 9 ///10 /// 命令执行上下文接口,代表了一次命令的执行过程。11 /// 12 public interface ICommandExecuteContext13 {14 ///15 /// 命令执行服务。16 /// 17 ICommandService CommandService { get; }18 19 ///20 /// 正在执行的命令。21 /// 22 ICommand Command { get; }23 24 ///25 /// 执行下一个 27 void ExecuteNext();28 }29 },如果已经是最后一个,就会执行 。26 ///
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 using Happy.ExtensionMethod.Reflection; 8 9 namespace Happy.Command.Internal10 {11 internal sealed class CommandExecuteContext : ICommandExecuteContext12 {13 private CommandInterceptorChain _commandInterceptorChain;14 15 internal CommandExecuteContext(ICommandService commandService, ICommand command, Action commandExecutor)16 {17 this.CommandService = commandService;18 this.Command = command;19 _commandInterceptorChain = new CommandInterceptorChain(20 this,21 command.GetType().GetAttributes(),22 commandExecutor);23 }24 25 26 public ICommandService CommandService27 {28 get;29 private set;30 }31 32 public ICommand Command { get; private set; }33 34 public void ExecuteNext()35 {36 _commandInterceptorChain.ExecuteNext();37 }38 }39 }
管道过滤器的内部实现
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Happy.Command.Internal 8 { 9 internal sealed class CommandInterceptorChain10 {11 private ICommandExecuteContext _commandExecuteContext;12 private CommandInterceptorAttribute[] _commandInterceptors;13 private Action _commandExecutor;14 private int _currentCommandInterceptorIndex = -1;15 16 internal CommandInterceptorChain(17 ICommandExecuteContext commandExecuteContext,18 CommandInterceptorAttribute[] commandInterceptors,19 Action commandExecutor)20 {21 _commandExecuteContext = commandExecuteContext;22 _commandInterceptors = commandInterceptors.OrderBy(x => x.Order).ToArray();23 _commandExecutor = commandExecutor;24 }25 26 private CommandInterceptorAttribute CurrentCommandInterceptor27 {28 get29 {30 return _commandInterceptors[_currentCommandInterceptorIndex];31 }32 }33 34 internal void ExecuteNext()35 {36 _currentCommandInterceptorIndex++;37 38 if (_currentCommandInterceptorIndex < _commandInterceptors.Length)39 {40 this.CurrentCommandInterceptor.Intercept(_commandExecuteContext );41 }42 else43 {44 _commandExecutor();45 }46 }47 }48 }
命令服务,负责执行命令
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Threading; 7 8 using Common.Logging; 9 using Microsoft.Practices.ServiceLocation;10 11 using Happy.DesignByContract;12 13 namespace Happy.Command.Internal14 {15 internal sealed class DefaultCommandService : ICommandService16 {17 private readonly Dictionary_services = new Dictionary ();18 19 public void Execute (TCommand command)20 where TCommand : ICommand21 {22 command.MustNotNull("command");23 24 var context = this.CreateCommandExecuteContext(command);25 26 context.ExecuteNext();27 }28 29 public ICommandService AddService (T service)30 {31 _services[typeof(T)] = service;32 33 return this;34 }35 36 public T GetService ()37 {38 return (T)_services[typeof(T)];39 }40 41 private CommandExecuteContext CreateCommandExecuteContext (TCommand command)42 where TCommand : ICommand43 {44 45 return new CommandExecuteContext(this, command, () =>46 {47 this.ExecuteCommandHandler(command);48 });49 }50 51 private void ExecuteCommandHandler (TCommand command)52 where TCommand : ICommand53 {54 ServiceLocator.Current.MustNotNull("ServiceLocator.Current");55 56 var commandHandler = ServiceLocator57 .Current58 .GetInstance >();59 60 commandHandler.Handle(command);61 }62 }63 }
事务拦截器
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Transactions; 7 8 namespace Happy.Command 9 {10 ///11 /// 事务拦截器。12 /// 13 public sealed class TransactionAttribute : CommandInterceptorAttribute14 {15 ///16 public TransactionAttribute(int order) : base(order) { }17 18 /// 19 public override void Intercept(ICommandExecuteContext context)20 {21 using (var ts = new TransactionScope())22 {23 context.ExecuteNext();24 25 ts.Complete();26 }27 }28 }29 }
应用事务拦截器
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 using Happy.Domain; 8 using Happy.Command; 9 using Happy.DesignByContract;10 11 namespace Happy.Application12 {13 ///14 /// 简单的创建命令。15 /// 16 [Transaction(1)]17 public abstract class SimpleCreateCommand: SimpleCommand 18 where TAggregateRoot : AggregateRoot19 {20 }21 }
执行命令
1 ///2 /// 创建。 3 /// 4 public ActionResult Create(TAggregateRoot item) 5 { 6 this.CurrentCommandService.Execute(new TCreateCommand 7 { 8 Aggregate = item 9 });10 11 return this.NewtonsoftJson(new12 {13 success = true,14 items = this.GetById(item.Id)15 });16 }
备注
这里的命令模式本质上是一种消息模式,因为命令里没有任何行为,将行为独立了出来。像WCF、ASP.NET和ASP.NET MVC本质上也是消息模式,他们也内置了管道过滤器模式。