背景
某位大牛說過,采用命名模式的好處是,你可以將命令按照不同的方式執行,如:排隊、異步、遠程和攔截等等。今天我介紹一下如何攔截命令的執行,這有些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 /// <summary> 10 /// 命令接口。 11 /// </summary> 12 public interface ICommand 13 { 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 /// <summary> 10 /// 命令處理器接口,一個命令只能有一個命令處理器。 11 /// </summary> 12 public interface ICommandHandler<TCommand> 13 where TCommand : ICommand 14 { 15 /// <summary> 16 /// 處理命令。 17 /// </summary> 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 /// <summary> 10 /// 命令攔截器,攔截正在被執行的命令。 11 /// </summary> 12 [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 13 public abstract class CommandInterceptorAttribute : Attribute 14 { 15 /// <summary> 16 /// 構造方法。 17 /// </summary> 18 /// <param name="order">指示攔截器在管道中的位置</param> 19 protected CommandInterceptorAttribute(int order) 20 { 21 this.Order = order; 22 } 23 24 /// <summary> 25 /// 攔截正在被執行的命令。 26 /// </summary> 27 /// <param name="context">命令執行上下文</param> 28 public abstract void Intercept(ICommandExecuteContext context); 29 30 /// <summary> 31 /// 攔截器在管道中的位置。 32 /// </summary> 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 /// <summary> 10 /// 命令執行上下文接口,代表了一次命令的執行過程。 11 /// </summary> 12 public interface ICommandExecuteContext 13 { 14 /// <summary> 15 /// 命令執行服務。 16 /// </summary> 17 ICommandService CommandService { get; } 18 19 /// <summary> 20 /// 正在執行的命令。 21 /// </summary> 22 ICommand Command { get; } 23 24 /// <summary> 25 /// 執行下一個<see cref="CommandInterceptorAttribute"/>,如果已經是最后一個,就會執行<see cref="ICommandHandler{TCommand}"/>。 26 /// </summary> 27 void ExecuteNext(); 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.ExtensionMethod.Reflection; 8 9 namespace Happy.Command.Internal 10 { 11 internal sealed class CommandExecuteContext : ICommandExecuteContext 12 { 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<CommandInterceptorAttribute>(), 22 commandExecutor); 23 } 24 25 26 public ICommandService CommandService 27 { 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 CommandInterceptorChain 10 { 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 CurrentCommandInterceptor 27 { 28 get 29 { 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 else 43 { 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.Internal 14 { 15 internal sealed class DefaultCommandService : ICommandService 16 { 17 private readonly Dictionary<Type, object> _services = new Dictionary<Type, object>(); 18 19 public void Execute<TCommand>(TCommand command) 20 where TCommand : ICommand 21 { 22 command.MustNotNull("command"); 23 24 var context = this.CreateCommandExecuteContext(command); 25 26 context.ExecuteNext(); 27 } 28 29 public ICommandService AddService<T>(T service) 30 { 31 _services[typeof(T)] = service; 32 33 return this; 34 } 35 36 public T GetService<T>() 37 { 38 return (T)_services[typeof(T)]; 39 } 40 41 private CommandExecuteContext CreateCommandExecuteContext<TCommand>(TCommand command) 42 where TCommand : ICommand 43 { 44 45 return new CommandExecuteContext(this, command, () => 46 { 47 this.ExecuteCommandHandler(command); 48 }); 49 } 50 51 private void ExecuteCommandHandler<TCommand>(TCommand command) 52 where TCommand : ICommand 53 { 54 ServiceLocator.Current.MustNotNull("ServiceLocator.Current"); 55 56 var commandHandler = ServiceLocator 57 .Current 58 .GetInstance<ICommandHandler<TCommand>>(); 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 /// <summary> 11 /// 事務攔截器。 12 /// </summary> 13 public sealed class TransactionAttribute : CommandInterceptorAttribute 14 { 15 /// <inheritdoc /> 16 public TransactionAttribute(int order) : base(order) { } 17 18 /// <inheritdoc /> 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.Application 12 { 13 /// <summary> 14 /// 簡單的創建命令。 15 /// </summary> 16 [Transaction(1)] 17 public abstract class SimpleCreateCommand<TAggregateRoot> : SimpleCommand<TAggregateRoot> 18 where TAggregateRoot : AggregateRoot 19 { 20 } 21 }
執行命令
1 /// <summary> 2 /// 創建。 3 /// </summary> 4 public ActionResult Create(TAggregateRoot item) 5 { 6 this.CurrentCommandService.Execute(new TCreateCommand 7 { 8 Aggregate = item 9 }); 10 11 return this.NewtonsoftJson(new 12 { 13 success = true, 14 items = this.GetById(item.Id) 15 }); 16 }
備注
這里的命令模式本質上是一種消息模式,因為命令里沒有任何行為,將行為獨立了出來。像WCF、ASP.NET和ASP.NET MVC本質上也是消息模式,他們也內置了管道過濾器模式。