Overview
When you write a task, your components are instantiated and invoked by the PostSharp Platform. But there are situations where you would like to instantiate the PostSharp Platform yourself. For instance when you develop an application server. See for instance COM+. You can define transactional behavior using the management GUI. Suppose you want to do something else without COM+. You will need to weave assemblies at runtime, exactly before they are loaded into memory for execution.
Even if you cannot weave at compile time, PostSharp is still a good choice. But you need to host it in your application server.
PostSharp comes with two standard hosts:
- The PostSharp MSBuild task.
- The PostSharp command-line utility.
Additionally, the PostSharp.Samples.Host sample illustrates how to host PostSharp to perform runtime weaving.
The PostSharp Object
The PostSharp Object is interface between the host and the
Platform Infrastructure. Its semantics are defined by the IPostSharpObject
interface. They are very simple: a single method
InvokeProjects that allows to execute a set of
projects.
In order to get an instance of the PostSharp Object, you should
use the CreateInstance method of the PostSharpObject
class. This method requires that you pass a PostSharpObjectSettings
object. It allows you to determine whether PostSharp will
share the current application domain or will create its
own.
When to use a Private Application Domain?
You should use a Private Application Domain when you do not want original assemblies to be loaded in the current application domain. If you use a private application domain, you will be able, after you do not need PostSharp any more, to unload it and all assemblies it contains.
For instance, we use a private application domain for the PostSharp MSBuild task, because we are not allowed to load into the MSBuild application domain assemblies that are being built. But for the console application, we can share the main application domain. In runtime scenarios, you will probably want to have a private application domain for security reasons (you do not want the woven code to 'see' the unwoven code).
The PostSharp Host
Conceptually, the PostSharp Host is the software component that instantiates and invokes the PostSharp Object. Technically, the PostSharp Host should implement the IPostSharpHost interface. This interface is invoked by the PostSharp Object principally when it does not know what to do with an assembly.
The IPostSharpHost interface is useful when
assemblies and loaded and processed dynamically as they are
requested by the runtime engine. This is typically the case for
runtime execution scenarios. In compile-time scenarios, there is no
meaningful interaction between the PostSharp Platform and the host.
That's why PostSharp provides a default implementation of the
IPostSharpHost interface for compile-time
scenarios.
Additionally to providing (even implicitly) a
IPostSharpHost implementation, the host should
register to the Message event of the
Messenger class in order to propagate messages to the
proper sink.
The Remote and the Local Host
The implementation of this interface resides in the host application domain. For this reason, taking from the point of view of PostSharp, it is called the remote host. If your host need to execute some logic in the PostSharp application domain, you need to implement a local host.
The local host resides in the PostSharp application domain and lays between the PostSharp Object and the remove host. The PostSharp Object never contacts directly the remote host. It goes always through the local host.
PostSharp provides a default implementation in the PostSharpLocalHost class. The default behavior is just to call the remote host. If you want to customize this behavior, you can derive this class and use the LocalHostImplementation property of the PostSharpObjectSettings class to tell PostSharp to use your class instead of the default one.
Custom local hosts can also be used to react to the events
defined on the PostSharpObject.
Interaction between the Platform and the Host
The PostSharp Object has the possibility to process modules in deep-first order. That is, referenced assemblies are processed before referencing ones. If you start a program, all its dependencies would be processed fist and the program in itself would be processed as the last.
This feature is useful when the transformation of an assembly depends on a transformation or an analysis performed on dependencies, for instance if you want to modify public fields into properties.
Deep-first processing is enabled (for each module independently) by the ProcessDependenciesFirst property of the ProjectInvocationParameters class.
When deep-first processing is enabled, the Platform will contact the host for each dependency it finds and has no information about yet:
-
ResolveAssemblyReference- Where is the referenced assembly located? -
GetProjectInvocationParameters- How this assembly/module should be processed?
Assembly Renaming
If you use PostSharp to modify an assembly at runtime, you will probably need to change their identity. There are two good reasons:
- You will probably not be able to resign strongly named assemblies, and the VRE will refuse to load a assembly that has a public key but is not signed using the private key.
- We have to ensure that the VRE will not load the unmodified
assembly instead of the modified one. However that's what will
happen if the assembly is located in the probe path or in the GAC,
because the
AssemblyResolveevent that we can customize is called when standard heuristics have failed.
For this reason, it is necessary to change the name of woven assemblies both in AssemblyManifestDeclaration and AssemblyRefDeclaration.
PostSharp can do it automatically if the OverwriteAssemblyNames property of the PostSharpObjectSettings object is set.
Order of Project Execution
When you process many modules in a single call of the IPostSharpObject.InvokeProjects
method, tasks can be executed in different order.
First, remember that tasks are grouped in phases. The application-level configuration file standardly define four phases: load, analyze, transform and generate. All tasks that implement the Execute method should belong to one phase.
Say we have phases H1 ... H4 and projects P1 ... P7. Two orders of project execution are available:
-
Phased execution: All tasks of all projects in a single
phase are executed before the tasks of the next phases are
executed. It is the order:
H1(P1), H1(P2), ..., H1(P7), H2(P1), H2(P2), ..., H2(P7), ..., H4(P1), H4(P2), ..., H4(P7).
-
Sequential execution: All tasks of a project are
executed before the tasks of a next project are executed. The
order:
H1(P1), ... H4(P1), H1(P2), ... H4(P2), ..., H1(P7), H4(P7).
The order of execution is influenced by the PostSharpObjectSettings.ProjectExecutionOrder property.