Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/puresharper/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
https://github.com/puresharper/puresharp
advice aop architecture aspects composition csharp framework injection interception ioc net proxy
Last synced: 2 months ago
JSON representation
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
- Host: GitHub
- URL: https://github.com/puresharper/puresharp
- Owner: Puresharper
- License: mit
- Created: 2017-05-07T00:26:20.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2019-01-31T18:47:12.000Z (almost 6 years ago)
- Last Synced: 2024-11-12T12:52:51.592Z (2 months ago)
- Topics: advice, aop, architecture, aspects, composition, csharp, framework, injection, interception, ioc, net, proxy
- Language: C#
- Homepage:
- Size: 175 KB
- Stars: 145
- Watchers: 14
- Forks: 17
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# 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 APIThis framework is divided into 2 parts :
- **IPuresharp** [![NuGet](https://img.shields.io/nuget/v/IPuresharp.svg)](https://www.nuget.org/packages/IPuresharp)IPuresharp is a nuget package dedicated to rewrite assemblies (using **[Mono.Cecil](https://github.com/jbevain/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** [![NuGet](https://img.shields.io/nuget/v/Puresharp.svg)](https://www.nuget.org/packages/Puresharp)
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 interfacespublic 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(() => new A(Metadata.Value, Metadata.Value), Instantiation.Multiton);
_composition.Setup(() => new B(Metadata.Value, 28), Instantiation.Multiton);
_composition.Setup(() => new C(), Instantiation.Multiton);
Create a container from composition setupvar _container = _composition.Materialize();
Instantiate a module of IA from container
using (var _module = _container.Module())
{
var _ia = _module.Value;
}
_note : module is [IDisposable](https://msdn.microsoft.com/en-us/library/system.idisposable(v=vs.110).aspx) 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 implementationpublic 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>
{
}
Define an **Advice** to log before whith Trace.WriteLine for exemple when calling methodspublic class Log : IAdvice
{
private MethodBase m_Method;public Log(MethodBase method)
{
this.m_Method = method;
}public void Instance(T instance)
{
}public void Argument(ref T value)
{
}public void Begin()
{
Trace.WriteLine(this.m_Method);
}public void Await(MethodInfo method, Task task)
{
}public void Await(MethodInfo method, Task task)
{
}
public void Continue()
{
}
public void Throw(ref Exception exception)
{
}public void Throw(ref Exception exception, ref T value)
{
}public void Return()
{
}public void Return(ref T value)
{
}
public void Dispose()
{
}
}
Define an **Aspect** that use log **Advice**public class Logging : Aspect
{
override public IEnumerable 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();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 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.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.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.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.Value)),
Expression.Call
(
Metadata.Method(() => string.Concat(Metadata.Value, Metadata.Value)),
Expression.Constant("Returned Value : "),
Expression.Call(_Execution.Return, Metadata.Method(_Object => _Object.ToString()))
)
);
});
//Validate an email parameter value.
yield return Advice
.For(method)
.Parameter()
.Validate((_Parameter, _Attribute, _Value) =>
{
if (_Value == null) { throw new ArgumentNullException(_Parameter.Name); }
try { new MailAddress(_Value.ToString()); }
catch (Exception exception) { throw new ArgumentException(_Parameter.Name, exception); }
});
}
}
#### FAQ- **Can I weave multiple Aspects into the same Pointcut?**
_Yes, just be carefull about weaving order._- **How can I remove an Aspect from a Pointcut?**
_There is a **Release** method defined in **Aspect** to get rid of **Aspect** from **Pointcut**._- **Are attributes required to define Pointcut?**
_No, **Pointcut** can be defined by directly inherits from **Pointcut** and implement the abstract method **Match** that take a **MethodBase** as single argument and return a boolean to indicate if a method is in **Pointcut** scope._- **Why I have to use IPuresharp?**
_Interception is based on IPuresharp. Indeed IPuresharp add a build action to rewrite CIL to make assembly "Architect Friendly" by injecting transparents and hidden features to to grant full execution control at runtime._- **Can I intercept constructor? If yes, how do I implement it?**
_Constructor interception is supported and is treated like another method with declaring type as first argument and void for return type._- **Is generic types and methods supported?**
_Generic types and methods ares fully supported by injection._- **Can I intercept async methods?**
_Async methods ares fully supported by injection and offer a way to intercept each asynchronous steps._## More
- [https://en.m.wikipedia.org/wiki/Aspect-oriented_programming](https://en.m.wikipedia.org/wiki/Aspect-oriented_programming)
- [https://en.m.wikipedia.org/wiki/Dependency_injection](https://en.m.wikipedia.org/wiki/Dependency_injection)
- [https://en.m.wikipedia.org/wiki/SOLID](https://en.m.wikipedia.org/wiki/SOLID)
- [https://en.m.wikipedia.org/wiki/Domain-driven_design](https://en.m.wikipedia.org/wiki/Domain-driven_design)## Contribution
- Feedbacks are wellcome, don't hesitate to tell me if any improvements seems to be cool
- Any pull requests for patching or correct my bad english are wellcome too
- You can also donate to help financially maintain the project : **3D8txm4gMJDtti7tpcYnndLDfxpADkLggn**