Application of Attribute Programming in .NET (6)
(Bear). In the design of the .NET Framework intercepting mechanism, there is a variety of message receivers between the client and the object, which make up a linked list, the process of the client's call object, and the call return implementation interception. You can customize your own message receiver, insert them into the list, to complete your pre-processing and post-processing of a call. So how is the call interception to architecture or how to implement it?
There are two calls in .NET, one is a cross-sufficiency domain (App Domain), one is a convexT, both calls through the intermediate agent (Proxy), the agent is divided into two parts: Transparent agent and actual agent. The transparent agent exposes the same common entry point as an object. When the customer calls the transparent agent, the transparent agent converts the frame in the stack into a message (the object of the implementation IMessage interface mentioned in the previous section), the message contains the method name and Parameter and other attribute sets, then pass the message to the actual agent, take two cases: In the case of a cross-application domain, the actual agent uses a formatter to serialize the message, then put it in the remote channel; cross across In the case of a context environment, the actual agent does not have to know the formatter, channel, and context interceptor, it only needs to intercept the call before passing the message before, then it passes the message to a message receiver (implementing IMESSAGESINK) Each receiver knows its next receiver that passes the message to the next receiver after it is processed (forward), until the last receiver of the linked list, the last receiver The stack creator, which restores the message as a stack frame, then calls the object, when the modified method is returned, the stack creator converts the result to a message, and the message receiver that calls it, then the message along the original Link tables go back, the message receiver on each chain is processed after the return message. The first receiver until the first receiver of the list, the first receiver passed the message back to the actual agent, and the actual agent passed the message to the transparent agent, and the latter placed the message back into the client's stack. From the above description We see that the message crossing the context does not need to format, CLR uses an internal channel called CrossContextChannel, which is also a message receiver.
There are several types of message receivers, a call interception can be performed on the server side, and the server-side receiver intercepts all the calls to the objects in the server context, while doing some pre-processing and post-processing. The client's receiver intercepts all the calls of the following environment, and also do some pre-processing and post-processing. The server is responsible for the installation of the server-side receiver, intercepting the receiver accessed to the server-side context environment is called the Server Context Environment Receiver, and the receiver that intercepts the actual object is an object receiver. The client receiver installed through the customer is called a client context environment receiver, and the client receiver installed is referred to as an envoy receiver, and the receiver only intercepts those objects related to it. The first receiver of the client's last receiver and server-side is an example of a CrossContextChannel type. Different types of receivers make up different segments, each segment is installed, called a receiver called a terminator, and the terminator functions as a message of this segment to the next segment. The last terminator in the server context environment segment is ServerContextTerminatorsink. If you call NextSink on the terminator, it will return a NULL, and their behavior is like a dead end, but the private fields of the next receiver object are saved inside them. We roughly introduced the implementation mechanism of the .NET Framework's object call interception, the purpose is to let everyone have a understanding of this mechanism. Now it is time to implement our code, through the implementation of the code, you can see how messages are processed process. The first is to define a receiver CALLTRACESINK for our program:
//Tracecontext.cs
Using system;
Using system.Runtime.Remoting.Contexts;
Using system.runtime.remoting.Messaging;
Using system.Runtime.Remoting.actiVation;
Namespace NiwalkerDemo
{
Public Class CallTracesink: iMessagesink // Implement iMessagesink
{
Private iMessagesink NextSink; // Save the next receiver
// Initialize the next receiver in the constructor
Public CallTracesink (iMessagesink Next)
{
Nextsink = next;
}
// The iMessageSink interface properties that must be implemented
Public iMessagesink NextSink
{
get
{
Return NEXTSINK;
}
}
/ / Implement the interface method of iMessages, which is called when the message is delivered.
Public iMessage SyncProcessMessage (IMESSAGE MSG)
{
// Intercept message, do before doing
Preprocess (MSG);
/ / Pass the message to the next receiver
IMessage Retmsg = NextSink.SyncProcessMessage (MSG);
// Call the return time to intercept, and proceed
PostProcess (MSG, RETMSG);
Return RETMSG;
}
// IMESSAGESINK interface method for asynchronous processing, we don't achieve asynchronous processing, so simply return NULL,
// No matter what synchronization or asynchronous, this method needs to be defined.
Public IMESSAGECTRL AsyncProcessMessage (iMessage MSG, IMESSAGESINK Replysink)
{
Return NULL;
}
// Our pre-treatment method, for checking inventory, for simplified purposes, we write check inventory and send mail together, // In actual implementation, you may also need to bind the Inventory object to one Context environment,
/ / In addition, the send mail can be designed as another receiver and then install by NextSink.
Private Void Preprocess (IMESSAGE MSG)
{
/ / Check if it is a way to call, we only intercept the Submit method of the ORDER.
ImethodCallMessage call = msg as iMethodCallMessage;
IF (call == null)
Return;
if (Call.MethodName == "Submit")
{
String product = call.getarg (0) .tostring (); // Get the first parameter of the Submit method
INT Qty = (int) call.getarg (1); // Get the second parameter of the Submit method
// Call Inventory Check Inventory stock
IF (new inventory (). Checkout (Product, Qty))
Console.writeline ("Order Availible");
Else
{
Console.writeline ("Order Unvailible");
Sendemail ();
}
}
}
// Post-processing method, used to record order submission information, can also be recorded as a receiver
// We have treated here, just for demonstration
Private void PostProcess (iMessage MSG, IMESSAGE RETMSG)
{
ImethodCallMessage call = msg as iMethodCallMessage;
IF (call == null)
Return;
Console.writeline ("Log Order Information");
}
Private void sendemail ()
{
Console.WriteLine ("Send Email to Manager);
}
}
...
Next we define the properties of the context environment, the context environmental properties must implement the corresponding interfaces based on the receiver type you want to create, such as: If you create a server context environment receiver, you must implement the ICONTRIBUTSERVERCONTEXTSINK interface.
...
Public Class CalltraceProperty: iconTextProperty, IcontributeObjectsink
{
Public CallTraceProperty ()
{
}
// ICONTRIBUTEOBJECTSINK interface method, instantiate message receiver
Public IMESSAGESINK GETOBJECTSINK (MarshalByrefObject Obj, iMessagesink Next)
{
Return New CallTraceSink (Next);
}
// IconTextProperty interface method, if this method returns Ture, activate the object in a new context environment
Public Bool IsNewContextok (Context NewctX)
{
Return True;
}
// IconTextProperty interface method provides advanced use
Public void freeze (context newctx)
{
}
// iconTextProperty interface properties
Public String Name
{
Get {return "Ordertrace";}}
}
...
Finally ContextAttribute
...
[AttributeUsage (AttributeTargets.class)]
Public Class CallTracettribute: ContextAttribute
{
Public CallTracettribute (): Base ("CallTrace")
{
}
/ / Rebarry the ContextAttribute method, create a context environment property
Public override void getpropertiesfornewcontext (iconstructioncallmessage ctormsg)
{
CTORMSG.CONTEXTPROPERTIES.ADD (New CallTraceProperty ());
}
}
}
In order to see how the Submit method that calls the Order object is intercepted, we have changed the Order class slightly, and design it to the derived class of ContextBoundObject:
//INVENTORY.CS
//Order.cs
Using system;
Namespace NiwalkerDemo
{
[CallTrace]
Public Class Order: ContextBoundObject
{
...
Public Void Submit (String Product, Int Quantity)
{
THIS.PRODUCT = Product;
THIS.QUANTINTITY = quantity;
}
...
}
}
Client call code:
...
Public Class Appmain
{
Static void main ()
{
Order Order1 = New ORDER (100);
Order1.submit ("item1", 150);
Order Order2 = New Order (101);
Order2.Submit ("item2", 150);
}
}
...
The results show that we have successfully intercepted the SBUMIT of Order. It should be noted that the code here is merely used as a demo to the ContextAttribute application, which is a thick line. In the specific practice, everyone can design more exquisite.
Postscript: I originally wanted more introductions to Attribute, and I found something that I had to talk about. Allow me to discuss them in other topics. Thank you very much, you have patient reading this series. If the content introduced here is in your programming career, then I am very honored. Thank you again. (Full text)