I saw my colleague write a scheme for caching using AOP technology two days ago, so I was curious to see how this was achieved. It turned out to be used. NET, a class ContextBoundObject and attribute related technologies. In fact, a class in the. NET framework has long been, so far only to know it, is a bit brief encounter feeling. On the web, there have been many examples of using the ContextBoundObject class to implement AOP, in which I have seen an example of implementing an AOP transaction using ContextBoundObject and attribute, and I think it is a good idea to implement an AOP cache. I'll share this article here:
Objective
Using attribute to implement method-level transactions has always been my dream, talking about attribute [C # | Attribute | DefaultValueAttribute] have manifested my helplessness, attribute really is really true non-intrusive things (in fact I want to invade:)), Former Dudu attribute in. NET Programming series articles, but always from the imagination and demand there is so little discrepancy, through three days of effort, Google's company, the following and everyone to share my three days of results with the attribute realize AOP transaction!
Thank you article
1. Aspect-oriented programming enables Better Code encapsulation and reuse the key CallContext is found here.
2. C # attribute application in. NET programming (GOTO) This article is not found in the original address, Dudu's attribute in. NET Programming series article is the decomposition of this article, he wrote five, the following people can be seen in advance from this article.
Note Before reading
1. The core and breakthrough point of the whole article lies in the use of contextual context, it is important to pay attention to the role of CallContext in the whole program
2. The sqlhelper you see in this article uses Microsoft SqlHelper.cs.
3. This article focuses on how to achieve, and has been tested through, only the key code, so please read carefully, some of the code directly copied down the run will be wrong!
Body
First, let's look at a piece of code that is not transacted:
SqlDAL.cs
Public abstract class Sqldal
{
#region ConnectionString
Private SqlConnectionStringBuilder _connectionstring = null;
<summary>
String connection
</summary>
Public virtual SqlConnectionStringBuilder ConnectionString
{
Get
{
if (_connectionstring = = NULL | | | string. IsNullOrEmpty (_connectionstring.connectionstring))
{
_connectionstring = new SqlConnectionStringBuilder (configurations.sqlserver_connection_string);
}
return _connectionstring;
}
set {_connectionstring = value;}
}
#endregion
#region ExecuteNonQuery
public int ExecuteNonQuery (string cmdtext)
{
Return Sqlhelper.executenonquery (connectionstring.connectionstring, CommandType.Text, Cmdtext);
}
public int ExecuteNonQuery (string cmdtext, CommandType type)
{
Return Sqlhelper.executenonquery (connectionstring.connectionstring, type, cmdtext);
}
public int ExecuteNonQuery (string cmdtext, CommandType type, params sqlparameter[] cmdparameters)
{
Return Sqlhelper.executenonquery (connectionstring.connectionstring, type, Cmdtext, cmdparameters);
}
#endregion
}
Code Description:
1. This class is further encapsulated in the SqlHelper.cs.
2. configurations.sqlserver_connection_string replace it with its own connection string.
UserInfoAction.cs
public class Userinfoaction:sqldal
{
<summary>
Add user
</summary>
public void Add (UserInfo user)
{
StringBuilder sb = new StringBuilder ();
Sb. Append ("UPDATE [UserInfo] SET password= '");
Sb. Append (user. Password);
Sb. Append ("' WHERE uid=");
Sb. Append (user. UID);
ExecuteNonQuery (SQL);
}
}
If we want to join the transaction, the usual way is to try in the method, catch and then commit, Rollback, the shortcomings do not say, the following I will be on the side of the code side to explain, trying to master this method:
First two of the classes I modified
SqlDAL.cs
Public abstract class Sqldal:contextboundobject
{
Private SqlTransaction _sqltrans;
<summary>
Operation only supported with transactions
</summary>
Public SqlTransaction Sqltrans
{
Get
{
if (_sqltrans = = null)
{
Trying to get a transaction from the context
Object obj = CallContext.GetData (transactionaop.contextname);
if (obj! = null && obj is sqltransaction)
_sqltrans = obj as sqltransaction;
}
return _sqltrans;
}
set {_sqltrans = value;}
}
#region ConnectionString
Private SqlConnectionStringBuilder _connectionstring = null;
<summary>
String connection
</summary>
Public virtual SqlConnectionStringBuilder ConnectionString
{
Get
{
if (_connectionstring = = NULL | | | string. IsNullOrEmpty (_connectionstring.connectionstring))
{
_connectionstring = new SqlConnectionStringBuilder (configurations.sqlserver_connection_string);
}
return _connectionstring;
}
set {_connectionstring = value;}
}
#endregion
#region ExecuteNonQuery
public int ExecuteNonQuery (string cmdtext)
{
if (Sqltrans = = null)
Return Sqlhelper.executenonquery (connectionstring.connectionstring, CommandType.Text, Cmdtext);
Else
Return Sqlhelper.executenonquery (Sqltrans, CommandType.Text, Cmdtext);
}
public int ExecuteNonQuery (string cmdtext, CommandType type)
{
if (Sqltrans = = null)
Return Sqlhelper.executenonquery (connectionstring.connectionstring, type, cmdtext);
Else
Return Sqlhelper.executenonquery (Sqltrans, type, cmdtext);
}
public int ExecuteNonQuery (string cmdtext, CommandType type, params sqlparameter[] cmdparameters)
{
if (Sqltrans = = null)
Return Sqlhelper.executenonquery (connectionstring.connectionstring, type, Cmdtext, cmdparameters);
Else
Return Sqlhelper.executenonquery (Sqltrans, type, Cmdtext, cmdparameters);
}
#endregion
}
Code Description:
1. A property Sqltrans is added, and each executenonquery is executed before execution is performed in a transactional manner. This is done to prepare for the subsequent fetch of the transaction from the context.
2. Class inherits ContextBoundObject, note that is required, as MSDN describes: defines the base class for all context-bound classes.
3. The TRANSACTIONAOP will be given later.
UserInfoAction.cs
[Transaction]
public class Userinfoaction:sqldal
{
[Transactionmethod]
public void Add (UserInfo user)
{
StringBuilder sb = new StringBuilder ();
Sb. Append ("UPDATE [UserInfo] SET password= '");
Sb. Append (user. Password);
Sb. Append ("' WHERE uid=");
Sb. Append (user. UID);
ExecuteNonQuery (SQL);
}
}
Code Description:
1. Very concise, non-intrusive, very little change, very convenient (want to add 2 tags to the transaction, do not want to remove).
2. Two attribute will be given later.
<summary>
Label class all database operations within a method join transaction control
</summary>
[AttributeUsage (AttributeTargets.Class, AllowMultiple = False)]
Public sealed class Transactionattribute:contextattribute, Icontributeobjectsink
{
<summary>
Label class all database operations within a method join transaction control, use Transactionmethodattribute to annotate simultaneously
</summary>
Public TransactionAttribute ()
: Base ("Transaction")
{ }
Public IMessageSink getobjectsink (MarshalByRefObject obj, IMessageSink next)
{
return new TRANSACTIONAOP (next);
}
}
<summary>
All database operations within the labeling method are added to the transaction control
</summary>
[AttributeUsage (AttributeTargets.Method, AllowMultiple = False)]
public sealed class Transactionmethodattribute:attribute
{
<summary>
All database operations within the labeling method are added to the transaction control
</summary>
Public Transactionmethodattribute ()
{
}
}
Code Description:
1. In the above two articles are the icontextproperty, icontributeobjectsink separate inheritance and implementation, in fact, we found that ContextAttribute has inherited the Icontextproperty, All I need to do here is just to inherit the Icontributeobjectsink. Descriptions of the two interfaces are described in more detail in the previous article.
2. The TRANSACTIONAOP will be given later.
3. It is important to note that two attribute need to be used together, and I found attribute if the tag on the class he will be displayed instantiation, but on the method will not, the break point can be traced to this process, otherwise I will not be able to get two to mark.
TransactionAop.cs
public sealed class Transactionaop:imessagesink
{
Private IMessageSink Nextsink; Save the next sink
<summary>
constructor function
</summary>
<param name= "Next" > Receiver </param>
Public Transactionaop (IMessageSink nextsink)
{
This.nextsink = Nextsink;
}
<summary>
IMessageSink interface method, for asynchronous processing, we do not implement asynchronous processing, so simply return NULL,
Whether synchronous or asynchronous, this method needs to be defined
</summary>
<param name= "MSG" ></param>
<param name= "Replysink" ></param>
<returns></returns>
Public Imessagectrl asyncprocessmessage (IMessage msg, IMessageSink Replysink)
{
return null;
}
<summary>
Next receiver
</summary>
Public IMessageSink Nextsink
{
get {return nextsink;}
}
<summary>
///
</summary>
<param name= "MSG" ></param>
<returns></returns>
Public IMessage SyncProcessMessage (IMessage msg)
{
IMessage retmsg = null;
IMethodCallMessage call = msg as IMethodCallMessage;
if (call = = NULL | | (Attribute.GetCustomAttribute (call. MethodBase, typeof (Transactionmethodattribute))) = = null)
Retmsg = Nextsink.syncprocessmessage (msg);
Else
{
Switch to your own database connection here
using (SqlConnection Connect = new SqlConnection (configurations.sqlserver_connection_string))
{
Connect.open ();
SqlTransaction Sqltrans = Connect.begintransaction ();
Tell the store store in context
Callcontext.setdata (Transactionaop.contextname, Sqltrans);
Passing a message to the next sink-> means executing your own method
Retmsg = Nextsink.syncprocessmessage (msg);
if (Sqltrans! = null)
{
IMethodReturnMessage Methodreturn = retmsg as IMethodReturnMessage;
Exception except = methodreturn.exception;
if (except! = NULL)
{
Sqltrans.rollback ();
Can do logs and other processing
}
Else
{
Sqltrans.commit ();
}
Sqltrans.dispose ();
Sqltrans = null;
}
}
}
return retmsg;
}
<summary>
For extracting, storing sqltransaction
</summary>
public static string Contextname
{
get {return "TRANSACTIONAOP";}
}
}
Code Description:
1. IMessageSink MSDN: Defines the interface for a message sink.
2. Focus on the code within the SyncProcessMessage method, create a transaction here, and store it in the middle of the context, remember the Sqltrans attribute above Sqldal, which is obtained from the context.
3. Please note that the error can be caught here, but there is no way to handle the error, so the error will continue to be thrown out, but the integrity of the transaction we have achieved. You can do global processing at Global.asax, or try it manually, but we don't need to manage the transaction, just when the normal error is handled.
End
We can see that in the method is labeled all the database operations will be managed by the transaction, it is also my wish, it seems that my attribute to do the right to see a glimmer of hope, welcome you more comments:)
Supplement (2009-1-8)
With regard to the performance issues mentioned in the comments, it is certainly less efficient to use AOP to implement transactions than the direct try catch and then commit and rollback efficiency, but obviously maintainability and ease of use are much higher, so look at individual needs. Here is a question about Sqldal inheritance ContextBoundObject, and here are the solutions:
1. The simplest way to modify the least userinfoaction: Copy the Sqldal to change the class name, inherit the ContextBoundObject, and then change the inheritance class. Very not recommended: (
2. Instead of using the method of inheriting methods to access the data tier from the outset, change the sqldal to a normal class and access the data layer by declaring a sqldal way:
Private Sqldal _sqldao;
Public Sqldal Sqldao
{
Get
{
if (_sqldao = = null)
{
_sqldao = new Sqldal ();
Object obj = CallContext.GetData (transactionaop.contextname);
if (obj! = null && obj is sqltransaction)
_sqldao.sqltrans = obj as sqltransaction;
}
return _sqldao;
}
}
In this way, compared to no more than a transaction class only a value process and judgment process, efficiency should be more than the inheritance of Sqldal Direct inheritance ContextBoundObject much better.
Personal feeling is not very good, continue to explore, has thought of reducing a attribute way, thank you for your suggestions:)
Original: http://www.cnblogs.com/over140/archive/2009/01/07/1371307.html
Implementing an AOP summary using ContextBoundObject:
1. To define two attributes, one is that the tag is used to mark the class to be AOP (TransactionAttribute), and an attribute is used to mark the method to be AOP (Transactionmethodattribute). Defining a tag to an AOP class to inherit an interface: ContextAttribute, Icontributeobjectsink, defines the method used to mark an AOP to inherit the interface attribute, here is just a sign function. In defining a tag to an AOP class (TransactionAttribute) to define a method Getobjectsink, return an instance of an inherited interface IMessageSink class (TRANSACTIONAOP).
2, to define an inheritance interface IMessageSink class (TRANSACTIONAOP), this class is to implement AOP is the core code, you can do real code before or after the implementation of some of your own code, such as logging, caching, transactions and so on.
3. In the class to be used, the following conditions must be met:
1. Inheriting class ContextBoundObject
2, classes and methods are to add the corresponding characteristics we defined previously (TransactionAttribute, Transactionmethodattribute).
. NET website & System Development Technology Learning Exchange Group: 533829726
This site is reproduced in addition to the article, are the original site or translation, welcome any form of reprint, but please be sure to indicate the source, respect for the work of others, create a harmonious network environment.
Reprint Please specify: The article reprinted from: Blue Fox software studio»c#. NET implementation of AOP technology--AOP Transaction Implementation examples using ContextBoundObject and attribute
This article is titled: C #. NET implementation of AOP technology--AOP Transaction Implementation examples using ContextBoundObject and attribute
This address: http://www.lanhusoft.com/Article/240.html
C#. NET implementation of AOP technology--AOP Transaction Implementation examples using ContextBoundObject and attribute