Puresharp는 유연하고 효율적인 애플리케이션을 생성하여 생산성을 향상시키는 .NET 4.5.2+/.NET Core 2.1용 기능 세트입니다.
Puresharp는 주로 전문 애플리케이션의 기본을 구축하기 위한 아키텍처 도구를 제공합니다.
이 프레임워크는 두 부분으로 나뉩니다.
IPuresharp는 어셈블리를 다시 작성( Mono.Cecil 사용)하여 런타임 시 고도로 사용자 정의할 수 있도록 하는 전용 너겟 패키지입니다. IPuresharp는 어셈블리에 새 라이브러리 참조를 추가하지 않지만 빌드 성공 직후 어셈블리를 자동으로 다시 작성하는 빌드 후 프로세스만 포함합니다.
Install-Package IPuresharp -Version 5.0.5
타사 어셈블리를 관리하기 위해 명령줄에서 수동으로 사용할 수 있습니다.
IPuresharp.exe "FullnameToAssembly.dll"
Puresharp는 건강하고 생산적인 아키텍처를 설계하는 데 유용한 다양한 기능을 제공하는 너겟 패키지입니다. 이 패키지에는 IL 작가 IPuresharp를 가져오는 요소를 쉽게 처리할 수 있는 모든 포병도 포함되어 있습니다. nuget 패키지는 다른 종속성 없이 라이브러리(Puresharp.dll)를 추가합니다.
Install-Package Puresharp -Version 5.0.5
참고: 모든 프로젝트에 IPuresharp nuget 패키지를 설치하고 명시적으로 필요한 프로젝트에만 Puresharp nuget 패키지를 설치하는 것이 좋습니다.
DI 컨테이너의 글로벌 워크플로우는 다른 작업 흐름과 유사합니다. 구성을 설정하고, 컨테이너를 생성하고, 컨테이너에서 일부 구성 요소를 인스턴스화합니다.
구성할 인터페이스의 예
public interface IA
{
}
public interface IB
{
}
public interface IC
{
}
인터페이스에 바인딩하기 위한 구현의 예
public class A : IA
{
public A(IB b, IC c)
{
}
}
public class B : IB
{
public B(IC c, int constant)
{
}
}
public class C : IC
{
}
컴포지션 만들기
var _composition = new Composition();
IA, IB, IC 각각 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);
컴포지션 설정에서 컨테이너 만들기
var _container = _composition.Materialize();
컨테이너에서 IA 모듈 인스턴스화
using (var _module = _container.Module<IA>())
{
var _ia = _module.Value;
}
참고: 모듈은 모든 종속성에 대한 IDisposable 및 crontrol 수명 주기입니다.
종속성에 대한 관리 수명주기는 어떻게 되나요? 모듈이 구성으로 설정되면 인스턴스화 모드가 필요하며 Singleton (컨테이너와 관련된 수명주기가 있는 단일 인스턴스), Multiton (모듈 자체와 관련된 수명주기가 있는 각 모듈에 대한 새 인스턴스) 또는 Volatile (항상 새 인스턴스)일 수 있습니다. 소유자 모듈과 관련된 수명주기 포함). 컨테이너와 모듈은 모두 생성된 구성 요소를 해제하기 위한 IDisposable입니다.
내 인터페이스가 수명주기 관리와 일치하도록 IDisposable을 구현했습니까? 반대로 구성 요소의 인터페이스는 순전히 인프라 문제인 IDisposable 인터페이스를 구현해서는 안 됩니다. 구현만이 가능할 수 있습니다. 컨테이너는 IDisposable 인터페이스를 구현할 때 구현을 적절하게 삭제합니다.
구성요소를 구성하기 위해 기존 일반 매개변수 대신 람다 표현식을 사용하는 이유는 무엇입니까? 람다 표현식은 사용할 대상 생성자를 지정하고, 종속성을 사용할 시기를 지정하고, 상수를 캡처하는 방법을 제공합니다.
종속성은 어떻게 구성됩니까? 컨테이너에서 종속성을 다시 가져와야 하는 경우 표현식에 Metadata<T>.Value를 사용하면 됩니다.
생성자 주입으로 인해 구성 요소 간의 순환 참조가 방지됩니까? 아니요, 순환 참조는 기능입니다. 인스턴스가 생성될 때 실제로는 그렇지 않습니다. 지연 프록시 인스턴스는 사용되지 않는 리소스 보유를 최소화하고 순환 참조를 허용하도록 준비됩니다.
미리보기에서는 구성 요소를 설정하는 데 생성자만 사용됩니다. 생성자 주입으로 제한됩니까? 아니요, 표현은 완전히 열려 있습니다. 정적 메서드, 생성자, 멤버를 삽입할 수 있으며 다양한 스타일을 혼합할 수도 있습니다.
작업 흐름:
인터페이스의 예
[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);
}
구현 예
public class Service : IService
{
public void SaveComment(int id, string text)
{
}
public string GetComment(int id)
{
return null;
}
}
모든 읽기 전용 작업을 기록한다고 가정합니다. 이를 위해 읽기 전용 작업인 모든 메서드(Read 속성과 Operation 속성이 배치된 위치)를 나타내는 Pointcut을 정의해야 합니다.
public class ReadonlyOperation : Pointcut.And<Pointcut<Operation>, Pointcut<Read>>
{
}
예를 들어 메서드를 호출할 때 Trace.WriteLine을 사용하여 전에 기록할 조언을 정의합니다.
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()
{
}
}
Log Advice를 사용하는 Aspect 정의
public class Logging : Aspect
{
override public IEnumerable<Advisor> Manage(MethodBase method)
{
yield return Advice
.For(method)
.Around(() => new Log(method));
}
}
Aspect를 인스턴스화하고 이를 ReadonlyOperation Pointcut 에 엮습니다.
var _logging = new Logging();
_logging.Weave<ReadonlyOperation>();
축하합니다. 이제 모든 읽기 전용 작업 계약에 로깅 측면이 주입되었습니다.
다음은 다양한 생성 방법과 조언자를 볼 수 있는 샘플 세트입니다.
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
(
Metadata.Method(() => string.Concat(Metadata<string>.Value, Metadata<string>.Value)),
Expression.Constant("Returned Value : "),
Expression.Call(_Execution.Return, Metadata<object>.Method(_Object => _Object.ToString()))
)
);
});
//Validate an email parameter value.
yield return Advice
.For(method)
.Parameter<EmailAddressAttribute>()
.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); }
});
}
}
여러 Aspect를 동일한 Pointcut에 엮을 수 있나요? 네, 직조 순서에 주의하세요.
Pointcut에서 Aspect를 어떻게 제거할 수 있나요? Pointcut 에서 Aspect를 제거하기 위해 Aspect 에 Release 메소드가 정의되어 있습니다.
Pointcut을 정의하는 데 속성이 필요합니까? 아니요, Pointcut은 Pointcut 에서 직접 상속하여 정의할 수 있으며 MethodBase를 단일 인수로 사용하고 메서드가 Pointcut 범위에 있는지 여부를 나타내는 부울을 반환하는 추상 메서드 Match를 구현합니다.
IPuresharp를 사용해야 하는 이유는 무엇입니까? 차단은 IPuresharp를 기반으로 합니다. 실제로 IPuresharp는 투명 기능과 숨겨진 기능을 주입하여 런타임에 전체 실행 제어 권한을 부여함으로써 어셈블리를 "건축가 친화적"으로 만들기 위해 CIL을 다시 작성하는 빌드 작업을 추가합니다.
생성자를 가로챌 수 있나요? 그렇다면 어떻게 구현합니까? 생성자 가로채기가 지원되며 유형을 첫 번째 인수로 선언하고 반환 유형으로 void를 선언하는 다른 메서드처럼 처리됩니다.
일반 유형 및 메소드가 지원됩니까? 일반 유형 및 메소드는 주입을 통해 완벽하게 지원됩니다.
비동기 메서드를 가로챌 수 있나요? 비동기 메서드는 주입을 통해 완전히 지원되며 각 비동기 단계를 가로채는 방법을 제공합니다.