前段时间看到了几篇关于Attribute相关的文章,里面讲的很详细,参考:Attribute在.NET编程中的应用(五)
不过让我收获最多的关于“.NET Framework拦截机制的内容”,而且这也让我解决了一年前提出的问题!
地址:急丶急丶急 → 关于特性和IOC或者AOP的结合,其实跟IOC和AOP一点关于都没有,只是当时知识积累还不够,对解决问题的能力还不足!
下面我就结合一些理论知识以及对上面提出的案例进行改装,使它能够运用一般的方法之中(自定义一个数据库回滚特性)!
1.定义一个RollBack的特性
1 [AttributeUsage(AttributeTargets.Method)] 2 public class RollBackAttribute : Attribute 3 { 4 private bool _isCommit = true; 5 6 public RollBackAttribute() 7 { } 8 9 public bool IsCommit //定义一个属性提示是否要提交数据 10 { 11 get { return _isCommit; } 12 set { _isCommit = value; } 13 } 14 }
2.定义一个消息接收器
1 public class RollBackMessageSink : IMessageSink 2 { 3 //定义下一个消息接收器实例 4 private IMessageSink _nextSink; 5 6 public RollBackMessageSink(IMessageSink messageSink) //通过构造函数来传递下一个消息接收器 7 { 8 _nextSink = messageSink; 9 } 10 11 //异步执行消息,一般情况下不需要写任何逻辑 12 public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink) 13 { 14 throw new NotImplementedException(); 15 } 16 17 public IMessageSink NextSink 18 { 19 get 20 { 21 return _nextSink; 22 } 23 } 24 25 //同步执行消息 26 public IMessage SyncProcessMessage(IMessage msg) 27 { 28 //这边就是最重要的代码了
//有一点要说明的:如果调用[_nextSink.SyncProcessMessage(msg)]就说明方法已经执行好了
//这边有一个前处理和后处理的概念
//前处理:也就是调用方法前的处理事件
//后处理:也就是方法调用结束后的处理事件
//前处理和后处理的标记:就是是否调用了[_nextSink.SyncProcessMessage(msg)],调用之前就是前处理,反之是后处理 66 RollBackAttribute rollBack = GetRollBackAttribute(msg); 67 if (rollBack == null) return _nextSink.SyncProcessMessage(msg); 68 if (rollBack.IsCommit) 69 { 70 using (TransactionScope scope = new TransactionScope()) 71 { 72 return _nextSink.SyncProcessMessage(msg); 73 } 74 } 75 return _nextSink.SyncProcessMessage(msg); 76 } 77 78 private RollBackAttribute GetRollBackAttribute(IMessage msg) 79 { 80 //特别要注意["__TypeName"],其实这边是两个下划线,这个一定要记,可以通过调试来查看
81 string typeName = (string)msg.Properties["__TypeName"];
82 string methodName = (string)msg.Properties["__MethodName"]; 83 84 Console.WriteLine(typeName); 85 Console.WriteLine(methodName); 86 87 //根据类型和方法名获取自定义特性 88 object[] customAttributes = Type.GetType(typeName).GetMethod(methodName).GetCustomAttributes(true); 89 if (customAttributes != null && customAttributes.Length > 0) 90 { 91 RollBackAttribute rollback = customAttributes[0] as RollBackAttribute; 92 if (rollback != null) 93 { 94 //Console.WriteLine("是个对象啦"); 95 return rollback; 96 } 97 } 98 return null; 99 } 100 }
3.下面来定义上下文属性,只要把消息接收器放到上下文中才行
1 //必须要实现下面两个接口的 2 //接下来我们定义上下文环境的属性,上下文环境属性必须根据你要创建的接收器类型来实现相应的接口, 3 //比如:如果创建的是服务器上下文环境接收器,那么必须实现IContributeServerContextSink接口。 4 public class RollBackProperty : IContributeObjectSink, IContextProperty 5 { 6 //从接口名可以很清楚的看出来,通过这个上下文把消息接收器加到消息链中去 7 //添加对象链 8 #region IContributeObjectSink Members 9 10 //这个方法就是最重要的了 11 public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink) 12 { 13 //获取当前的消息接收器的实例 14 return new RollBackMessageSink(nextSink); 15 } 16 17 #endregion 18 19 #region IContextProperty Members 20 21 //一般情况下不要实现,高级代码,o(∩_∩)o 哈哈 22 //终结对象 23 public void Freeze(Context newContext) 24 { 25 26 } 27 28 //这是是问你是否想要创建新的上下文实例 29 public bool IsNewContextOK(Context newCtx) 30 { 31 //默认情况下都是返回true 32 return true; 33 } 34 35 public string Name 36 { 37 get 38 { 39 //可以任意指定字符串 40 return "XXXXXXXXXXXXX"; 41 } 42 } 43 44 #endregion 45 }
4.倒数第二步了,定义特性,把第三个定义的属性加到运行时的上下文中去
1 [AttributeUsage(AttributeTargets.Class)] 2 public class RollBackContextAttribute: ContextAttribute 3 { 4 public RollBackContextAttribute() 5 : base("VVVVVVVV") 6 { } 7 8 public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg) 9 { 10 //把自定义好的上下文实例添加到上下文集合中去 11 ctorMsg.ContextProperties.Add(new RollBackProperty()); 12 } 13 }
5.最后一步,定义一个回滚帮助类
1 [RollBackContext] 2 //必须继承这个类,只有继承这个类才能使对象在多个上下文边界中进行传递 3 public class RollBackHandler : ContextBoundObject 4 { 5 }
6.测试
1 public class TestRollBack : RollBackHandler //必须继承这个回滚助手类 2 { 3 [通过消息机制实现回滚特性.RollBack] //为方法添加RollBack特性 4 public void MyTest() 5 { 6 SqlConnectionStringBuilder connectionString = new SqlConnectionStringBuilder(); 7 connectionString.DataSource = @"LBDZ-20120514VC\SQLEXPRESS"; 8 connectionString.InitialCatalog = "My"; 9 connectionString.IntegratedSecurity = true; 10 11 using (SqlConnection conn = new SqlConnection(connectionString.ToString())) 12 { 13 conn.Open(); 14 string strSQL = "INSERT dbo.MyNames (ListName) VALUES ( 'XXXXX')"; 15 SqlCommand cmd = conn.CreateCommand(); 16 cmd.CommandText = strSQL; 17 cmd.ExecuteNonQuery(); 18 } 19 } 20 }
运行结果可想而知。
好了,其实还没有对这些知识点内部机制有个很熟的理解,不过通过自己的动手实验,也让自己明白了这些东西是怎么个回事!
通过这件事也让俺明白了很多时候并不是你不笨,只是你的经验还不够,你的知识积累还很少,其实很多知识要领到了一定时候自然会明白的!
以同步至:个人文章目录索引