Puresharp
Puresharp is a Framework that provides the essential APIs (AOP, IOC, etc...) to productively build high quality (.NET 4.5.2+ & .NET Core 2.1+) applications through reliability, scalability and performance without no compromise
Install / Use
/learn @Puresharper/PuresharpREADME
Puresharp API .NET
Puresharp is a set of features for .NET 4.5.2+ / .NET Core 2.1 to improve productivity by producing flexible and efficient applications.
Overview
Puresharp mainly provides architectural tools to build the basics of professional applications :
- Dependency Injection Container
- Aspect Oriented Programming
- Metadata Reflection API
This framework is divided into 2 parts :
IPuresharp is a nuget package dedicated to rewrite assemblies (using Mono.Cecil) to allow them to be highly customizable at runtime. IPuresharp won't add a new library reference to assmblies, but only include a post build process to automatically rewrite assemblies just after success build.
Install-Package IPuresharp -Version 5.0.5
It can be used manually with command line to manage third party assemblies
IPuresharp.exe "FullnameToAssembly.dll"
Puresharp is a nuget package offering various features useful for designing a healthy and productive architecture. This package also includes all the artillery to easily handle the elements that brings the IL writer IPuresharp. The nuget package add a library (Puresharp.dll) without any other depencies.
Install-Package Puresharp -Version 5.0.5
note : It is recommanded to install IPuresharp nuget package in all projects and install Puresharp nuget package only in project where you explicitly need it.
Dependency Injection Container
The global workflow of the DI Container is similar to others : setup a composition, create a container and instantiate from container some components.
Preview
Example of interfaces to configure
public interface IA
{
}
public interface IB
{
}
public interface IC
{
}
Example of implementations to bind to interfaces
public class A : IA
{
public A(IB b, IC c)
{
}
}
public class B : IB
{
public B(IC c, int constant)
{
}
}
public class C : IC
{
}
Create a composition
var _composition = new Composition();
Setup composition for IA, IB, IC whith respectivily A, B, C
_composition.Setup<IA>(() => new A(Metadata<IB>.Value, Metadata<IC>.Value), Instantiation.Multiton);
_composition.Setup<IB>(() => new B(Metadata<IC>.Value, 28), Instantiation.Multiton);
_composition.Setup<IC>(() => new C(), Instantiation.Multiton);
Create a container from composition setup
var _container = _composition.Materialize();
Instantiate a module of IA from container
using (var _module = _container.Module<IA>())
{
var _ia = _module.Value;
}
note : module is IDisposable and crontrol lifecycle for all dependencies.
FAQ
-
How is managed lifecycle for dependencies? When a module is setup into composition, instantiation mode is required and can be Singleton (a single instance with a lifecycle related to container), Multiton (a new instance for each module with lifecycle related to module itself) or Volatile (always a new instance with lifecycle related to owner module). Container and Module are both IDisposable to release created components.
-
Sould my interfaces implement IDisposable to match with lifecycle management? On the contrary, the interface of a component should never implement the IDisposable interface which is a purely infrastructure concern. Only implementations could possibly be. The container makes sure to dispose the implementations properly when it implements the IDisposable interface.
-
Why using lambda expression to configure components instead of classic generic parameter? Lambda expression offer a way to target constructor to use, specify when to use dependencies and capture constant.
-
How dependency is configured? Simply use Metadata<T>.Value in expression when you need to get back dependency from container.
-
Is constructor injection prevent cyclic reference between component? No, cyclic references are a feature. When an instance is created, it is not really the case, a lazy proxy instance is prepared to minimize unused resources retention and allow cyclic references.
-
In preview, only constructors are used to setup component, is it limited to constructor injection? No, expressions are totally open. You can inject static methods, constructors, members and even mix differents styles.
Aspect Oriented Programming
Workflow :
- Identify group of methods by defining a Pointcut
- Specify an Aspect by defining some Advices
- Instantiate the Aspect and Weave it into Pointcut
Preview
Example of interface
[AttributeUsage(AttributeTargets.Method)]
public class Read : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
public class Operation : Attribute
{
}
public interface IService
{
[Operation]
void SaveComment(int id, string text);
[Read]
[Operation]
string GetComment(int id);
}
Example of implementation
public class Service : IService
{
public void SaveComment(int id, string text)
{
}
public string GetComment(int id)
{
return null;
}
}
Supposed we want to log all readonly operations. For that, we have to define a Pointcut that represent all methods that are readonly operation (where Read attribute and Operation attribute are placed)
public class ReadonlyOperation : Pointcut.And<Pointcut<Operation>, Pointcut<Read>>
{
}
Define an Advice to log before whith Trace.WriteLine for exemple when calling methods
public class Log : IAdvice
{
private MethodBase m_Method;
public Log(MethodBase method)
{
this.m_Method = method;
}
public void Instance<T>(T instance)
{
}
public void Argument<T>(ref T value)
{
}
public void Begin()
{
Trace.WriteLine(this.m_Method);
}
public void Await(MethodInfo method, Task task)
{
}
public void Await<T>(MethodInfo method, Task<T> task)
{
}
public void Continue()
{
}
public void Throw(ref Exception exception)
{
}
public void Throw<T>(ref Exception exception, ref T value)
{
}
public void Return()
{
}
public void Return<T>(ref T value)
{
}
public void Dispose()
{
}
}
Define an Aspect that use log Advice
public class Logging : Aspect
{
override public IEnumerable<Advisor> Manage(MethodBase method)
{
yield return Advice
.For(method)
.Around(() => new Log(method));
}
}
Instantiate Aspect and weave it to our ReadonlyOperation Pointcut
var _logging = new Logging();
_logging.Weave<ReadonlyOperation>();
Congratulation, the logging Aspect in now injected to all readonly operation contract.
Sample
Here a set of sample to let see differents way to create and advisor.
public class Logging : Aspect
{
override public IEnumerable<Advisor> Manage(MethodBase method)
{
//Use classic interceptor to create an 'Around' advisor (place break points in interceptor methods to test interception).
yield return Advice
.For(method)
.Around(() => new Interceptor());
//Use linq expression to generate a 'Before' advisor.
yield return Advice
.For(method)
.Before(invocation =>
{
return Expression.Call
(
Metadata.Method(() => Console.WriteLine(Metadata<string>.Value)),
Expression.Constant($"Expression : { method.Name }")
);
});
//Use linq expression to generate a 'Before' advisor.
yield return Advice
.For(method)
.Before
(
Expression.Call
(
Metadata.Method(() => Console.WriteLine(Metadata<string>.Value)),
Expression.Constant($"Expression2 : { method.Name }")
)
);
//Use ILGeneration from reflection emit API to generate a 'Before' advisor.
yield return Advice
.For(method)
.Before(advice =>
{
advice.Emit(OpCodes.Ldstr, $"ILGenerator : { method.Name }");
advice.Emit(OpCodes.Call, Metadata.Method(() => Console.WriteLine(Metadata<string>.Value)));
});
//Use simple Action to generate a 'Before' advisor.
yield return Advice
.For(method)
.Before(() => Console.WriteLine($"Action : { method.Name }"));
//Use an expression to generate an 'After-Returning-Value' Advisor
yield return Advice
.For(method)
.After()
.Returning()
.Value(_Execution =>
{
return Expression.Call
(
Metadata.Method(() => Console.WriteLine(Metadata<string>.Value)),
Expression.Call
Related Skills
product-manager-skills
31PM skill for Claude Code, Codex, Cursor, and Windsurf: diagnose SaaS metrics, critique PRDs, plan roadmaps, run discovery, and coach PM career transitions.
devplan-mcp-server
3MCP server for generating development plans, project roadmaps, and task breakdowns for Claude Code. Turn project ideas into paint-by-numbers implementation plans.
