Coding Styles
This commit is contained in:
@@ -1,204 +1,202 @@
|
||||
namespace Unosquare.Swan.Components
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Unosquare.Swan.Components {
|
||||
/// <summary>
|
||||
/// A fixed-size buffer that acts as an infinite length one.
|
||||
/// This buffer is backed by unmanaged, very fast memory so ensure you call
|
||||
/// the dispose method when you are done using it.
|
||||
/// Only for Windows.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.IDisposable" />
|
||||
public sealed class CircularBuffer : IDisposable {
|
||||
private readonly Object _syncLock = new Object();
|
||||
private IntPtr _buffer = IntPtr.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// A fixed-size buffer that acts as an infinite length one.
|
||||
/// This buffer is backed by unmanaged, very fast memory so ensure you call
|
||||
/// the dispose method when you are done using it.
|
||||
/// Only for Windows.
|
||||
/// Initializes a new instance of the <see cref="CircularBuffer"/> class.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.IDisposable" />
|
||||
public sealed class CircularBuffer : IDisposable
|
||||
{
|
||||
private readonly object _syncLock = new object();
|
||||
private IntPtr _buffer = IntPtr.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CircularBuffer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="bufferLength">Length of the buffer.</param>
|
||||
public CircularBuffer(int bufferLength)
|
||||
{
|
||||
/// <param name="bufferLength">Length of the buffer.</param>
|
||||
public CircularBuffer(Int32 bufferLength) {
|
||||
#if !NET452
|
||||
if (Runtime.OS != Swan.OperatingSystem.Windows)
|
||||
throw new InvalidOperationException("CircularBuffer component is only available in Windows");
|
||||
#endif
|
||||
|
||||
Length = bufferLength;
|
||||
_buffer = Marshal.AllocHGlobal(Length);
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the capacity of this buffer.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The length.
|
||||
/// </value>
|
||||
public int Length { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current, 0-based read index.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The index of the read.
|
||||
/// </value>
|
||||
public int ReadIndex { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current, 0-based write index.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The index of the write.
|
||||
/// </value>
|
||||
public int WriteIndex { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an the object associated with the last write.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The write tag.
|
||||
/// </value>
|
||||
public TimeSpan WriteTag { get; private set; } = TimeSpan.MinValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the available bytes to read.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The readable count.
|
||||
/// </value>
|
||||
public int ReadableCount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of bytes that can be written.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The writable count.
|
||||
/// </value>
|
||||
public int WritableCount => Length - ReadableCount;
|
||||
|
||||
/// <summary>
|
||||
/// Gets percentage of used bytes (readbale/available, from 0.0 to 1.0).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The capacity percent.
|
||||
/// </value>
|
||||
public double CapacityPercent => 1.0 * ReadableCount / Length;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bytes into the target array.
|
||||
/// </summary>
|
||||
/// <param name="requestedBytes">The requested bytes.</param>
|
||||
/// <param name="target">The target.</param>
|
||||
/// <param name="targetOffset">The target offset.</param>
|
||||
/// <exception cref="System.InvalidOperationException">
|
||||
/// Exception that is thrown when a method call is invalid for the object's current state.
|
||||
/// </exception>
|
||||
public void Read(int requestedBytes, byte[] target, int targetOffset)
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (requestedBytes > ReadableCount)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Unable to read {requestedBytes} bytes. Only {ReadableCount} bytes are available");
|
||||
}
|
||||
|
||||
var readCount = 0;
|
||||
while (readCount < requestedBytes)
|
||||
{
|
||||
var copyLength = Math.Min(Length - ReadIndex, requestedBytes - readCount);
|
||||
var sourcePtr = _buffer + ReadIndex;
|
||||
Marshal.Copy(sourcePtr, target, targetOffset + readCount, copyLength);
|
||||
|
||||
readCount += copyLength;
|
||||
ReadIndex += copyLength;
|
||||
ReadableCount -= copyLength;
|
||||
|
||||
if (ReadIndex >= Length)
|
||||
ReadIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data to the backing buffer using the specified pointer and length.
|
||||
/// and associating a write tag for this operation.
|
||||
/// </summary>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="length">The length.</param>
|
||||
/// <param name="writeTag">The write tag.</param>
|
||||
/// <exception cref="System.InvalidOperationException">Unable to write to circular buffer. Call the Read method to make some additional room.</exception>
|
||||
public void Write(IntPtr source, int length, TimeSpan writeTag)
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (ReadableCount + length > Length)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Unable to write to circular buffer. Call the {nameof(Read)} method to make some additional room.");
|
||||
}
|
||||
|
||||
var writeCount = 0;
|
||||
while (writeCount < length)
|
||||
{
|
||||
var copyLength = Math.Min(Length - WriteIndex, length - writeCount);
|
||||
var sourcePtr = source + writeCount;
|
||||
var targetPtr = _buffer + WriteIndex;
|
||||
CopyMemory(targetPtr, sourcePtr, (uint) copyLength);
|
||||
|
||||
writeCount += copyLength;
|
||||
WriteIndex += copyLength;
|
||||
ReadableCount += copyLength;
|
||||
|
||||
if (WriteIndex >= Length)
|
||||
WriteIndex = 0;
|
||||
}
|
||||
|
||||
WriteTag = writeTag;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets all states as if this buffer had just been created.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
WriteIndex = 0;
|
||||
ReadIndex = 0;
|
||||
WriteTag = TimeSpan.MinValue;
|
||||
ReadableCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
if (_buffer == IntPtr.Zero) return;
|
||||
|
||||
Marshal.FreeHGlobal(_buffer);
|
||||
_buffer = IntPtr.Zero;
|
||||
Length = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fast pointer memory block copy function.
|
||||
/// </summary>
|
||||
/// <param name="destination">The destination.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="length">The length.</param>
|
||||
[DllImport("kernel32")]
|
||||
public static extern void CopyMemory(IntPtr destination, IntPtr source, uint length);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
this.Length = bufferLength;
|
||||
this._buffer = Marshal.AllocHGlobal(this.Length);
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the capacity of this buffer.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The length.
|
||||
/// </value>
|
||||
public Int32 Length {
|
||||
get; private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current, 0-based read index.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The index of the read.
|
||||
/// </value>
|
||||
public Int32 ReadIndex {
|
||||
get; private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current, 0-based write index.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The index of the write.
|
||||
/// </value>
|
||||
public Int32 WriteIndex {
|
||||
get; private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an the object associated with the last write.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The write tag.
|
||||
/// </value>
|
||||
public TimeSpan WriteTag { get; private set; } = TimeSpan.MinValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the available bytes to read.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The readable count.
|
||||
/// </value>
|
||||
public Int32 ReadableCount {
|
||||
get; private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of bytes that can be written.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The writable count.
|
||||
/// </value>
|
||||
public Int32 WritableCount => this.Length - this.ReadableCount;
|
||||
|
||||
/// <summary>
|
||||
/// Gets percentage of used bytes (readbale/available, from 0.0 to 1.0).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The capacity percent.
|
||||
/// </value>
|
||||
public Double CapacityPercent => 1.0 * this.ReadableCount / this.Length;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bytes into the target array.
|
||||
/// </summary>
|
||||
/// <param name="requestedBytes">The requested bytes.</param>
|
||||
/// <param name="target">The target.</param>
|
||||
/// <param name="targetOffset">The target offset.</param>
|
||||
/// <exception cref="System.InvalidOperationException">
|
||||
/// Exception that is thrown when a method call is invalid for the object's current state.
|
||||
/// </exception>
|
||||
public void Read(Int32 requestedBytes, Byte[] target, Int32 targetOffset) {
|
||||
lock(this._syncLock) {
|
||||
if(requestedBytes > this.ReadableCount) {
|
||||
throw new InvalidOperationException(
|
||||
$"Unable to read {requestedBytes} bytes. Only {this.ReadableCount} bytes are available");
|
||||
}
|
||||
|
||||
Int32 readCount = 0;
|
||||
while(readCount < requestedBytes) {
|
||||
Int32 copyLength = Math.Min(this.Length - this.ReadIndex, requestedBytes - readCount);
|
||||
IntPtr sourcePtr = this._buffer + this.ReadIndex;
|
||||
Marshal.Copy(sourcePtr, target, targetOffset + readCount, copyLength);
|
||||
|
||||
readCount += copyLength;
|
||||
this.ReadIndex += copyLength;
|
||||
this.ReadableCount -= copyLength;
|
||||
|
||||
if(this.ReadIndex >= this.Length) {
|
||||
this.ReadIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data to the backing buffer using the specified pointer and length.
|
||||
/// and associating a write tag for this operation.
|
||||
/// </summary>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="length">The length.</param>
|
||||
/// <param name="writeTag">The write tag.</param>
|
||||
/// <exception cref="System.InvalidOperationException">Unable to write to circular buffer. Call the Read method to make some additional room.</exception>
|
||||
public void Write(IntPtr source, Int32 length, TimeSpan writeTag) {
|
||||
lock(this._syncLock) {
|
||||
if(this.ReadableCount + length > this.Length) {
|
||||
throw new InvalidOperationException(
|
||||
$"Unable to write to circular buffer. Call the {nameof(Read)} method to make some additional room.");
|
||||
}
|
||||
|
||||
Int32 writeCount = 0;
|
||||
while(writeCount < length) {
|
||||
Int32 copyLength = Math.Min(this.Length - this.WriteIndex, length - writeCount);
|
||||
IntPtr sourcePtr = source + writeCount;
|
||||
IntPtr targetPtr = this._buffer + this.WriteIndex;
|
||||
CopyMemory(targetPtr, sourcePtr, (UInt32)copyLength);
|
||||
|
||||
writeCount += copyLength;
|
||||
this.WriteIndex += copyLength;
|
||||
this.ReadableCount += copyLength;
|
||||
|
||||
if(this.WriteIndex >= this.Length) {
|
||||
this.WriteIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
this.WriteTag = writeTag;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets all states as if this buffer had just been created.
|
||||
/// </summary>
|
||||
public void Clear() {
|
||||
lock(this._syncLock) {
|
||||
this.WriteIndex = 0;
|
||||
this.ReadIndex = 0;
|
||||
this.WriteTag = TimeSpan.MinValue;
|
||||
this.ReadableCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() {
|
||||
if(this._buffer == IntPtr.Zero) {
|
||||
return;
|
||||
}
|
||||
|
||||
Marshal.FreeHGlobal(this._buffer);
|
||||
this._buffer = IntPtr.Zero;
|
||||
this.Length = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fast pointer memory block copy function.
|
||||
/// </summary>
|
||||
/// <param name="destination">The destination.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="length">The length.</param>
|
||||
[DllImport("kernel32")]
|
||||
public static extern void CopyMemory(IntPtr destination, IntPtr source, UInt32 length);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,109 +1,101 @@
|
||||
namespace Unosquare.Swan.Components
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Unosquare.Swan.Components {
|
||||
/// <summary>
|
||||
/// Represents a CsProjFile (and FsProjFile) parser.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Based on https://github.com/maartenba/dotnetcli-init.
|
||||
/// </remarks>
|
||||
/// <typeparam name="T">The type of <c>CsProjMetadataBase</c>.</typeparam>
|
||||
/// <seealso cref="System.IDisposable" />
|
||||
public class CsProjFile<T>
|
||||
: IDisposable
|
||||
where T : CsProjMetadataBase {
|
||||
private readonly Stream _stream;
|
||||
private readonly Boolean _leaveOpen;
|
||||
private readonly XDocument _xmlDocument;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a CsProjFile (and FsProjFile) parser.
|
||||
/// Initializes a new instance of the <see cref="CsProjFile{T}"/> class.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Based on https://github.com/maartenba/dotnetcli-init.
|
||||
/// </remarks>
|
||||
/// <typeparam name="T">The type of <c>CsProjMetadataBase</c>.</typeparam>
|
||||
/// <seealso cref="System.IDisposable" />
|
||||
public class CsProjFile<T>
|
||||
: IDisposable
|
||||
where T : CsProjMetadataBase
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
private readonly bool _leaveOpen;
|
||||
private readonly XDocument _xmlDocument;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CsProjFile{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
public CsProjFile(string filename = null)
|
||||
: this(OpenFile(filename))
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CsProjFile{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <param name="leaveOpen">if set to <c>true</c> [leave open].</param>
|
||||
/// <exception cref="ArgumentException">Project file is not of the new .csproj type.</exception>
|
||||
public CsProjFile(Stream stream, bool leaveOpen = false)
|
||||
{
|
||||
_stream = stream;
|
||||
_leaveOpen = leaveOpen;
|
||||
|
||||
_xmlDocument = XDocument.Load(stream);
|
||||
|
||||
var projectElement = _xmlDocument.Descendants("Project").FirstOrDefault();
|
||||
var sdkAttribute = projectElement?.Attribute("Sdk");
|
||||
var sdk = sdkAttribute?.Value;
|
||||
if (sdk != "Microsoft.NET.Sdk" && sdk != "Microsoft.NET.Sdk.Web")
|
||||
{
|
||||
throw new ArgumentException("Project file is not of the new .csproj type.");
|
||||
}
|
||||
|
||||
Metadata = Activator.CreateInstance<T>();
|
||||
Metadata.SetData(_xmlDocument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the metadata.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The nu get metadata.
|
||||
/// </value>
|
||||
public T Metadata { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Saves this instance.
|
||||
/// </summary>
|
||||
public void Save()
|
||||
{
|
||||
_stream.SetLength(0);
|
||||
_stream.Position = 0;
|
||||
|
||||
_xmlDocument.Save(_stream);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_leaveOpen)
|
||||
{
|
||||
_stream?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static FileStream OpenFile(string filename)
|
||||
{
|
||||
if (filename == null)
|
||||
{
|
||||
filename = Directory
|
||||
.EnumerateFiles(Directory.GetCurrentDirectory(), "*.csproj", SearchOption.TopDirectoryOnly)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
if (filename == null)
|
||||
{
|
||||
filename = Directory
|
||||
.EnumerateFiles(Directory.GetCurrentDirectory(), "*.fsproj", SearchOption.TopDirectoryOnly)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(filename))
|
||||
throw new ArgumentNullException(nameof(filename));
|
||||
|
||||
return File.Open(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
||||
}
|
||||
}
|
||||
/// <param name="filename">The filename.</param>
|
||||
public CsProjFile(String filename = null)
|
||||
: this(OpenFile(filename)) {
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CsProjFile{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <param name="leaveOpen">if set to <c>true</c> [leave open].</param>
|
||||
/// <exception cref="ArgumentException">Project file is not of the new .csproj type.</exception>
|
||||
public CsProjFile(Stream stream, Boolean leaveOpen = false) {
|
||||
this._stream = stream;
|
||||
this._leaveOpen = leaveOpen;
|
||||
|
||||
this._xmlDocument = XDocument.Load(stream);
|
||||
|
||||
XElement projectElement = this._xmlDocument.Descendants("Project").FirstOrDefault();
|
||||
XAttribute sdkAttribute = projectElement?.Attribute("Sdk");
|
||||
String sdk = sdkAttribute?.Value;
|
||||
if(sdk != "Microsoft.NET.Sdk" && sdk != "Microsoft.NET.Sdk.Web") {
|
||||
throw new ArgumentException("Project file is not of the new .csproj type.");
|
||||
}
|
||||
|
||||
this.Metadata = Activator.CreateInstance<T>();
|
||||
this.Metadata.SetData(this._xmlDocument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the metadata.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The nu get metadata.
|
||||
/// </value>
|
||||
public T Metadata {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves this instance.
|
||||
/// </summary>
|
||||
public void Save() {
|
||||
this._stream.SetLength(0);
|
||||
this._stream.Position = 0;
|
||||
|
||||
this._xmlDocument.Save(this._stream);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() {
|
||||
if(!this._leaveOpen) {
|
||||
this._stream?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static FileStream OpenFile(String filename) {
|
||||
if(filename == null) {
|
||||
filename = Directory
|
||||
.EnumerateFiles(Directory.GetCurrentDirectory(), "*.csproj", SearchOption.TopDirectoryOnly)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
if(filename == null) {
|
||||
filename = Directory
|
||||
.EnumerateFiles(Directory.GetCurrentDirectory(), "*.fsproj", SearchOption.TopDirectoryOnly)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
if(String.IsNullOrWhiteSpace(filename)) {
|
||||
throw new ArgumentNullException(nameof(filename));
|
||||
}
|
||||
|
||||
return File.Open(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
namespace Unosquare.Swan.Components
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Unosquare.Swan.Components
|
||||
{
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a CsProj metadata abstract class
|
||||
/// to use with <c>CsProjFile</c> parser.
|
||||
@@ -17,7 +18,7 @@
|
||||
/// <value>
|
||||
/// The package identifier.
|
||||
/// </value>
|
||||
public string PackageId => FindElement(nameof(PackageId))?.Value;
|
||||
public String PackageId => this.FindElement(nameof(this.PackageId))?.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the assembly.
|
||||
@@ -25,7 +26,7 @@
|
||||
/// <value>
|
||||
/// The name of the assembly.
|
||||
/// </value>
|
||||
public string AssemblyName => FindElement(nameof(AssemblyName))?.Value;
|
||||
public String AssemblyName => this.FindElement(nameof(this.AssemblyName))?.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target frameworks.
|
||||
@@ -33,7 +34,7 @@
|
||||
/// <value>
|
||||
/// The target frameworks.
|
||||
/// </value>
|
||||
public string TargetFrameworks => FindElement(nameof(TargetFrameworks))?.Value;
|
||||
public String TargetFrameworks => this.FindElement(nameof(this.TargetFrameworks))?.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target framework.
|
||||
@@ -41,7 +42,7 @@
|
||||
/// <value>
|
||||
/// The target framework.
|
||||
/// </value>
|
||||
public string TargetFramework => FindElement(nameof(TargetFramework))?.Value;
|
||||
public String TargetFramework => this.FindElement(nameof(this.TargetFramework))?.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version.
|
||||
@@ -49,25 +50,25 @@
|
||||
/// <value>
|
||||
/// The version.
|
||||
/// </value>
|
||||
public string Version => FindElement(nameof(Version))?.Value;
|
||||
public String Version => this.FindElement(nameof(this.Version))?.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Parses the cs proj tags.
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments.</param>
|
||||
public abstract void ParseCsProjTags(ref string[] args);
|
||||
public abstract void ParseCsProjTags(ref String[] args);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the data.
|
||||
/// </summary>
|
||||
/// <param name="xmlDocument">The XML document.</param>
|
||||
public void SetData(XDocument xmlDocument) => _xmlDocument = xmlDocument;
|
||||
public void SetData(XDocument xmlDocument) => this._xmlDocument = xmlDocument;
|
||||
|
||||
/// <summary>
|
||||
/// Finds the element.
|
||||
/// </summary>
|
||||
/// <param name="elementName">Name of the element.</param>
|
||||
/// <returns>A XElement.</returns>
|
||||
protected XElement FindElement(string elementName) => _xmlDocument.Descendants(elementName).FirstOrDefault();
|
||||
protected XElement FindElement(String elementName) => this._xmlDocument.Descendants(elementName).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
@@ -1,144 +1,139 @@
|
||||
namespace Unosquare.Swan.Components
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Abstractions;
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Unosquare.Swan.Abstractions;
|
||||
|
||||
namespace Unosquare.Swan.Components {
|
||||
/// <summary>
|
||||
/// Represents logic providing several delay mechanisms.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following example shows how to implement delay mechanisms.
|
||||
/// <code>
|
||||
/// using Unosquare.Swan.Components;
|
||||
///
|
||||
/// public class Example
|
||||
/// {
|
||||
/// public static void Main()
|
||||
/// {
|
||||
/// // using the ThreadSleep strategy
|
||||
/// using (var delay = new DelayProvider(DelayProvider.DelayStrategy.ThreadSleep))
|
||||
/// {
|
||||
/// // retrieve how much time was delayed
|
||||
/// var time = delay.WaitOne();
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public sealed class DelayProvider : IDisposable {
|
||||
private readonly Object _syncRoot = new Object();
|
||||
private readonly Stopwatch _delayStopwatch = new Stopwatch();
|
||||
|
||||
private Boolean _isDisposed;
|
||||
private IWaitEvent _delayEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Represents logic providing several delay mechanisms.
|
||||
/// Initializes a new instance of the <see cref="DelayProvider"/> class.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following example shows how to implement delay mechanisms.
|
||||
/// <code>
|
||||
/// using Unosquare.Swan.Components;
|
||||
///
|
||||
/// public class Example
|
||||
/// {
|
||||
/// public static void Main()
|
||||
/// {
|
||||
/// // using the ThreadSleep strategy
|
||||
/// using (var delay = new DelayProvider(DelayProvider.DelayStrategy.ThreadSleep))
|
||||
/// {
|
||||
/// // retrieve how much time was delayed
|
||||
/// var time = delay.WaitOne();
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public sealed class DelayProvider : IDisposable
|
||||
{
|
||||
private readonly object _syncRoot = new object();
|
||||
private readonly Stopwatch _delayStopwatch = new Stopwatch();
|
||||
|
||||
private bool _isDisposed;
|
||||
private IWaitEvent _delayEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DelayProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="strategy">The strategy.</param>
|
||||
public DelayProvider(DelayStrategy strategy = DelayStrategy.TaskDelay)
|
||||
{
|
||||
Strategy = strategy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the different ways of providing delays.
|
||||
/// </summary>
|
||||
public enum DelayStrategy
|
||||
{
|
||||
/// <summary>
|
||||
/// Using the Thread.Sleep(15) mechanism.
|
||||
/// </summary>
|
||||
ThreadSleep,
|
||||
|
||||
/// <summary>
|
||||
/// Using the Task.Delay(1).Wait mechanism.
|
||||
/// </summary>
|
||||
TaskDelay,
|
||||
|
||||
/// <summary>
|
||||
/// Using a wait event that completes in a background ThreadPool thread.
|
||||
/// </summary>
|
||||
ThreadPool,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected delay strategy.
|
||||
/// </summary>
|
||||
public DelayStrategy Strategy { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates the smallest possible, synchronous delay based on the selected strategy.
|
||||
/// </summary>
|
||||
/// <returns>The elapsed time of the delay.</returns>
|
||||
public TimeSpan WaitOne()
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
if (_isDisposed) return TimeSpan.Zero;
|
||||
|
||||
_delayStopwatch.Restart();
|
||||
|
||||
switch (Strategy)
|
||||
{
|
||||
case DelayStrategy.ThreadSleep:
|
||||
DelaySleep();
|
||||
break;
|
||||
case DelayStrategy.TaskDelay:
|
||||
DelayTask();
|
||||
break;
|
||||
#if !NETSTANDARD1_3
|
||||
case DelayStrategy.ThreadPool:
|
||||
DelayThreadPool();
|
||||
break;
|
||||
/// <param name="strategy">The strategy.</param>
|
||||
public DelayProvider(DelayStrategy strategy = DelayStrategy.TaskDelay) => this.Strategy = strategy;
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the different ways of providing delays.
|
||||
/// </summary>
|
||||
public enum DelayStrategy {
|
||||
/// <summary>
|
||||
/// Using the Thread.Sleep(15) mechanism.
|
||||
/// </summary>
|
||||
ThreadSleep,
|
||||
|
||||
/// <summary>
|
||||
/// Using the Task.Delay(1).Wait mechanism.
|
||||
/// </summary>
|
||||
TaskDelay,
|
||||
|
||||
/// <summary>
|
||||
/// Using a wait event that completes in a background ThreadPool thread.
|
||||
/// </summary>
|
||||
ThreadPool,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected delay strategy.
|
||||
/// </summary>
|
||||
public DelayStrategy Strategy {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the smallest possible, synchronous delay based on the selected strategy.
|
||||
/// </summary>
|
||||
/// <returns>The elapsed time of the delay.</returns>
|
||||
public TimeSpan WaitOne() {
|
||||
lock(this._syncRoot) {
|
||||
if(this._isDisposed) {
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
|
||||
this._delayStopwatch.Restart();
|
||||
|
||||
switch(this.Strategy) {
|
||||
case DelayStrategy.ThreadSleep:
|
||||
DelaySleep();
|
||||
break;
|
||||
case DelayStrategy.TaskDelay:
|
||||
DelayTask();
|
||||
break;
|
||||
#if !NETSTANDARD1_3
|
||||
case DelayStrategy.ThreadPool:
|
||||
this.DelayThreadPool();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return _delayStopwatch.Elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
#region Dispose Pattern
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
_isDisposed = true;
|
||||
_delayEvent?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Delay Mechanisms
|
||||
|
||||
private static void DelaySleep() => Thread.Sleep(15);
|
||||
|
||||
private static void DelayTask() => Task.Delay(1).Wait();
|
||||
|
||||
#if !NETSTANDARD1_3
|
||||
private void DelayThreadPool()
|
||||
{
|
||||
if (_delayEvent == null)
|
||||
_delayEvent = WaitEventFactory.Create(isCompleted: true, useSlim: true);
|
||||
|
||||
_delayEvent.Begin();
|
||||
ThreadPool.QueueUserWorkItem((s) =>
|
||||
{
|
||||
DelaySleep();
|
||||
_delayEvent.Complete();
|
||||
});
|
||||
|
||||
_delayEvent.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
return this._delayStopwatch.Elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
#region Dispose Pattern
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() {
|
||||
lock(this._syncRoot) {
|
||||
if(this._isDisposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._isDisposed = true;
|
||||
this._delayEvent?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Delay Mechanisms
|
||||
|
||||
private static void DelaySleep() => Thread.Sleep(15);
|
||||
|
||||
private static void DelayTask() => Task.Delay(1).Wait();
|
||||
|
||||
#if !NETSTANDARD1_3
|
||||
private void DelayThreadPool() {
|
||||
if(this._delayEvent == null) {
|
||||
this._delayEvent = WaitEventFactory.Create(isCompleted: true, useSlim: true);
|
||||
}
|
||||
|
||||
this._delayEvent.Begin();
|
||||
_ = ThreadPool.QueueUserWorkItem((s) => {
|
||||
DelaySleep();
|
||||
this._delayEvent.Complete();
|
||||
});
|
||||
|
||||
this._delayEvent.Wait();
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,114 +1,113 @@
|
||||
namespace Unosquare.Swan.Components
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unosquare.Swan.Components {
|
||||
/// <summary>
|
||||
/// Resolution settings.
|
||||
/// </summary>
|
||||
public class DependencyContainerResolveOptions {
|
||||
/// <summary>
|
||||
/// Resolution settings.
|
||||
/// Gets the default options (attempt resolution of unregistered types, fail on named resolution if name not found).
|
||||
/// </summary>
|
||||
public class DependencyContainerResolveOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the default options (attempt resolution of unregistered types, fail on named resolution if name not found).
|
||||
/// </summary>
|
||||
public static DependencyContainerResolveOptions Default { get; } = new DependencyContainerResolveOptions();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the unregistered resolution action.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The unregistered resolution action.
|
||||
/// </value>
|
||||
public DependencyContainerUnregisteredResolutionActions UnregisteredResolutionAction { get; set; } =
|
||||
DependencyContainerUnregisteredResolutionActions.AttemptResolve;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the named resolution failure action.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The named resolution failure action.
|
||||
/// </value>
|
||||
public DependencyContainerNamedResolutionFailureActions NamedResolutionFailureAction { get; set; } =
|
||||
DependencyContainerNamedResolutionFailureActions.Fail;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the constructor parameters.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The constructor parameters.
|
||||
/// </value>
|
||||
public Dictionary<string, object> ConstructorParameters { get; } = new Dictionary<string, object>();
|
||||
|
||||
/// <summary>
|
||||
/// Clones this instance.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public DependencyContainerResolveOptions Clone() => new DependencyContainerResolveOptions
|
||||
{
|
||||
NamedResolutionFailureAction = NamedResolutionFailureAction,
|
||||
UnregisteredResolutionAction = UnregisteredResolutionAction,
|
||||
};
|
||||
}
|
||||
|
||||
public static DependencyContainerResolveOptions Default { get; } = new DependencyContainerResolveOptions();
|
||||
|
||||
/// <summary>
|
||||
/// Defines Resolution actions.
|
||||
/// Gets or sets the unregistered resolution action.
|
||||
/// </summary>
|
||||
public enum DependencyContainerUnregisteredResolutionActions
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempt to resolve type, even if the type isn't registered.
|
||||
///
|
||||
/// Registered types/options will always take precedence.
|
||||
/// </summary>
|
||||
AttemptResolve,
|
||||
|
||||
/// <summary>
|
||||
/// Fail resolution if type not explicitly registered
|
||||
/// </summary>
|
||||
Fail,
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to resolve unregistered type if requested type is generic
|
||||
/// and no registration exists for the specific generic parameters used.
|
||||
///
|
||||
/// Registered types/options will always take precedence.
|
||||
/// </summary>
|
||||
GenericsOnly,
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// The unregistered resolution action.
|
||||
/// </value>
|
||||
public DependencyContainerUnregisteredResolutionActions UnregisteredResolutionAction {
|
||||
get; set;
|
||||
} =
|
||||
DependencyContainerUnregisteredResolutionActions.AttemptResolve;
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates failure actions.
|
||||
/// Gets or sets the named resolution failure action.
|
||||
/// </summary>
|
||||
public enum DependencyContainerNamedResolutionFailureActions
|
||||
{
|
||||
/// <summary>
|
||||
/// The attempt unnamed resolution
|
||||
/// </summary>
|
||||
AttemptUnnamedResolution,
|
||||
|
||||
/// <summary>
|
||||
/// The fail
|
||||
/// </summary>
|
||||
Fail,
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// The named resolution failure action.
|
||||
/// </value>
|
||||
public DependencyContainerNamedResolutionFailureActions NamedResolutionFailureAction {
|
||||
get; set;
|
||||
} =
|
||||
DependencyContainerNamedResolutionFailureActions.Fail;
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates duplicate definition actions.
|
||||
/// Gets the constructor parameters.
|
||||
/// </summary>
|
||||
public enum DependencyContainerDuplicateImplementationActions
|
||||
{
|
||||
/// <summary>
|
||||
/// The register single
|
||||
/// </summary>
|
||||
RegisterSingle,
|
||||
|
||||
/// <summary>
|
||||
/// The register multiple
|
||||
/// </summary>
|
||||
RegisterMultiple,
|
||||
|
||||
/// <summary>
|
||||
/// The fail
|
||||
/// </summary>
|
||||
Fail,
|
||||
}
|
||||
/// <value>
|
||||
/// The constructor parameters.
|
||||
/// </value>
|
||||
public Dictionary<String, Object> ConstructorParameters { get; } = new Dictionary<String, Object>();
|
||||
|
||||
/// <summary>
|
||||
/// Clones this instance.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public DependencyContainerResolveOptions Clone() => new DependencyContainerResolveOptions {
|
||||
NamedResolutionFailureAction = NamedResolutionFailureAction,
|
||||
UnregisteredResolutionAction = UnregisteredResolutionAction,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines Resolution actions.
|
||||
/// </summary>
|
||||
public enum DependencyContainerUnregisteredResolutionActions {
|
||||
/// <summary>
|
||||
/// Attempt to resolve type, even if the type isn't registered.
|
||||
///
|
||||
/// Registered types/options will always take precedence.
|
||||
/// </summary>
|
||||
AttemptResolve,
|
||||
|
||||
/// <summary>
|
||||
/// Fail resolution if type not explicitly registered
|
||||
/// </summary>
|
||||
Fail,
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to resolve unregistered type if requested type is generic
|
||||
/// and no registration exists for the specific generic parameters used.
|
||||
///
|
||||
/// Registered types/options will always take precedence.
|
||||
/// </summary>
|
||||
GenericsOnly,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates failure actions.
|
||||
/// </summary>
|
||||
public enum DependencyContainerNamedResolutionFailureActions {
|
||||
/// <summary>
|
||||
/// The attempt unnamed resolution
|
||||
/// </summary>
|
||||
AttemptUnnamedResolution,
|
||||
|
||||
/// <summary>
|
||||
/// The fail
|
||||
/// </summary>
|
||||
Fail,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates duplicate definition actions.
|
||||
/// </summary>
|
||||
public enum DependencyContainerDuplicateImplementationActions {
|
||||
/// <summary>
|
||||
/// The register single
|
||||
/// </summary>
|
||||
RegisterSingle,
|
||||
|
||||
/// <summary>
|
||||
/// The register multiple
|
||||
/// </summary>
|
||||
RegisterMultiple,
|
||||
|
||||
/// <summary>
|
||||
/// The fail
|
||||
/// </summary>
|
||||
Fail,
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
namespace Unosquare.Swan.Components
|
||||
{
|
||||
using System;
|
||||
|
||||
namespace Unosquare.Swan.Components {
|
||||
/// <summary>
|
||||
/// A Message to be published/delivered by Messenger.
|
||||
/// </summary>
|
||||
public interface IMessageHubMessage {
|
||||
/// <summary>
|
||||
/// A Message to be published/delivered by Messenger.
|
||||
/// The sender of the message, or null if not supported by the message implementation.
|
||||
/// </summary>
|
||||
public interface IMessageHubMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// The sender of the message, or null if not supported by the message implementation.
|
||||
/// </summary>
|
||||
object Sender { get; }
|
||||
}
|
||||
Object Sender {
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,466 +1,443 @@
|
||||
// ===============================================================================
|
||||
// TinyIoC - TinyMessenger
|
||||
//
|
||||
// A simple messenger/event aggregator.
|
||||
//
|
||||
// https://github.com/grumpydev/TinyIoC/blob/master/src/TinyIoC/TinyMessenger.cs
|
||||
// ===============================================================================
|
||||
// Copyright © Steven Robbins. All rights reserved.
|
||||
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
|
||||
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
|
||||
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// ===============================================================================
|
||||
|
||||
namespace Unosquare.Swan.Components
|
||||
{
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
#region Message Types / Interfaces
|
||||
|
||||
// ===============================================================================
|
||||
// TinyIoC - TinyMessenger
|
||||
//
|
||||
// A simple messenger/event aggregator.
|
||||
//
|
||||
// https://github.com/grumpydev/TinyIoC/blob/master/src/TinyIoC/TinyMessenger.cs
|
||||
// ===============================================================================
|
||||
// Copyright © Steven Robbins. All rights reserved.
|
||||
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
|
||||
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
|
||||
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// ===============================================================================
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Unosquare.Swan.Components {
|
||||
#region Message Types / Interfaces
|
||||
|
||||
/// <summary>
|
||||
/// Represents a message subscription.
|
||||
/// </summary>
|
||||
public interface IMessageHubSubscription {
|
||||
/// <summary>
|
||||
/// Represents a message subscription.
|
||||
/// Token returned to the subscribed to reference this subscription.
|
||||
/// </summary>
|
||||
public interface IMessageHubSubscription
|
||||
{
|
||||
/// <summary>
|
||||
/// Token returned to the subscribed to reference this subscription.
|
||||
/// </summary>
|
||||
MessageHubSubscriptionToken SubscriptionToken { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether delivery should be attempted.
|
||||
/// </summary>
|
||||
/// <param name="message">Message that may potentially be delivered.</param>
|
||||
/// <returns><c>true</c> - ok to send, <c>false</c> - should not attempt to send.</returns>
|
||||
bool ShouldAttemptDelivery(IMessageHubMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// Deliver the message.
|
||||
/// </summary>
|
||||
/// <param name="message">Message to deliver.</param>
|
||||
void Deliver(IMessageHubMessage message);
|
||||
}
|
||||
|
||||
MessageHubSubscriptionToken SubscriptionToken {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Message proxy definition.
|
||||
/// Whether delivery should be attempted.
|
||||
/// </summary>
|
||||
/// <param name="message">Message that may potentially be delivered.</param>
|
||||
/// <returns><c>true</c> - ok to send, <c>false</c> - should not attempt to send.</returns>
|
||||
Boolean ShouldAttemptDelivery(IMessageHubMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// Deliver the message.
|
||||
/// </summary>
|
||||
/// <param name="message">Message to deliver.</param>
|
||||
void Deliver(IMessageHubMessage message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Message proxy definition.
|
||||
///
|
||||
/// A message proxy can be used to intercept/alter messages and/or
|
||||
/// marshal delivery actions onto a particular thread.
|
||||
/// </summary>
|
||||
public interface IMessageHubProxy {
|
||||
/// <summary>
|
||||
/// Delivers the specified message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="subscription">The subscription.</param>
|
||||
void Deliver(IMessageHubMessage message, IMessageHubSubscription subscription);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default "pass through" proxy.
|
||||
///
|
||||
/// Does nothing other than deliver the message.
|
||||
/// </summary>
|
||||
public sealed class MessageHubDefaultProxy : IMessageHubProxy {
|
||||
private MessageHubDefaultProxy() {
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Singleton instance of the proxy.
|
||||
/// </summary>
|
||||
public static MessageHubDefaultProxy Instance { get; } = new MessageHubDefaultProxy();
|
||||
|
||||
/// <summary>
|
||||
/// Delivers the specified message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="subscription">The subscription.</param>
|
||||
public void Deliver(IMessageHubMessage message, IMessageHubSubscription subscription)
|
||||
=> subscription.Deliver(message);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Hub Interface
|
||||
|
||||
/// <summary>
|
||||
/// Messenger hub responsible for taking subscriptions/publications and delivering of messages.
|
||||
/// </summary>
|
||||
public interface IMessageHub {
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action.
|
||||
/// Messages will be delivered via the specified proxy.
|
||||
///
|
||||
/// A message proxy can be used to intercept/alter messages and/or
|
||||
/// marshal delivery actions onto a particular thread.
|
||||
/// All messages of this type will be delivered.
|
||||
/// </summary>
|
||||
public interface IMessageHubProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// Delivers the specified message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="subscription">The subscription.</param>
|
||||
void Deliver(IMessageHubMessage message, IMessageHubSubscription subscription);
|
||||
}
|
||||
|
||||
/// <typeparam name="TMessage">Type of message.</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered.</param>
|
||||
/// <param name="useStrongReferences">Use strong references to destination and deliveryAction.</param>
|
||||
/// <param name="proxy">Proxy to use when delivering the messages.</param>
|
||||
/// <returns>MessageSubscription used to unsubscribing.</returns>
|
||||
MessageHubSubscriptionToken Subscribe<TMessage>(
|
||||
Action<TMessage> deliveryAction,
|
||||
Boolean useStrongReferences,
|
||||
IMessageHubProxy proxy)
|
||||
where TMessage : class, IMessageHubMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Default "pass through" proxy.
|
||||
/// Subscribe to a message type with the given destination and delivery action with the given filter.
|
||||
/// Messages will be delivered via the specified proxy.
|
||||
/// All references are held with WeakReferences
|
||||
/// Only messages that "pass" the filter will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message.</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered.</param>
|
||||
/// <param name="messageFilter">The message filter.</param>
|
||||
/// <param name="useStrongReferences">Use strong references to destination and deliveryAction.</param>
|
||||
/// <param name="proxy">Proxy to use when delivering the messages.</param>
|
||||
/// <returns>
|
||||
/// MessageSubscription used to unsubscribing.
|
||||
/// </returns>
|
||||
MessageHubSubscriptionToken Subscribe<TMessage>(
|
||||
Action<TMessage> deliveryAction,
|
||||
Func<TMessage, Boolean> messageFilter,
|
||||
Boolean useStrongReferences,
|
||||
IMessageHubProxy proxy)
|
||||
where TMessage : class, IMessageHubMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe from a particular message type.
|
||||
///
|
||||
/// Does nothing other than deliver the message.
|
||||
/// Does not throw an exception if the subscription is not found.
|
||||
/// </summary>
|
||||
public sealed class MessageHubDefaultProxy : IMessageHubProxy
|
||||
{
|
||||
private MessageHubDefaultProxy()
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Singleton instance of the proxy.
|
||||
/// </summary>
|
||||
public static MessageHubDefaultProxy Instance { get; } = new MessageHubDefaultProxy();
|
||||
|
||||
/// <summary>
|
||||
/// Delivers the specified message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="subscription">The subscription.</param>
|
||||
public void Deliver(IMessageHubMessage message, IMessageHubSubscription subscription)
|
||||
=> subscription.Deliver(message);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Hub Interface
|
||||
|
||||
/// <typeparam name="TMessage">Type of message.</typeparam>
|
||||
/// <param name="subscriptionToken">Subscription token received from Subscribe.</param>
|
||||
void Unsubscribe<TMessage>(MessageHubSubscriptionToken subscriptionToken)
|
||||
where TMessage : class, IMessageHubMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Messenger hub responsible for taking subscriptions/publications and delivering of messages.
|
||||
/// Publish a message to any subscribers.
|
||||
/// </summary>
|
||||
public interface IMessageHub
|
||||
{
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action.
|
||||
/// Messages will be delivered via the specified proxy.
|
||||
///
|
||||
/// All messages of this type will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message.</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered.</param>
|
||||
/// <param name="useStrongReferences">Use strong references to destination and deliveryAction.</param>
|
||||
/// <param name="proxy">Proxy to use when delivering the messages.</param>
|
||||
/// <returns>MessageSubscription used to unsubscribing.</returns>
|
||||
MessageHubSubscriptionToken Subscribe<TMessage>(
|
||||
Action<TMessage> deliveryAction,
|
||||
bool useStrongReferences,
|
||||
IMessageHubProxy proxy)
|
||||
where TMessage : class, IMessageHubMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action with the given filter.
|
||||
/// Messages will be delivered via the specified proxy.
|
||||
/// All references are held with WeakReferences
|
||||
/// Only messages that "pass" the filter will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message.</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered.</param>
|
||||
/// <param name="messageFilter">The message filter.</param>
|
||||
/// <param name="useStrongReferences">Use strong references to destination and deliveryAction.</param>
|
||||
/// <param name="proxy">Proxy to use when delivering the messages.</param>
|
||||
/// <returns>
|
||||
/// MessageSubscription used to unsubscribing.
|
||||
/// </returns>
|
||||
MessageHubSubscriptionToken Subscribe<TMessage>(
|
||||
Action<TMessage> deliveryAction,
|
||||
Func<TMessage, bool> messageFilter,
|
||||
bool useStrongReferences,
|
||||
IMessageHubProxy proxy)
|
||||
where TMessage : class, IMessageHubMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe from a particular message type.
|
||||
///
|
||||
/// Does not throw an exception if the subscription is not found.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message.</typeparam>
|
||||
/// <param name="subscriptionToken">Subscription token received from Subscribe.</param>
|
||||
void Unsubscribe<TMessage>(MessageHubSubscriptionToken subscriptionToken)
|
||||
where TMessage : class, IMessageHubMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Publish a message to any subscribers.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message.</typeparam>
|
||||
/// <param name="message">Message to deliver.</param>
|
||||
void Publish<TMessage>(TMessage message)
|
||||
where TMessage : class, IMessageHubMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Publish a message to any subscribers asynchronously.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message.</typeparam>
|
||||
/// <param name="message">Message to deliver.</param>
|
||||
/// <returns>A task from Publish action.</returns>
|
||||
Task PublishAsync<TMessage>(TMessage message)
|
||||
where TMessage : class, IMessageHubMessage;
|
||||
}
|
||||
|
||||
/// <typeparam name="TMessage">Type of message.</typeparam>
|
||||
/// <param name="message">Message to deliver.</param>
|
||||
void Publish<TMessage>(TMessage message)
|
||||
where TMessage : class, IMessageHubMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Publish a message to any subscribers asynchronously.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message.</typeparam>
|
||||
/// <param name="message">Message to deliver.</param>
|
||||
/// <returns>A task from Publish action.</returns>
|
||||
Task PublishAsync<TMessage>(TMessage message)
|
||||
where TMessage : class, IMessageHubMessage;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Hub Implementation
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <example>
|
||||
/// The following code describes how to use a MessageHub. Both the
|
||||
/// subscription and the message sending are done in the same place but this is only for explanatory purposes.
|
||||
/// <code>
|
||||
/// class Example
|
||||
/// {
|
||||
/// using Unosquare.Swan;
|
||||
/// using Unosquare.Swan.Components;
|
||||
///
|
||||
/// static void Main()
|
||||
/// {
|
||||
/// // using DependencyContainer to create an instance of MessageHub
|
||||
/// var messageHub = DependencyContainer
|
||||
/// .Current
|
||||
/// .Resolve<IMessageHub>() as MessageHub;
|
||||
///
|
||||
/// // create an instance of the publisher class
|
||||
/// // which has a string as its content
|
||||
/// var message = new MessageHubGenericMessage<string>(new object(), "SWAN");
|
||||
///
|
||||
/// // subscribe to the publisher's event
|
||||
/// // and just print out the content which is a string
|
||||
/// // a token is returned which can be used to unsubscribe later on
|
||||
/// var token = messageHub
|
||||
/// .Subscribe<MessageHubGenericMessage<string>>(m => m.Content.Info());
|
||||
///
|
||||
/// // publish the message described above which is
|
||||
/// // the string 'SWAN'
|
||||
/// messageHub.Publish(message);
|
||||
///
|
||||
/// // unsuscribe, we will no longer receive any messages
|
||||
/// messageHub.Unsubscribe<MessageHubGenericMessage<string>>(token);
|
||||
///
|
||||
/// Terminal.Flush();
|
||||
/// }
|
||||
///
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public sealed class MessageHub : IMessageHub {
|
||||
#region Private Types and Interfaces
|
||||
|
||||
private readonly Object _subscriptionsPadlock = new Object();
|
||||
|
||||
private readonly Dictionary<Type, List<SubscriptionItem>> _subscriptions =
|
||||
new Dictionary<Type, List<SubscriptionItem>>();
|
||||
|
||||
private class WeakMessageSubscription<TMessage> : IMessageHubSubscription
|
||||
where TMessage : class, IMessageHubMessage {
|
||||
private readonly WeakReference _deliveryAction;
|
||||
private readonly WeakReference _messageFilter;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WeakMessageSubscription{TMessage}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="subscriptionToken">The subscription token.</param>
|
||||
/// <param name="deliveryAction">The delivery action.</param>
|
||||
/// <param name="messageFilter">The message filter.</param>
|
||||
/// <exception cref="ArgumentNullException">subscriptionToken
|
||||
/// or
|
||||
/// deliveryAction
|
||||
/// or
|
||||
/// messageFilter.</exception>
|
||||
public WeakMessageSubscription(
|
||||
MessageHubSubscriptionToken subscriptionToken,
|
||||
Action<TMessage> deliveryAction,
|
||||
Func<TMessage, Boolean> messageFilter) {
|
||||
this.SubscriptionToken = subscriptionToken ?? throw new ArgumentNullException(nameof(subscriptionToken));
|
||||
this._deliveryAction = new WeakReference(deliveryAction);
|
||||
this._messageFilter = new WeakReference(messageFilter);
|
||||
}
|
||||
|
||||
public MessageHubSubscriptionToken SubscriptionToken {
|
||||
get;
|
||||
}
|
||||
|
||||
public Boolean ShouldAttemptDelivery(IMessageHubMessage message) => this._deliveryAction.IsAlive && this._messageFilter.IsAlive &&
|
||||
((Func<TMessage, Boolean>)this._messageFilter.Target).Invoke((TMessage)message);
|
||||
|
||||
public void Deliver(IMessageHubMessage message) {
|
||||
if(this._deliveryAction.IsAlive) {
|
||||
((Action<TMessage>)this._deliveryAction.Target).Invoke((TMessage)message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class StrongMessageSubscription<TMessage> : IMessageHubSubscription
|
||||
where TMessage : class, IMessageHubMessage {
|
||||
private readonly Action<TMessage> _deliveryAction;
|
||||
private readonly Func<TMessage, Boolean> _messageFilter;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StrongMessageSubscription{TMessage}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="subscriptionToken">The subscription token.</param>
|
||||
/// <param name="deliveryAction">The delivery action.</param>
|
||||
/// <param name="messageFilter">The message filter.</param>
|
||||
/// <exception cref="ArgumentNullException">subscriptionToken
|
||||
/// or
|
||||
/// deliveryAction
|
||||
/// or
|
||||
/// messageFilter.</exception>
|
||||
public StrongMessageSubscription(
|
||||
MessageHubSubscriptionToken subscriptionToken,
|
||||
Action<TMessage> deliveryAction,
|
||||
Func<TMessage, Boolean> messageFilter) {
|
||||
this.SubscriptionToken = subscriptionToken ?? throw new ArgumentNullException(nameof(subscriptionToken));
|
||||
this._deliveryAction = deliveryAction;
|
||||
this._messageFilter = messageFilter;
|
||||
}
|
||||
|
||||
public MessageHubSubscriptionToken SubscriptionToken {
|
||||
get;
|
||||
}
|
||||
|
||||
public Boolean ShouldAttemptDelivery(IMessageHubMessage message) => this._messageFilter.Invoke((TMessage)message);
|
||||
|
||||
public void Deliver(IMessageHubMessage message) => this._deliveryAction.Invoke((TMessage)message);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Hub Implementation
|
||||
|
||||
|
||||
#region Subscription dictionary
|
||||
|
||||
private class SubscriptionItem {
|
||||
public SubscriptionItem(IMessageHubProxy proxy, IMessageHubSubscription subscription) {
|
||||
this.Proxy = proxy;
|
||||
this.Subscription = subscription;
|
||||
}
|
||||
|
||||
public IMessageHubProxy Proxy {
|
||||
get;
|
||||
}
|
||||
public IMessageHubSubscription Subscription {
|
||||
get;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public API
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action.
|
||||
/// Messages will be delivered via the specified proxy.
|
||||
///
|
||||
/// All messages of this type will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message.</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered.</param>
|
||||
/// <param name="useStrongReferences">Use strong references to destination and deliveryAction. </param>
|
||||
/// <param name="proxy">Proxy to use when delivering the messages.</param>
|
||||
/// <returns>MessageSubscription used to unsubscribing.</returns>
|
||||
public MessageHubSubscriptionToken Subscribe<TMessage>(
|
||||
Action<TMessage> deliveryAction,
|
||||
Boolean useStrongReferences = true,
|
||||
IMessageHubProxy proxy = null)
|
||||
where TMessage : class, IMessageHubMessage => this.Subscribe(deliveryAction, m => true, useStrongReferences, proxy);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action with the given filter.
|
||||
/// Messages will be delivered via the specified proxy.
|
||||
/// All references are held with WeakReferences
|
||||
/// Only messages that "pass" the filter will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message.</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered.</param>
|
||||
/// <param name="messageFilter">The message filter.</param>
|
||||
/// <param name="useStrongReferences">Use strong references to destination and deliveryAction.</param>
|
||||
/// <param name="proxy">Proxy to use when delivering the messages.</param>
|
||||
/// <returns>
|
||||
/// MessageSubscription used to unsubscribing.
|
||||
/// </returns>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Codequalität", "IDE0068:Empfohlenes Dispose-Muster verwenden", Justification = "<Ausstehend>")]
|
||||
public MessageHubSubscriptionToken Subscribe<TMessage>(
|
||||
Action<TMessage> deliveryAction,
|
||||
Func<TMessage, Boolean> messageFilter,
|
||||
Boolean useStrongReferences = true,
|
||||
IMessageHubProxy proxy = null)
|
||||
where TMessage : class, IMessageHubMessage {
|
||||
if(deliveryAction == null) {
|
||||
throw new ArgumentNullException(nameof(deliveryAction));
|
||||
}
|
||||
|
||||
if(messageFilter == null) {
|
||||
throw new ArgumentNullException(nameof(messageFilter));
|
||||
}
|
||||
|
||||
lock(this._subscriptionsPadlock) {
|
||||
if(!this._subscriptions.TryGetValue(typeof(TMessage), out List<SubscriptionItem> currentSubscriptions)) {
|
||||
currentSubscriptions = new List<SubscriptionItem>();
|
||||
this._subscriptions[typeof(TMessage)] = currentSubscriptions;
|
||||
}
|
||||
|
||||
MessageHubSubscriptionToken subscriptionToken = new MessageHubSubscriptionToken(this, typeof(TMessage));
|
||||
|
||||
IMessageHubSubscription subscription = useStrongReferences
|
||||
? new StrongMessageSubscription<TMessage>(
|
||||
subscriptionToken,
|
||||
deliveryAction,
|
||||
messageFilter)
|
||||
: (IMessageHubSubscription)new WeakMessageSubscription<TMessage>(
|
||||
subscriptionToken,
|
||||
deliveryAction,
|
||||
messageFilter);
|
||||
|
||||
currentSubscriptions.Add(new SubscriptionItem(proxy ?? MessageHubDefaultProxy.Instance, subscription));
|
||||
|
||||
return subscriptionToken;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <example>
|
||||
/// The following code describes how to use a MessageHub. Both the
|
||||
/// subscription and the message sending are done in the same place but this is only for explanatory purposes.
|
||||
/// <code>
|
||||
/// class Example
|
||||
/// {
|
||||
/// using Unosquare.Swan;
|
||||
/// using Unosquare.Swan.Components;
|
||||
///
|
||||
/// static void Main()
|
||||
/// {
|
||||
/// // using DependencyContainer to create an instance of MessageHub
|
||||
/// var messageHub = DependencyContainer
|
||||
/// .Current
|
||||
/// .Resolve<IMessageHub>() as MessageHub;
|
||||
///
|
||||
/// // create an instance of the publisher class
|
||||
/// // which has a string as its content
|
||||
/// var message = new MessageHubGenericMessage<string>(new object(), "SWAN");
|
||||
///
|
||||
/// // subscribe to the publisher's event
|
||||
/// // and just print out the content which is a string
|
||||
/// // a token is returned which can be used to unsubscribe later on
|
||||
/// var token = messageHub
|
||||
/// .Subscribe<MessageHubGenericMessage<string>>(m => m.Content.Info());
|
||||
///
|
||||
/// // publish the message described above which is
|
||||
/// // the string 'SWAN'
|
||||
/// messageHub.Publish(message);
|
||||
///
|
||||
/// // unsuscribe, we will no longer receive any messages
|
||||
/// messageHub.Unsubscribe<MessageHubGenericMessage<string>>(token);
|
||||
///
|
||||
/// Terminal.Flush();
|
||||
/// }
|
||||
///
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public sealed class MessageHub : IMessageHub
|
||||
{
|
||||
#region Private Types and Interfaces
|
||||
|
||||
private readonly object _subscriptionsPadlock = new object();
|
||||
|
||||
private readonly Dictionary<Type, List<SubscriptionItem>> _subscriptions =
|
||||
new Dictionary<Type, List<SubscriptionItem>>();
|
||||
|
||||
private class WeakMessageSubscription<TMessage> : IMessageHubSubscription
|
||||
where TMessage : class, IMessageHubMessage
|
||||
{
|
||||
private readonly WeakReference _deliveryAction;
|
||||
private readonly WeakReference _messageFilter;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WeakMessageSubscription{TMessage}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="subscriptionToken">The subscription token.</param>
|
||||
/// <param name="deliveryAction">The delivery action.</param>
|
||||
/// <param name="messageFilter">The message filter.</param>
|
||||
/// <exception cref="ArgumentNullException">subscriptionToken
|
||||
/// or
|
||||
/// deliveryAction
|
||||
/// or
|
||||
/// messageFilter.</exception>
|
||||
public WeakMessageSubscription(
|
||||
MessageHubSubscriptionToken subscriptionToken,
|
||||
Action<TMessage> deliveryAction,
|
||||
Func<TMessage, bool> messageFilter)
|
||||
{
|
||||
SubscriptionToken = subscriptionToken ?? throw new ArgumentNullException(nameof(subscriptionToken));
|
||||
_deliveryAction = new WeakReference(deliveryAction);
|
||||
_messageFilter = new WeakReference(messageFilter);
|
||||
}
|
||||
|
||||
public MessageHubSubscriptionToken SubscriptionToken { get; }
|
||||
|
||||
public bool ShouldAttemptDelivery(IMessageHubMessage message)
|
||||
{
|
||||
return _deliveryAction.IsAlive && _messageFilter.IsAlive &&
|
||||
((Func<TMessage, bool>) _messageFilter.Target).Invoke((TMessage) message);
|
||||
}
|
||||
|
||||
public void Deliver(IMessageHubMessage message)
|
||||
{
|
||||
if (_deliveryAction.IsAlive)
|
||||
{
|
||||
((Action<TMessage>) _deliveryAction.Target).Invoke((TMessage) message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class StrongMessageSubscription<TMessage> : IMessageHubSubscription
|
||||
where TMessage : class, IMessageHubMessage
|
||||
{
|
||||
private readonly Action<TMessage> _deliveryAction;
|
||||
private readonly Func<TMessage, bool> _messageFilter;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StrongMessageSubscription{TMessage}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="subscriptionToken">The subscription token.</param>
|
||||
/// <param name="deliveryAction">The delivery action.</param>
|
||||
/// <param name="messageFilter">The message filter.</param>
|
||||
/// <exception cref="ArgumentNullException">subscriptionToken
|
||||
/// or
|
||||
/// deliveryAction
|
||||
/// or
|
||||
/// messageFilter.</exception>
|
||||
public StrongMessageSubscription(
|
||||
MessageHubSubscriptionToken subscriptionToken,
|
||||
Action<TMessage> deliveryAction,
|
||||
Func<TMessage, bool> messageFilter)
|
||||
{
|
||||
SubscriptionToken = subscriptionToken ?? throw new ArgumentNullException(nameof(subscriptionToken));
|
||||
_deliveryAction = deliveryAction;
|
||||
_messageFilter = messageFilter;
|
||||
}
|
||||
|
||||
public MessageHubSubscriptionToken SubscriptionToken { get; }
|
||||
|
||||
public bool ShouldAttemptDelivery(IMessageHubMessage message) => _messageFilter.Invoke((TMessage) message);
|
||||
|
||||
public void Deliver(IMessageHubMessage message) => _deliveryAction.Invoke((TMessage) message);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Subscription dictionary
|
||||
|
||||
private class SubscriptionItem
|
||||
{
|
||||
public SubscriptionItem(IMessageHubProxy proxy, IMessageHubSubscription subscription)
|
||||
{
|
||||
Proxy = proxy;
|
||||
Subscription = subscription;
|
||||
}
|
||||
|
||||
public IMessageHubProxy Proxy { get; }
|
||||
public IMessageHubSubscription Subscription { get; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public API
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action.
|
||||
/// Messages will be delivered via the specified proxy.
|
||||
///
|
||||
/// All messages of this type will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message.</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered.</param>
|
||||
/// <param name="useStrongReferences">Use strong references to destination and deliveryAction. </param>
|
||||
/// <param name="proxy">Proxy to use when delivering the messages.</param>
|
||||
/// <returns>MessageSubscription used to unsubscribing.</returns>
|
||||
public MessageHubSubscriptionToken Subscribe<TMessage>(
|
||||
Action<TMessage> deliveryAction,
|
||||
bool useStrongReferences = true,
|
||||
IMessageHubProxy proxy = null)
|
||||
where TMessage : class, IMessageHubMessage
|
||||
{
|
||||
return Subscribe(deliveryAction, m => true, useStrongReferences, proxy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to a message type with the given destination and delivery action with the given filter.
|
||||
/// Messages will be delivered via the specified proxy.
|
||||
/// All references are held with WeakReferences
|
||||
/// Only messages that "pass" the filter will be delivered.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message.</typeparam>
|
||||
/// <param name="deliveryAction">Action to invoke when message is delivered.</param>
|
||||
/// <param name="messageFilter">The message filter.</param>
|
||||
/// <param name="useStrongReferences">Use strong references to destination and deliveryAction.</param>
|
||||
/// <param name="proxy">Proxy to use when delivering the messages.</param>
|
||||
/// <returns>
|
||||
/// MessageSubscription used to unsubscribing.
|
||||
/// </returns>
|
||||
public MessageHubSubscriptionToken Subscribe<TMessage>(
|
||||
Action<TMessage> deliveryAction,
|
||||
Func<TMessage, bool> messageFilter,
|
||||
bool useStrongReferences = true,
|
||||
IMessageHubProxy proxy = null)
|
||||
where TMessage : class, IMessageHubMessage
|
||||
{
|
||||
if (deliveryAction == null)
|
||||
throw new ArgumentNullException(nameof(deliveryAction));
|
||||
|
||||
if (messageFilter == null)
|
||||
throw new ArgumentNullException(nameof(messageFilter));
|
||||
|
||||
lock (_subscriptionsPadlock)
|
||||
{
|
||||
if (!_subscriptions.TryGetValue(typeof(TMessage), out var currentSubscriptions))
|
||||
{
|
||||
currentSubscriptions = new List<SubscriptionItem>();
|
||||
_subscriptions[typeof(TMessage)] = currentSubscriptions;
|
||||
}
|
||||
|
||||
var subscriptionToken = new MessageHubSubscriptionToken(this, typeof(TMessage));
|
||||
|
||||
IMessageHubSubscription subscription;
|
||||
if (useStrongReferences)
|
||||
{
|
||||
subscription = new StrongMessageSubscription<TMessage>(
|
||||
subscriptionToken,
|
||||
deliveryAction,
|
||||
messageFilter);
|
||||
}
|
||||
else
|
||||
{
|
||||
subscription = new WeakMessageSubscription<TMessage>(
|
||||
subscriptionToken,
|
||||
deliveryAction,
|
||||
messageFilter);
|
||||
}
|
||||
|
||||
currentSubscriptions.Add(new SubscriptionItem(proxy ?? MessageHubDefaultProxy.Instance, subscription));
|
||||
|
||||
return subscriptionToken;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Unsubscribe<TMessage>(MessageHubSubscriptionToken subscriptionToken)
|
||||
where TMessage : class, IMessageHubMessage
|
||||
{
|
||||
if (subscriptionToken == null)
|
||||
throw new ArgumentNullException(nameof(subscriptionToken));
|
||||
|
||||
lock (_subscriptionsPadlock)
|
||||
{
|
||||
if (!_subscriptions.TryGetValue(typeof(TMessage), out var currentSubscriptions))
|
||||
return;
|
||||
|
||||
var currentlySubscribed = currentSubscriptions
|
||||
.Where(sub => ReferenceEquals(sub.Subscription.SubscriptionToken, subscriptionToken))
|
||||
.ToList();
|
||||
|
||||
currentlySubscribed.ForEach(sub => currentSubscriptions.Remove(sub));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publish a message to any subscribers.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message.</typeparam>
|
||||
/// <param name="message">Message to deliver.</param>
|
||||
public void Publish<TMessage>(TMessage message)
|
||||
where TMessage : class, IMessageHubMessage
|
||||
{
|
||||
if (message == null)
|
||||
throw new ArgumentNullException(nameof(message));
|
||||
|
||||
List<SubscriptionItem> currentlySubscribed;
|
||||
lock (_subscriptionsPadlock)
|
||||
{
|
||||
if (!_subscriptions.TryGetValue(typeof(TMessage), out var currentSubscriptions))
|
||||
return;
|
||||
|
||||
currentlySubscribed = currentSubscriptions
|
||||
.Where(sub => sub.Subscription.ShouldAttemptDelivery(message))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
currentlySubscribed.ForEach(sub =>
|
||||
{
|
||||
try
|
||||
{
|
||||
sub.Proxy.Deliver(message, sub.Subscription);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore any errors and carry on
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publish a message to any subscribers asynchronously.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message.</typeparam>
|
||||
/// <param name="message">Message to deliver.</param>
|
||||
/// <returns>A task with the publish.</returns>
|
||||
public Task PublishAsync<TMessage>(TMessage message)
|
||||
where TMessage : class, IMessageHubMessage
|
||||
{
|
||||
return Task.Run(() => Publish(message));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public void Unsubscribe<TMessage>(MessageHubSubscriptionToken subscriptionToken)
|
||||
where TMessage : class, IMessageHubMessage {
|
||||
if(subscriptionToken == null) {
|
||||
throw new ArgumentNullException(nameof(subscriptionToken));
|
||||
}
|
||||
|
||||
lock(this._subscriptionsPadlock) {
|
||||
if(!this._subscriptions.TryGetValue(typeof(TMessage), out List<SubscriptionItem> currentSubscriptions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<SubscriptionItem> currentlySubscribed = currentSubscriptions
|
||||
.Where(sub => ReferenceEquals(sub.Subscription.SubscriptionToken, subscriptionToken))
|
||||
.ToList();
|
||||
|
||||
currentlySubscribed.ForEach(sub => currentSubscriptions.Remove(sub));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publish a message to any subscribers.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message.</typeparam>
|
||||
/// <param name="message">Message to deliver.</param>
|
||||
public void Publish<TMessage>(TMessage message)
|
||||
where TMessage : class, IMessageHubMessage {
|
||||
if(message == null) {
|
||||
throw new ArgumentNullException(nameof(message));
|
||||
}
|
||||
|
||||
List<SubscriptionItem> currentlySubscribed;
|
||||
lock(this._subscriptionsPadlock) {
|
||||
if(!this._subscriptions.TryGetValue(typeof(TMessage), out List<SubscriptionItem> currentSubscriptions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentlySubscribed = currentSubscriptions
|
||||
.Where(sub => sub.Subscription.ShouldAttemptDelivery(message))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
currentlySubscribed.ForEach(sub => {
|
||||
try {
|
||||
sub.Proxy.Deliver(message, sub.Subscription);
|
||||
} catch {
|
||||
// Ignore any errors and carry on
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publish a message to any subscribers asynchronously.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">Type of message.</typeparam>
|
||||
/// <param name="message">Message to deliver.</param>
|
||||
/// <returns>A task with the publish.</returns>
|
||||
public Task PublishAsync<TMessage>(TMessage message)
|
||||
where TMessage : class, IMessageHubMessage => Task.Run(() => this.Publish(message));
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,59 +1,55 @@
|
||||
namespace Unosquare.Swan.Components
|
||||
{
|
||||
using System;
|
||||
|
||||
using System;
|
||||
|
||||
namespace Unosquare.Swan.Components {
|
||||
/// <summary>
|
||||
/// Base class for messages that provides weak reference storage of the sender.
|
||||
/// </summary>
|
||||
public abstract class MessageHubMessageBase
|
||||
: IMessageHubMessage {
|
||||
/// <summary>
|
||||
/// Base class for messages that provides weak reference storage of the sender.
|
||||
/// Store a WeakReference to the sender just in case anyone is daft enough to
|
||||
/// keep the message around and prevent the sender from being collected.
|
||||
/// </summary>
|
||||
public abstract class MessageHubMessageBase
|
||||
: IMessageHubMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Store a WeakReference to the sender just in case anyone is daft enough to
|
||||
/// keep the message around and prevent the sender from being collected.
|
||||
/// </summary>
|
||||
private readonly WeakReference _sender;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MessageHubMessageBase"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <exception cref="System.ArgumentNullException">sender.</exception>
|
||||
protected MessageHubMessageBase(object sender)
|
||||
{
|
||||
if (sender == null)
|
||||
throw new ArgumentNullException(nameof(sender));
|
||||
|
||||
_sender = new WeakReference(sender);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The sender of the message, or null if not supported by the message implementation.
|
||||
/// </summary>
|
||||
public object Sender => _sender?.Target;
|
||||
}
|
||||
|
||||
private readonly WeakReference _sender;
|
||||
|
||||
/// <summary>
|
||||
/// Generic message with user specified content.
|
||||
/// Initializes a new instance of the <see cref="MessageHubMessageBase"/> class.
|
||||
/// </summary>
|
||||
/// <typeparam name="TContent">Content type to store.</typeparam>
|
||||
public class MessageHubGenericMessage<TContent>
|
||||
: MessageHubMessageBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MessageHubGenericMessage{TContent}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="content">The content.</param>
|
||||
public MessageHubGenericMessage(object sender, TContent content)
|
||||
: base(sender)
|
||||
{
|
||||
Content = content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contents of the message.
|
||||
/// </summary>
|
||||
public TContent Content { get; protected set; }
|
||||
}
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <exception cref="System.ArgumentNullException">sender.</exception>
|
||||
protected MessageHubMessageBase(Object sender) {
|
||||
if(sender == null) {
|
||||
throw new ArgumentNullException(nameof(sender));
|
||||
}
|
||||
|
||||
this._sender = new WeakReference(sender);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The sender of the message, or null if not supported by the message implementation.
|
||||
/// </summary>
|
||||
public Object Sender => this._sender?.Target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic message with user specified content.
|
||||
/// </summary>
|
||||
/// <typeparam name="TContent">Content type to store.</typeparam>
|
||||
public class MessageHubGenericMessage<TContent>
|
||||
: MessageHubMessageBase {
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MessageHubGenericMessage{TContent}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="content">The content.</param>
|
||||
public MessageHubGenericMessage(Object sender, TContent content)
|
||||
: base(sender) => this.Content = content;
|
||||
|
||||
/// <summary>
|
||||
/// Contents of the message.
|
||||
/// </summary>
|
||||
public TContent Content {
|
||||
get; protected set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +1,49 @@
|
||||
namespace Unosquare.Swan.Components
|
||||
{
|
||||
using System;
|
||||
using System;
|
||||
#if NETSTANDARD1_3
|
||||
using System.Reflection;
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
namespace Unosquare.Swan.Components {
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents an active subscription to a message.
|
||||
/// </summary>
|
||||
public sealed class MessageHubSubscriptionToken
|
||||
: IDisposable {
|
||||
private readonly WeakReference _hub;
|
||||
private readonly Type _messageType;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an active subscription to a message.
|
||||
/// Initializes a new instance of the <see cref="MessageHubSubscriptionToken"/> class.
|
||||
/// </summary>
|
||||
public sealed class MessageHubSubscriptionToken
|
||||
: IDisposable
|
||||
{
|
||||
private readonly WeakReference _hub;
|
||||
private readonly Type _messageType;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MessageHubSubscriptionToken"/> class.
|
||||
/// </summary>
|
||||
/// <param name="hub">The hub.</param>
|
||||
/// <param name="messageType">Type of the message.</param>
|
||||
/// <exception cref="System.ArgumentNullException">hub.</exception>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">messageType.</exception>
|
||||
public MessageHubSubscriptionToken(IMessageHub hub, Type messageType)
|
||||
{
|
||||
if (hub == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hub));
|
||||
}
|
||||
|
||||
if (!typeof(IMessageHubMessage).IsAssignableFrom(messageType))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(messageType));
|
||||
}
|
||||
|
||||
_hub = new WeakReference(hub);
|
||||
_messageType = messageType;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
if (_hub.IsAlive && _hub.Target is IMessageHub hub)
|
||||
{
|
||||
var unsubscribeMethod = typeof(IMessageHub).GetMethod(nameof(IMessageHub.Unsubscribe),
|
||||
new[] {typeof(MessageHubSubscriptionToken)});
|
||||
unsubscribeMethod = unsubscribeMethod.MakeGenericMethod(_messageType);
|
||||
unsubscribeMethod.Invoke(hub, new object[] {this});
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
/// <param name="hub">The hub.</param>
|
||||
/// <param name="messageType">Type of the message.</param>
|
||||
/// <exception cref="System.ArgumentNullException">hub.</exception>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">messageType.</exception>
|
||||
public MessageHubSubscriptionToken(IMessageHub hub, Type messageType) {
|
||||
if(hub == null) {
|
||||
throw new ArgumentNullException(nameof(hub));
|
||||
}
|
||||
|
||||
if(!typeof(IMessageHubMessage).IsAssignableFrom(messageType)) {
|
||||
throw new ArgumentOutOfRangeException(nameof(messageType));
|
||||
}
|
||||
|
||||
this._hub = new WeakReference(hub);
|
||||
this._messageType = messageType;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() {
|
||||
if(this._hub.IsAlive && this._hub.Target is IMessageHub hub) {
|
||||
System.Reflection.MethodInfo unsubscribeMethod = typeof(IMessageHub).GetMethod(nameof(IMessageHub.Unsubscribe),
|
||||
new[] { typeof(MessageHubSubscriptionToken) });
|
||||
unsubscribeMethod = unsubscribeMethod.MakeGenericMethod(this._messageType);
|
||||
_ = unsubscribeMethod.Invoke(hub, new Object[] { this });
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,424 +1,390 @@
|
||||
namespace Unosquare.Swan.Components
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Exceptions;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Unosquare.Swan.Exceptions;
|
||||
|
||||
namespace Unosquare.Swan.Components {
|
||||
/// <summary>
|
||||
/// Represents an abstract class for Object Factory.
|
||||
/// </summary>
|
||||
public abstract class ObjectFactoryBase {
|
||||
/// <summary>
|
||||
/// Represents an abstract class for Object Factory.
|
||||
/// Whether to assume this factory successfully constructs its objects
|
||||
///
|
||||
/// Generally set to true for delegate style factories as CanResolve cannot delve
|
||||
/// into the delegates they contain.
|
||||
/// </summary>
|
||||
public abstract class ObjectFactoryBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether to assume this factory successfully constructs its objects
|
||||
///
|
||||
/// Generally set to true for delegate style factories as CanResolve cannot delve
|
||||
/// into the delegates they contain.
|
||||
/// </summary>
|
||||
public virtual bool AssumeConstruction => false;
|
||||
|
||||
/// <summary>
|
||||
/// The type the factory instantiates.
|
||||
/// </summary>
|
||||
public abstract Type CreatesType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor to use, if specified.
|
||||
/// </summary>
|
||||
public ConstructorInfo Constructor { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the singleton variant.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The singleton variant.
|
||||
/// </value>
|
||||
/// <exception cref="DependencyContainerRegistrationException">singleton.</exception>
|
||||
public virtual ObjectFactoryBase SingletonVariant =>
|
||||
throw new DependencyContainerRegistrationException(GetType(), "singleton");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the multi instance variant.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The multi instance variant.
|
||||
/// </value>
|
||||
/// <exception cref="DependencyContainerRegistrationException">multi-instance.</exception>
|
||||
public virtual ObjectFactoryBase MultiInstanceVariant =>
|
||||
throw new DependencyContainerRegistrationException(GetType(), "multi-instance");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the strong reference variant.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The strong reference variant.
|
||||
/// </value>
|
||||
/// <exception cref="DependencyContainerRegistrationException">strong reference.</exception>
|
||||
public virtual ObjectFactoryBase StrongReferenceVariant =>
|
||||
throw new DependencyContainerRegistrationException(GetType(), "strong reference");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the weak reference variant.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The weak reference variant.
|
||||
/// </value>
|
||||
/// <exception cref="DependencyContainerRegistrationException">weak reference.</exception>
|
||||
public virtual ObjectFactoryBase WeakReferenceVariant =>
|
||||
throw new DependencyContainerRegistrationException(GetType(), "weak reference");
|
||||
|
||||
/// <summary>
|
||||
/// Create the type.
|
||||
/// </summary>
|
||||
/// <param name="requestedType">Type user requested to be resolved.</param>
|
||||
/// <param name="container">Container that requested the creation.</param>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <returns> Instance of type. </returns>
|
||||
public abstract object GetObject(
|
||||
Type requestedType,
|
||||
DependencyContainer container,
|
||||
DependencyContainerResolveOptions options);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the factory for child container.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="parent">The parent.</param>
|
||||
/// <param name="child">The child.</param>
|
||||
/// <returns></returns>
|
||||
public virtual ObjectFactoryBase GetFactoryForChildContainer(
|
||||
Type type,
|
||||
DependencyContainer parent,
|
||||
DependencyContainer child)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Boolean AssumeConstruction => false;
|
||||
|
||||
/// <summary>
|
||||
/// IObjectFactory that creates new instances of types for each resolution.
|
||||
/// The type the factory instantiates.
|
||||
/// </summary>
|
||||
internal class MultiInstanceFactory : ObjectFactoryBase
|
||||
{
|
||||
private readonly Type _registerType;
|
||||
private readonly Type _registerImplementation;
|
||||
|
||||
public MultiInstanceFactory(Type registerType, Type registerImplementation)
|
||||
{
|
||||
if (registerImplementation.IsAbstract() || registerImplementation.IsInterface())
|
||||
{
|
||||
throw new DependencyContainerRegistrationException(registerImplementation,
|
||||
"MultiInstanceFactory",
|
||||
true);
|
||||
}
|
||||
|
||||
if (!DependencyContainer.IsValidAssignment(registerType, registerImplementation))
|
||||
{
|
||||
throw new DependencyContainerRegistrationException(registerImplementation,
|
||||
"MultiInstanceFactory",
|
||||
true);
|
||||
}
|
||||
|
||||
_registerType = registerType;
|
||||
_registerImplementation = registerImplementation;
|
||||
}
|
||||
|
||||
public override Type CreatesType => _registerImplementation;
|
||||
|
||||
public override ObjectFactoryBase SingletonVariant =>
|
||||
new SingletonFactory(_registerType, _registerImplementation);
|
||||
|
||||
public override ObjectFactoryBase MultiInstanceVariant => this;
|
||||
|
||||
public override object GetObject(
|
||||
Type requestedType,
|
||||
DependencyContainer container,
|
||||
DependencyContainerResolveOptions options)
|
||||
{
|
||||
try
|
||||
{
|
||||
return container.RegisteredTypes.ConstructType(_registerImplementation, Constructor, options);
|
||||
}
|
||||
catch (DependencyContainerResolutionException ex)
|
||||
{
|
||||
throw new DependencyContainerResolutionException(_registerType, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract Type CreatesType {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IObjectFactory that invokes a specified delegate to construct the object.
|
||||
/// Constructor to use, if specified.
|
||||
/// </summary>
|
||||
internal class DelegateFactory : ObjectFactoryBase
|
||||
{
|
||||
private readonly Type _registerType;
|
||||
|
||||
private readonly Func<DependencyContainer, Dictionary<string, object>, object> _factory;
|
||||
|
||||
public DelegateFactory(
|
||||
Type registerType,
|
||||
Func<DependencyContainer,
|
||||
Dictionary<string, object>, object> factory)
|
||||
{
|
||||
_factory = factory ?? throw new ArgumentNullException(nameof(factory));
|
||||
|
||||
_registerType = registerType;
|
||||
}
|
||||
|
||||
public override bool AssumeConstruction => true;
|
||||
|
||||
public override Type CreatesType => _registerType;
|
||||
|
||||
public override ObjectFactoryBase WeakReferenceVariant => new WeakDelegateFactory(_registerType, _factory);
|
||||
|
||||
public override ObjectFactoryBase StrongReferenceVariant => this;
|
||||
|
||||
public override object GetObject(
|
||||
Type requestedType,
|
||||
DependencyContainer container,
|
||||
DependencyContainerResolveOptions options)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _factory.Invoke(container, options.ConstructorParameters);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new DependencyContainerResolutionException(_registerType, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ConstructorInfo Constructor {
|
||||
get; private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IObjectFactory that invokes a specified delegate to construct the object
|
||||
/// Holds the delegate using a weak reference.
|
||||
/// Gets the singleton variant.
|
||||
/// </summary>
|
||||
internal class WeakDelegateFactory : ObjectFactoryBase
|
||||
{
|
||||
private readonly Type _registerType;
|
||||
|
||||
private readonly WeakReference _factory;
|
||||
|
||||
public WeakDelegateFactory(Type registerType,
|
||||
Func<DependencyContainer, Dictionary<string, object>, object> factory)
|
||||
{
|
||||
if (factory == null)
|
||||
throw new ArgumentNullException(nameof(factory));
|
||||
|
||||
_factory = new WeakReference(factory);
|
||||
|
||||
_registerType = registerType;
|
||||
}
|
||||
|
||||
public override bool AssumeConstruction => true;
|
||||
|
||||
public override Type CreatesType => _registerType;
|
||||
|
||||
public override ObjectFactoryBase StrongReferenceVariant
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!(_factory.Target is Func<DependencyContainer, Dictionary<string, object>, object> factory))
|
||||
throw new DependencyContainerWeakReferenceException(_registerType);
|
||||
|
||||
return new DelegateFactory(_registerType, factory);
|
||||
}
|
||||
}
|
||||
|
||||
public override ObjectFactoryBase WeakReferenceVariant => this;
|
||||
|
||||
public override object GetObject(
|
||||
Type requestedType,
|
||||
DependencyContainer container,
|
||||
DependencyContainerResolveOptions options)
|
||||
{
|
||||
if (!(_factory.Target is Func<DependencyContainer, Dictionary<string, object>, object> factory))
|
||||
throw new DependencyContainerWeakReferenceException(_registerType);
|
||||
|
||||
try
|
||||
{
|
||||
return factory.Invoke(container, options.ConstructorParameters);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new DependencyContainerResolutionException(_registerType, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// The singleton variant.
|
||||
/// </value>
|
||||
/// <exception cref="DependencyContainerRegistrationException">singleton.</exception>
|
||||
public virtual ObjectFactoryBase SingletonVariant =>
|
||||
throw new DependencyContainerRegistrationException(this.GetType(), "singleton");
|
||||
|
||||
/// <summary>
|
||||
/// Stores an particular instance to return for a type.
|
||||
/// Gets the multi instance variant.
|
||||
/// </summary>
|
||||
internal class InstanceFactory : ObjectFactoryBase, IDisposable
|
||||
{
|
||||
private readonly Type _registerType;
|
||||
private readonly Type _registerImplementation;
|
||||
private readonly object _instance;
|
||||
|
||||
public InstanceFactory(Type registerType, Type registerImplementation, object instance)
|
||||
{
|
||||
if (!DependencyContainer.IsValidAssignment(registerType, registerImplementation))
|
||||
throw new DependencyContainerRegistrationException(registerImplementation, "InstanceFactory", true);
|
||||
|
||||
_registerType = registerType;
|
||||
_registerImplementation = registerImplementation;
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
public override bool AssumeConstruction => true;
|
||||
|
||||
public override Type CreatesType => _registerImplementation;
|
||||
|
||||
public override ObjectFactoryBase MultiInstanceVariant =>
|
||||
new MultiInstanceFactory(_registerType, _registerImplementation);
|
||||
|
||||
public override ObjectFactoryBase WeakReferenceVariant =>
|
||||
new WeakInstanceFactory(_registerType, _registerImplementation, _instance);
|
||||
|
||||
public override ObjectFactoryBase StrongReferenceVariant => this;
|
||||
|
||||
public override object GetObject(
|
||||
Type requestedType,
|
||||
DependencyContainer container,
|
||||
DependencyContainerResolveOptions options)
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
var disposable = _instance as IDisposable;
|
||||
|
||||
disposable?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// The multi instance variant.
|
||||
/// </value>
|
||||
/// <exception cref="DependencyContainerRegistrationException">multi-instance.</exception>
|
||||
public virtual ObjectFactoryBase MultiInstanceVariant =>
|
||||
throw new DependencyContainerRegistrationException(this.GetType(), "multi-instance");
|
||||
|
||||
/// <summary>
|
||||
/// Stores the instance with a weak reference.
|
||||
/// Gets the strong reference variant.
|
||||
/// </summary>
|
||||
internal class WeakInstanceFactory : ObjectFactoryBase, IDisposable
|
||||
{
|
||||
private readonly Type _registerType;
|
||||
private readonly Type _registerImplementation;
|
||||
private readonly WeakReference _instance;
|
||||
|
||||
public WeakInstanceFactory(Type registerType, Type registerImplementation, object instance)
|
||||
{
|
||||
if (!DependencyContainer.IsValidAssignment(registerType, registerImplementation))
|
||||
{
|
||||
throw new DependencyContainerRegistrationException(
|
||||
registerImplementation,
|
||||
"WeakInstanceFactory",
|
||||
true);
|
||||
}
|
||||
|
||||
_registerType = registerType;
|
||||
_registerImplementation = registerImplementation;
|
||||
_instance = new WeakReference(instance);
|
||||
}
|
||||
|
||||
public override Type CreatesType => _registerImplementation;
|
||||
|
||||
public override ObjectFactoryBase MultiInstanceVariant =>
|
||||
new MultiInstanceFactory(_registerType, _registerImplementation);
|
||||
|
||||
public override ObjectFactoryBase WeakReferenceVariant => this;
|
||||
|
||||
public override ObjectFactoryBase StrongReferenceVariant
|
||||
{
|
||||
get
|
||||
{
|
||||
var instance = _instance.Target;
|
||||
|
||||
if (instance == null)
|
||||
throw new DependencyContainerWeakReferenceException(_registerType);
|
||||
|
||||
return new InstanceFactory(_registerType, _registerImplementation, instance);
|
||||
}
|
||||
}
|
||||
|
||||
public override object GetObject(
|
||||
Type requestedType,
|
||||
DependencyContainer container,
|
||||
DependencyContainerResolveOptions options)
|
||||
{
|
||||
var instance = _instance.Target;
|
||||
|
||||
if (instance == null)
|
||||
throw new DependencyContainerWeakReferenceException(_registerType);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void Dispose() => (_instance.Target as IDisposable)?.Dispose();
|
||||
}
|
||||
|
||||
/// <value>
|
||||
/// The strong reference variant.
|
||||
/// </value>
|
||||
/// <exception cref="DependencyContainerRegistrationException">strong reference.</exception>
|
||||
public virtual ObjectFactoryBase StrongReferenceVariant =>
|
||||
throw new DependencyContainerRegistrationException(this.GetType(), "strong reference");
|
||||
|
||||
/// <summary>
|
||||
/// A factory that lazy instantiates a type and always returns the same instance.
|
||||
/// Gets the weak reference variant.
|
||||
/// </summary>
|
||||
internal class SingletonFactory : ObjectFactoryBase, IDisposable
|
||||
{
|
||||
private readonly Type _registerType;
|
||||
private readonly Type _registerImplementation;
|
||||
private readonly object _singletonLock = new object();
|
||||
private object _current;
|
||||
|
||||
public SingletonFactory(Type registerType, Type registerImplementation)
|
||||
{
|
||||
if (registerImplementation.IsAbstract() || registerImplementation.IsInterface())
|
||||
{
|
||||
throw new DependencyContainerRegistrationException(registerImplementation, nameof(SingletonFactory), true);
|
||||
}
|
||||
|
||||
if (!DependencyContainer.IsValidAssignment(registerType, registerImplementation))
|
||||
{
|
||||
throw new DependencyContainerRegistrationException(registerImplementation, nameof(SingletonFactory), true);
|
||||
}
|
||||
|
||||
_registerType = registerType;
|
||||
_registerImplementation = registerImplementation;
|
||||
}
|
||||
|
||||
public override Type CreatesType => _registerImplementation;
|
||||
|
||||
public override ObjectFactoryBase SingletonVariant => this;
|
||||
|
||||
public override ObjectFactoryBase MultiInstanceVariant =>
|
||||
new MultiInstanceFactory(_registerType, _registerImplementation);
|
||||
|
||||
public override object GetObject(
|
||||
Type requestedType,
|
||||
DependencyContainer container,
|
||||
DependencyContainerResolveOptions options)
|
||||
{
|
||||
if (options.ConstructorParameters.Count != 0)
|
||||
throw new ArgumentException("Cannot specify parameters for singleton types");
|
||||
|
||||
lock (_singletonLock)
|
||||
{
|
||||
if (_current == null)
|
||||
_current = container.RegisteredTypes.ConstructType(_registerImplementation, Constructor, options);
|
||||
}
|
||||
|
||||
return _current;
|
||||
}
|
||||
|
||||
public override ObjectFactoryBase GetFactoryForChildContainer(
|
||||
Type type,
|
||||
DependencyContainer parent,
|
||||
DependencyContainer child)
|
||||
{
|
||||
// We make sure that the singleton is constructed before the child container takes the factory.
|
||||
// Otherwise the results would vary depending on whether or not the parent container had resolved
|
||||
// the type before the child container does.
|
||||
GetObject(type, parent, DependencyContainerResolveOptions.Default);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Dispose() => (_current as IDisposable)?.Dispose();
|
||||
}
|
||||
/// <value>
|
||||
/// The weak reference variant.
|
||||
/// </value>
|
||||
/// <exception cref="DependencyContainerRegistrationException">weak reference.</exception>
|
||||
public virtual ObjectFactoryBase WeakReferenceVariant =>
|
||||
throw new DependencyContainerRegistrationException(this.GetType(), "weak reference");
|
||||
|
||||
/// <summary>
|
||||
/// Create the type.
|
||||
/// </summary>
|
||||
/// <param name="requestedType">Type user requested to be resolved.</param>
|
||||
/// <param name="container">Container that requested the creation.</param>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <returns> Instance of type. </returns>
|
||||
public abstract Object GetObject(
|
||||
Type requestedType,
|
||||
DependencyContainer container,
|
||||
DependencyContainerResolveOptions options);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the factory for child container.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="parent">The parent.</param>
|
||||
/// <param name="child">The child.</param>
|
||||
/// <returns></returns>
|
||||
public virtual ObjectFactoryBase GetFactoryForChildContainer(
|
||||
Type type,
|
||||
DependencyContainer parent,
|
||||
DependencyContainer child) => this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// IObjectFactory that creates new instances of types for each resolution.
|
||||
/// </summary>
|
||||
internal class MultiInstanceFactory : ObjectFactoryBase {
|
||||
private readonly Type _registerType;
|
||||
private readonly Type _registerImplementation;
|
||||
|
||||
public MultiInstanceFactory(Type registerType, Type registerImplementation) {
|
||||
if(registerImplementation.IsAbstract() || registerImplementation.IsInterface()) {
|
||||
throw new DependencyContainerRegistrationException(registerImplementation,
|
||||
"MultiInstanceFactory",
|
||||
true);
|
||||
}
|
||||
|
||||
if(!DependencyContainer.IsValidAssignment(registerType, registerImplementation)) {
|
||||
throw new DependencyContainerRegistrationException(registerImplementation,
|
||||
"MultiInstanceFactory",
|
||||
true);
|
||||
}
|
||||
|
||||
this._registerType = registerType;
|
||||
this._registerImplementation = registerImplementation;
|
||||
}
|
||||
|
||||
public override Type CreatesType => this._registerImplementation;
|
||||
|
||||
public override ObjectFactoryBase SingletonVariant =>
|
||||
new SingletonFactory(this._registerType, this._registerImplementation);
|
||||
|
||||
public override ObjectFactoryBase MultiInstanceVariant => this;
|
||||
|
||||
public override Object GetObject(
|
||||
Type requestedType,
|
||||
DependencyContainer container,
|
||||
DependencyContainerResolveOptions options) {
|
||||
try {
|
||||
return container.RegisteredTypes.ConstructType(this._registerImplementation, this.Constructor, options);
|
||||
} catch(DependencyContainerResolutionException ex) {
|
||||
throw new DependencyContainerResolutionException(this._registerType, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// IObjectFactory that invokes a specified delegate to construct the object.
|
||||
/// </summary>
|
||||
internal class DelegateFactory : ObjectFactoryBase {
|
||||
private readonly Type _registerType;
|
||||
|
||||
private readonly Func<DependencyContainer, Dictionary<String, Object>, Object> _factory;
|
||||
|
||||
public DelegateFactory(
|
||||
Type registerType,
|
||||
Func<DependencyContainer,
|
||||
Dictionary<String, Object>, Object> factory) {
|
||||
this._factory = factory ?? throw new ArgumentNullException(nameof(factory));
|
||||
|
||||
this._registerType = registerType;
|
||||
}
|
||||
|
||||
public override Boolean AssumeConstruction => true;
|
||||
|
||||
public override Type CreatesType => this._registerType;
|
||||
|
||||
public override ObjectFactoryBase WeakReferenceVariant => new WeakDelegateFactory(this._registerType, this._factory);
|
||||
|
||||
public override ObjectFactoryBase StrongReferenceVariant => this;
|
||||
|
||||
public override Object GetObject(
|
||||
Type requestedType,
|
||||
DependencyContainer container,
|
||||
DependencyContainerResolveOptions options) {
|
||||
try {
|
||||
return this._factory.Invoke(container, options.ConstructorParameters);
|
||||
} catch(Exception ex) {
|
||||
throw new DependencyContainerResolutionException(this._registerType, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// IObjectFactory that invokes a specified delegate to construct the object
|
||||
/// Holds the delegate using a weak reference.
|
||||
/// </summary>
|
||||
internal class WeakDelegateFactory : ObjectFactoryBase {
|
||||
private readonly Type _registerType;
|
||||
|
||||
private readonly WeakReference _factory;
|
||||
|
||||
public WeakDelegateFactory(Type registerType,
|
||||
Func<DependencyContainer, Dictionary<String, Object>, Object> factory) {
|
||||
if(factory == null) {
|
||||
throw new ArgumentNullException(nameof(factory));
|
||||
}
|
||||
|
||||
this._factory = new WeakReference(factory);
|
||||
|
||||
this._registerType = registerType;
|
||||
}
|
||||
|
||||
public override Boolean AssumeConstruction => true;
|
||||
|
||||
public override Type CreatesType => this._registerType;
|
||||
|
||||
public override ObjectFactoryBase StrongReferenceVariant {
|
||||
get {
|
||||
if(!(this._factory.Target is Func<DependencyContainer, Dictionary<global::System.String, global::System.Object>, global::System.Object> factory)) {
|
||||
throw new DependencyContainerWeakReferenceException(this._registerType);
|
||||
}
|
||||
|
||||
return new DelegateFactory(this._registerType, factory);
|
||||
}
|
||||
}
|
||||
|
||||
public override ObjectFactoryBase WeakReferenceVariant => this;
|
||||
|
||||
public override Object GetObject(
|
||||
Type requestedType,
|
||||
DependencyContainer container,
|
||||
DependencyContainerResolveOptions options) {
|
||||
if(!(this._factory.Target is Func<DependencyContainer, Dictionary<global::System.String, global::System.Object>, global::System.Object> factory)) {
|
||||
throw new DependencyContainerWeakReferenceException(this._registerType);
|
||||
}
|
||||
|
||||
try {
|
||||
return factory.Invoke(container, options.ConstructorParameters);
|
||||
} catch(Exception ex) {
|
||||
throw new DependencyContainerResolutionException(this._registerType, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores an particular instance to return for a type.
|
||||
/// </summary>
|
||||
internal class InstanceFactory : ObjectFactoryBase, IDisposable {
|
||||
private readonly Type _registerType;
|
||||
private readonly Type _registerImplementation;
|
||||
private readonly Object _instance;
|
||||
|
||||
public InstanceFactory(Type registerType, Type registerImplementation, Object instance) {
|
||||
if(!DependencyContainer.IsValidAssignment(registerType, registerImplementation)) {
|
||||
throw new DependencyContainerRegistrationException(registerImplementation, "InstanceFactory", true);
|
||||
}
|
||||
|
||||
this._registerType = registerType;
|
||||
this._registerImplementation = registerImplementation;
|
||||
this._instance = instance;
|
||||
}
|
||||
|
||||
public override Boolean AssumeConstruction => true;
|
||||
|
||||
public override Type CreatesType => this._registerImplementation;
|
||||
|
||||
public override ObjectFactoryBase MultiInstanceVariant =>
|
||||
new MultiInstanceFactory(this._registerType, this._registerImplementation);
|
||||
|
||||
public override ObjectFactoryBase WeakReferenceVariant =>
|
||||
new WeakInstanceFactory(this._registerType, this._registerImplementation, this._instance);
|
||||
|
||||
public override ObjectFactoryBase StrongReferenceVariant => this;
|
||||
|
||||
public override Object GetObject(
|
||||
Type requestedType,
|
||||
DependencyContainer container,
|
||||
DependencyContainerResolveOptions options) => this._instance;
|
||||
|
||||
public void Dispose() {
|
||||
IDisposable disposable = this._instance as IDisposable;
|
||||
|
||||
disposable?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores the instance with a weak reference.
|
||||
/// </summary>
|
||||
internal class WeakInstanceFactory : ObjectFactoryBase, IDisposable {
|
||||
private readonly Type _registerType;
|
||||
private readonly Type _registerImplementation;
|
||||
private readonly WeakReference _instance;
|
||||
|
||||
public WeakInstanceFactory(Type registerType, Type registerImplementation, Object instance) {
|
||||
if(!DependencyContainer.IsValidAssignment(registerType, registerImplementation)) {
|
||||
throw new DependencyContainerRegistrationException(
|
||||
registerImplementation,
|
||||
"WeakInstanceFactory",
|
||||
true);
|
||||
}
|
||||
|
||||
this._registerType = registerType;
|
||||
this._registerImplementation = registerImplementation;
|
||||
this._instance = new WeakReference(instance);
|
||||
}
|
||||
|
||||
public override Type CreatesType => this._registerImplementation;
|
||||
|
||||
public override ObjectFactoryBase MultiInstanceVariant =>
|
||||
new MultiInstanceFactory(this._registerType, this._registerImplementation);
|
||||
|
||||
public override ObjectFactoryBase WeakReferenceVariant => this;
|
||||
|
||||
public override ObjectFactoryBase StrongReferenceVariant {
|
||||
get {
|
||||
Object instance = this._instance.Target;
|
||||
|
||||
if(instance == null) {
|
||||
throw new DependencyContainerWeakReferenceException(this._registerType);
|
||||
}
|
||||
|
||||
return new InstanceFactory(this._registerType, this._registerImplementation, instance);
|
||||
}
|
||||
}
|
||||
|
||||
public override Object GetObject(
|
||||
Type requestedType,
|
||||
DependencyContainer container,
|
||||
DependencyContainerResolveOptions options) {
|
||||
Object instance = this._instance.Target;
|
||||
|
||||
if(instance == null) {
|
||||
throw new DependencyContainerWeakReferenceException(this._registerType);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void Dispose() => (this._instance.Target as IDisposable)?.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A factory that lazy instantiates a type and always returns the same instance.
|
||||
/// </summary>
|
||||
internal class SingletonFactory : ObjectFactoryBase, IDisposable {
|
||||
private readonly Type _registerType;
|
||||
private readonly Type _registerImplementation;
|
||||
private readonly Object _singletonLock = new Object();
|
||||
private Object _current;
|
||||
|
||||
public SingletonFactory(Type registerType, Type registerImplementation) {
|
||||
if(registerImplementation.IsAbstract() || registerImplementation.IsInterface()) {
|
||||
throw new DependencyContainerRegistrationException(registerImplementation, nameof(SingletonFactory), true);
|
||||
}
|
||||
|
||||
if(!DependencyContainer.IsValidAssignment(registerType, registerImplementation)) {
|
||||
throw new DependencyContainerRegistrationException(registerImplementation, nameof(SingletonFactory), true);
|
||||
}
|
||||
|
||||
this._registerType = registerType;
|
||||
this._registerImplementation = registerImplementation;
|
||||
}
|
||||
|
||||
public override Type CreatesType => this._registerImplementation;
|
||||
|
||||
public override ObjectFactoryBase SingletonVariant => this;
|
||||
|
||||
public override ObjectFactoryBase MultiInstanceVariant =>
|
||||
new MultiInstanceFactory(this._registerType, this._registerImplementation);
|
||||
|
||||
public override Object GetObject(
|
||||
Type requestedType,
|
||||
DependencyContainer container,
|
||||
DependencyContainerResolveOptions options) {
|
||||
if(options.ConstructorParameters.Count != 0) {
|
||||
throw new ArgumentException("Cannot specify parameters for singleton types");
|
||||
}
|
||||
|
||||
lock(this._singletonLock) {
|
||||
if(this._current == null) {
|
||||
this._current = container.RegisteredTypes.ConstructType(this._registerImplementation, this.Constructor, options);
|
||||
}
|
||||
}
|
||||
|
||||
return this._current;
|
||||
}
|
||||
|
||||
public override ObjectFactoryBase GetFactoryForChildContainer(
|
||||
Type type,
|
||||
DependencyContainer parent,
|
||||
DependencyContainer child) {
|
||||
// We make sure that the singleton is constructed before the child container takes the factory.
|
||||
// Otherwise the results would vary depending on whether or not the parent container had resolved
|
||||
// the type before the child container does.
|
||||
_ = this.GetObject(type, parent, DependencyContainerResolveOptions.Default);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Dispose() => (this._current as IDisposable)?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,51 @@
|
||||
namespace Unosquare.Swan.Components
|
||||
{
|
||||
using System;
|
||||
|
||||
namespace Unosquare.Swan.Components {
|
||||
/// <summary>
|
||||
/// Represents the text of the standard output and standard error
|
||||
/// of a process, including its exit code.
|
||||
/// </summary>
|
||||
public class ProcessResult {
|
||||
/// <summary>
|
||||
/// Represents the text of the standard output and standard error
|
||||
/// of a process, including its exit code.
|
||||
/// Initializes a new instance of the <see cref="ProcessResult" /> class.
|
||||
/// </summary>
|
||||
public class ProcessResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ProcessResult" /> class.
|
||||
/// </summary>
|
||||
/// <param name="exitCode">The exit code.</param>
|
||||
/// <param name="standardOutput">The standard output.</param>
|
||||
/// <param name="standardError">The standard error.</param>
|
||||
public ProcessResult(int exitCode, string standardOutput, string standardError)
|
||||
{
|
||||
ExitCode = exitCode;
|
||||
StandardOutput = standardOutput;
|
||||
StandardError = standardError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the exit code.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The exit code.
|
||||
/// </value>
|
||||
public int ExitCode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text of the standard output.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The standard output.
|
||||
/// </value>
|
||||
public string StandardOutput { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text of the standard error.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The standard error.
|
||||
/// </value>
|
||||
public string StandardError { get; }
|
||||
}
|
||||
/// <param name="exitCode">The exit code.</param>
|
||||
/// <param name="standardOutput">The standard output.</param>
|
||||
/// <param name="standardError">The standard error.</param>
|
||||
public ProcessResult(Int32 exitCode, String standardOutput, String standardError) {
|
||||
this.ExitCode = exitCode;
|
||||
this.StandardOutput = standardOutput;
|
||||
this.StandardError = standardError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the exit code.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The exit code.
|
||||
/// </value>
|
||||
public Int32 ExitCode {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text of the standard output.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The standard output.
|
||||
/// </value>
|
||||
public String StandardOutput {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text of the standard error.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The standard error.
|
||||
/// </value>
|
||||
public String StandardError {
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,474 +1,447 @@
|
||||
namespace Unosquare.Swan.Components
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Unosquare.Swan.Components {
|
||||
/// <summary>
|
||||
/// Provides methods to help create external processes, and efficiently capture the
|
||||
/// standard error and standard output streams.
|
||||
/// </summary>
|
||||
public static class ProcessRunner {
|
||||
/// <summary>
|
||||
/// Provides methods to help create external processes, and efficiently capture the
|
||||
/// standard error and standard output streams.
|
||||
/// Defines a delegate to handle binary data reception from the standard
|
||||
/// output or standard error streams from a process.
|
||||
/// </summary>
|
||||
public static class ProcessRunner
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a delegate to handle binary data reception from the standard
|
||||
/// output or standard error streams from a process.
|
||||
/// </summary>
|
||||
/// <param name="processData">The process data.</param>
|
||||
/// <param name="process">The process.</param>
|
||||
public delegate void ProcessDataReceivedCallback(byte[] processData, Process process);
|
||||
|
||||
/// <summary>
|
||||
/// Runs the process asynchronously and if the exit code is 0,
|
||||
/// returns all of the standard output text. If the exit code is something other than 0
|
||||
/// it returns the contents of standard error.
|
||||
/// This method is meant to be used for programs that output a relatively small amount of text.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
/// <returns>The type of the result produced by this Task.</returns>
|
||||
public static Task<string> GetProcessOutputAsync(string filename, CancellationToken ct = default) =>
|
||||
GetProcessOutputAsync(filename, string.Empty, ct);
|
||||
|
||||
/// <summary>
|
||||
/// Runs the process asynchronously and if the exit code is 0,
|
||||
/// returns all of the standard output text. If the exit code is something other than 0
|
||||
/// it returns the contents of standard error.
|
||||
/// This method is meant to be used for programs that output a relatively small amount of text.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="arguments">The arguments.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
/// <returns>The type of the result produced by this Task.</returns>
|
||||
/// <example>
|
||||
/// The following code explains how to run an external process using the
|
||||
/// <see cref="GetProcessOutputAsync(string, string, CancellationToken)"/> method.
|
||||
/// <code>
|
||||
/// class Example
|
||||
/// {
|
||||
/// using System.Threading.Tasks;
|
||||
/// using Unosquare.Swan.Components;
|
||||
///
|
||||
/// static async Task Main()
|
||||
/// {
|
||||
/// // execute a process and save its output
|
||||
/// var data = await ProcessRunner.
|
||||
/// GetProcessOutputAsync("dotnet", "--help");
|
||||
///
|
||||
/// // print the output
|
||||
/// data.WriteLine();
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static async Task<string> GetProcessOutputAsync(
|
||||
string filename,
|
||||
string arguments,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var result = await GetProcessResultAsync(filename, arguments, ct).ConfigureAwait(false);
|
||||
return result.ExitCode == 0 ? result.StandardOutput : result.StandardError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the process output asynchronous.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="arguments">The arguments.</param>
|
||||
/// <param name="workingDirectory">The working directory.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
/// <returns>
|
||||
/// The type of the result produced by this Task.
|
||||
/// </returns>
|
||||
public static async Task<string> GetProcessOutputAsync(
|
||||
string filename,
|
||||
string arguments,
|
||||
string workingDirectory,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var result = await GetProcessResultAsync(filename, arguments, workingDirectory, ct: ct).ConfigureAwait(false);
|
||||
return result.ExitCode == 0 ? result.StandardOutput : result.StandardError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the process asynchronously and if the exit code is 0,
|
||||
/// returns all of the standard output text. If the exit code is something other than 0
|
||||
/// it returns the contents of standard error.
|
||||
/// This method is meant to be used for programs that output a relatively small amount
|
||||
/// of text using a different encoder.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="arguments">The arguments.</param>
|
||||
/// <param name="encoding">The encoding.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
/// <returns>
|
||||
/// The type of the result produced by this Task.
|
||||
/// </returns>
|
||||
public static async Task<string> GetProcessEncodedOutputAsync(
|
||||
string filename,
|
||||
string arguments = "",
|
||||
Encoding encoding = null,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var result = await GetProcessResultAsync(filename, arguments, null, encoding, ct).ConfigureAwait(false);
|
||||
return result.ExitCode == 0 ? result.StandardOutput : result.StandardError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes a process asynchronously and returns the text of the standard output and standard error streams
|
||||
/// along with the exit code. This method is meant to be used for programs that output a relatively small
|
||||
/// amount of text.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="arguments">The arguments.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
/// <returns>
|
||||
/// Text of the standard output and standard error streams along with the exit code as a <see cref="ProcessResult" /> instance.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">filename.</exception>
|
||||
public static Task<ProcessResult> GetProcessResultAsync(
|
||||
string filename,
|
||||
string arguments = "",
|
||||
CancellationToken ct = default) =>
|
||||
GetProcessResultAsync(filename, arguments, null, Definitions.CurrentAnsiEncoding, ct);
|
||||
|
||||
/// <summary>
|
||||
/// Executes a process asynchronously and returns the text of the standard output and standard error streams
|
||||
/// along with the exit code. This method is meant to be used for programs that output a relatively small
|
||||
/// amount of text.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="arguments">The arguments.</param>
|
||||
/// <param name="workingDirectory">The working directory.</param>
|
||||
/// <param name="encoding">The encoding.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
/// <returns>
|
||||
/// Text of the standard output and standard error streams along with the exit code as a <see cref="ProcessResult" /> instance.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">filename.</exception>
|
||||
/// <example>
|
||||
/// The following code describes how to run an external process using the <see cref="GetProcessResultAsync(string, string, string, Encoding, CancellationToken)" /> method.
|
||||
/// <code>
|
||||
/// class Example
|
||||
/// {
|
||||
/// using System.Threading.Tasks;
|
||||
/// using Unosquare.Swan.Components;
|
||||
/// static async Task Main()
|
||||
/// {
|
||||
/// // Execute a process asynchronously
|
||||
/// var data = await ProcessRunner.GetProcessResultAsync("dotnet", "--help");
|
||||
/// // print out the exit code
|
||||
/// $"{data.ExitCode}".WriteLine();
|
||||
/// // print out the output
|
||||
/// data.StandardOutput.WriteLine();
|
||||
/// // and the error if exists
|
||||
/// data.StandardError.Error();
|
||||
/// }
|
||||
/// }
|
||||
/// </code></example>
|
||||
public static async Task<ProcessResult> GetProcessResultAsync(
|
||||
string filename,
|
||||
string arguments,
|
||||
string workingDirectory,
|
||||
Encoding encoding = null,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
if (filename == null)
|
||||
throw new ArgumentNullException(nameof(filename));
|
||||
|
||||
if (encoding == null)
|
||||
encoding = Definitions.CurrentAnsiEncoding;
|
||||
|
||||
var standardOutputBuilder = new StringBuilder();
|
||||
var standardErrorBuilder = new StringBuilder();
|
||||
|
||||
var processReturn = await RunProcessAsync(
|
||||
filename,
|
||||
arguments,
|
||||
workingDirectory,
|
||||
(data, proc) => { standardOutputBuilder.Append(encoding.GetString(data)); },
|
||||
(data, proc) => { standardErrorBuilder.Append(encoding.GetString(data)); },
|
||||
encoding,
|
||||
true,
|
||||
ct)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return new ProcessResult(processReturn, standardOutputBuilder.ToString(), standardErrorBuilder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs an external process asynchronously, providing callbacks to
|
||||
/// capture binary data from the standard error and standard output streams.
|
||||
/// The callbacks contain a reference to the process so you can respond to output or
|
||||
/// error streams by writing to the process' input stream.
|
||||
/// The exit code (return value) will be -1 for forceful termination of the process.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="arguments">The arguments.</param>
|
||||
/// <param name="workingDirectory">The working directory.</param>
|
||||
/// <param name="onOutputData">The on output data.</param>
|
||||
/// <param name="onErrorData">The on error data.</param>
|
||||
/// <param name="encoding">The encoding.</param>
|
||||
/// <param name="syncEvents">if set to <c>true</c> the next data callback will wait until the current one completes.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
/// <returns>
|
||||
/// Value type will be -1 for forceful termination of the process.
|
||||
/// </returns>
|
||||
public static Task<int> RunProcessAsync(
|
||||
string filename,
|
||||
string arguments,
|
||||
string workingDirectory,
|
||||
ProcessDataReceivedCallback onOutputData,
|
||||
ProcessDataReceivedCallback onErrorData,
|
||||
Encoding encoding,
|
||||
bool syncEvents = true,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
if (filename == null)
|
||||
throw new ArgumentNullException(nameof(filename));
|
||||
|
||||
return Task.Run(() =>
|
||||
{
|
||||
// Setup the process and its corresponding start info
|
||||
var process = new Process
|
||||
{
|
||||
EnableRaisingEvents = false,
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
Arguments = arguments,
|
||||
CreateNoWindow = true,
|
||||
FileName = filename,
|
||||
RedirectStandardError = true,
|
||||
StandardErrorEncoding = encoding,
|
||||
RedirectStandardOutput = true,
|
||||
StandardOutputEncoding = encoding,
|
||||
UseShellExecute = false,
|
||||
/// <param name="processData">The process data.</param>
|
||||
/// <param name="process">The process.</param>
|
||||
public delegate void ProcessDataReceivedCallback(Byte[] processData, Process process);
|
||||
|
||||
/// <summary>
|
||||
/// Runs the process asynchronously and if the exit code is 0,
|
||||
/// returns all of the standard output text. If the exit code is something other than 0
|
||||
/// it returns the contents of standard error.
|
||||
/// This method is meant to be used for programs that output a relatively small amount of text.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
/// <returns>The type of the result produced by this Task.</returns>
|
||||
public static Task<String> GetProcessOutputAsync(String filename, CancellationToken ct = default) =>
|
||||
GetProcessOutputAsync(filename, String.Empty, ct);
|
||||
|
||||
/// <summary>
|
||||
/// Runs the process asynchronously and if the exit code is 0,
|
||||
/// returns all of the standard output text. If the exit code is something other than 0
|
||||
/// it returns the contents of standard error.
|
||||
/// This method is meant to be used for programs that output a relatively small amount of text.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="arguments">The arguments.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
/// <returns>The type of the result produced by this Task.</returns>
|
||||
/// <example>
|
||||
/// The following code explains how to run an external process using the
|
||||
/// <see cref="GetProcessOutputAsync(String, String, CancellationToken)"/> method.
|
||||
/// <code>
|
||||
/// class Example
|
||||
/// {
|
||||
/// using System.Threading.Tasks;
|
||||
/// using Unosquare.Swan.Components;
|
||||
///
|
||||
/// static async Task Main()
|
||||
/// {
|
||||
/// // execute a process and save its output
|
||||
/// var data = await ProcessRunner.
|
||||
/// GetProcessOutputAsync("dotnet", "--help");
|
||||
///
|
||||
/// // print the output
|
||||
/// data.WriteLine();
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static async Task<String> GetProcessOutputAsync(
|
||||
String filename,
|
||||
String arguments,
|
||||
CancellationToken ct = default) {
|
||||
ProcessResult result = await GetProcessResultAsync(filename, arguments, ct).ConfigureAwait(false);
|
||||
return result.ExitCode == 0 ? result.StandardOutput : result.StandardError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the process output asynchronous.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="arguments">The arguments.</param>
|
||||
/// <param name="workingDirectory">The working directory.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
/// <returns>
|
||||
/// The type of the result produced by this Task.
|
||||
/// </returns>
|
||||
public static async Task<String> GetProcessOutputAsync(
|
||||
String filename,
|
||||
String arguments,
|
||||
String workingDirectory,
|
||||
CancellationToken ct = default) {
|
||||
ProcessResult result = await GetProcessResultAsync(filename, arguments, workingDirectory, ct: ct).ConfigureAwait(false);
|
||||
return result.ExitCode == 0 ? result.StandardOutput : result.StandardError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the process asynchronously and if the exit code is 0,
|
||||
/// returns all of the standard output text. If the exit code is something other than 0
|
||||
/// it returns the contents of standard error.
|
||||
/// This method is meant to be used for programs that output a relatively small amount
|
||||
/// of text using a different encoder.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="arguments">The arguments.</param>
|
||||
/// <param name="encoding">The encoding.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
/// <returns>
|
||||
/// The type of the result produced by this Task.
|
||||
/// </returns>
|
||||
public static async Task<String> GetProcessEncodedOutputAsync(
|
||||
String filename,
|
||||
String arguments = "",
|
||||
Encoding encoding = null,
|
||||
CancellationToken ct = default) {
|
||||
ProcessResult result = await GetProcessResultAsync(filename, arguments, null, encoding, ct).ConfigureAwait(false);
|
||||
return result.ExitCode == 0 ? result.StandardOutput : result.StandardError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes a process asynchronously and returns the text of the standard output and standard error streams
|
||||
/// along with the exit code. This method is meant to be used for programs that output a relatively small
|
||||
/// amount of text.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="arguments">The arguments.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
/// <returns>
|
||||
/// Text of the standard output and standard error streams along with the exit code as a <see cref="ProcessResult" /> instance.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">filename.</exception>
|
||||
public static Task<ProcessResult> GetProcessResultAsync(
|
||||
String filename,
|
||||
String arguments = "",
|
||||
CancellationToken ct = default) =>
|
||||
GetProcessResultAsync(filename, arguments, null, Definitions.CurrentAnsiEncoding, ct);
|
||||
|
||||
/// <summary>
|
||||
/// Executes a process asynchronously and returns the text of the standard output and standard error streams
|
||||
/// along with the exit code. This method is meant to be used for programs that output a relatively small
|
||||
/// amount of text.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="arguments">The arguments.</param>
|
||||
/// <param name="workingDirectory">The working directory.</param>
|
||||
/// <param name="encoding">The encoding.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
/// <returns>
|
||||
/// Text of the standard output and standard error streams along with the exit code as a <see cref="ProcessResult" /> instance.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">filename.</exception>
|
||||
/// <example>
|
||||
/// The following code describes how to run an external process using the <see cref="GetProcessResultAsync(String, String, String, Encoding, CancellationToken)" /> method.
|
||||
/// <code>
|
||||
/// class Example
|
||||
/// {
|
||||
/// using System.Threading.Tasks;
|
||||
/// using Unosquare.Swan.Components;
|
||||
/// static async Task Main()
|
||||
/// {
|
||||
/// // Execute a process asynchronously
|
||||
/// var data = await ProcessRunner.GetProcessResultAsync("dotnet", "--help");
|
||||
/// // print out the exit code
|
||||
/// $"{data.ExitCode}".WriteLine();
|
||||
/// // print out the output
|
||||
/// data.StandardOutput.WriteLine();
|
||||
/// // and the error if exists
|
||||
/// data.StandardError.Error();
|
||||
/// }
|
||||
/// }
|
||||
/// </code></example>
|
||||
public static async Task<ProcessResult> GetProcessResultAsync(
|
||||
String filename,
|
||||
String arguments,
|
||||
String workingDirectory,
|
||||
Encoding encoding = null,
|
||||
CancellationToken ct = default) {
|
||||
if(filename == null) {
|
||||
throw new ArgumentNullException(nameof(filename));
|
||||
}
|
||||
|
||||
if(encoding == null) {
|
||||
encoding = Definitions.CurrentAnsiEncoding;
|
||||
}
|
||||
|
||||
StringBuilder standardOutputBuilder = new StringBuilder();
|
||||
StringBuilder standardErrorBuilder = new StringBuilder();
|
||||
|
||||
Int32 processReturn = await RunProcessAsync(
|
||||
filename,
|
||||
arguments,
|
||||
workingDirectory,
|
||||
(data, proc) => standardOutputBuilder.Append(encoding.GetString(data)),
|
||||
(data, proc) => standardErrorBuilder.Append(encoding.GetString(data)),
|
||||
encoding,
|
||||
true,
|
||||
ct)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return new ProcessResult(processReturn, standardOutputBuilder.ToString(), standardErrorBuilder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs an external process asynchronously, providing callbacks to
|
||||
/// capture binary data from the standard error and standard output streams.
|
||||
/// The callbacks contain a reference to the process so you can respond to output or
|
||||
/// error streams by writing to the process' input stream.
|
||||
/// The exit code (return value) will be -1 for forceful termination of the process.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="arguments">The arguments.</param>
|
||||
/// <param name="workingDirectory">The working directory.</param>
|
||||
/// <param name="onOutputData">The on output data.</param>
|
||||
/// <param name="onErrorData">The on error data.</param>
|
||||
/// <param name="encoding">The encoding.</param>
|
||||
/// <param name="syncEvents">if set to <c>true</c> the next data callback will wait until the current one completes.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
/// <returns>
|
||||
/// Value type will be -1 for forceful termination of the process.
|
||||
/// </returns>
|
||||
public static Task<Int32> RunProcessAsync(
|
||||
String filename,
|
||||
String arguments,
|
||||
String workingDirectory,
|
||||
ProcessDataReceivedCallback onOutputData,
|
||||
ProcessDataReceivedCallback onErrorData,
|
||||
Encoding encoding,
|
||||
Boolean syncEvents = true,
|
||||
CancellationToken ct = default) {
|
||||
if(filename == null) {
|
||||
throw new ArgumentNullException(nameof(filename));
|
||||
}
|
||||
|
||||
return Task.Run(() => {
|
||||
// Setup the process and its corresponding start info
|
||||
Process process = new Process {
|
||||
EnableRaisingEvents = false,
|
||||
StartInfo = new ProcessStartInfo {
|
||||
Arguments = arguments,
|
||||
CreateNoWindow = true,
|
||||
FileName = filename,
|
||||
RedirectStandardError = true,
|
||||
StandardErrorEncoding = encoding,
|
||||
RedirectStandardOutput = true,
|
||||
StandardOutputEncoding = encoding,
|
||||
UseShellExecute = false,
|
||||
#if NET452
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(workingDirectory))
|
||||
process.StartInfo.WorkingDirectory = workingDirectory;
|
||||
|
||||
// Launch the process and discard any buffered data for standard error and standard output
|
||||
process.Start();
|
||||
process.StandardError.DiscardBufferedData();
|
||||
process.StandardOutput.DiscardBufferedData();
|
||||
|
||||
// Launch the asynchronous stream reading tasks
|
||||
var readTasks = new Task[2];
|
||||
readTasks[0] = CopyStreamAsync(
|
||||
process,
|
||||
process.StandardOutput.BaseStream,
|
||||
onOutputData,
|
||||
syncEvents,
|
||||
ct);
|
||||
readTasks[1] = CopyStreamAsync(
|
||||
process,
|
||||
process.StandardError.BaseStream,
|
||||
onErrorData,
|
||||
syncEvents,
|
||||
ct);
|
||||
|
||||
try
|
||||
{
|
||||
// Wait for all tasks to complete
|
||||
Task.WaitAll(readTasks, ct);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Wait for the process to exit
|
||||
while (ct.IsCancellationRequested == false)
|
||||
{
|
||||
if (process.HasExited || process.WaitForExit(5))
|
||||
break;
|
||||
}
|
||||
|
||||
// Forcefully kill the process if it do not exit
|
||||
try
|
||||
{
|
||||
if (process.HasExited == false)
|
||||
process.Kill();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// swallow
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Retrieve and return the exit code.
|
||||
// -1 signals error
|
||||
return process.HasExited ? process.ExitCode : -1;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs an external process asynchronously, providing callbacks to
|
||||
/// capture binary data from the standard error and standard output streams.
|
||||
/// The callbacks contain a reference to the process so you can respond to output or
|
||||
/// error streams by writing to the process' input stream.
|
||||
/// The exit code (return value) will be -1 for forceful termination of the process.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="arguments">The arguments.</param>
|
||||
/// <param name="onOutputData">The on output data.</param>
|
||||
/// <param name="onErrorData">The on error data.</param>
|
||||
/// <param name="syncEvents">if set to <c>true</c> the next data callback will wait until the current one completes.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
/// <returns>Value type will be -1 for forceful termination of the process.</returns>
|
||||
/// <example>
|
||||
/// The following example illustrates how to run an external process using the
|
||||
/// <see cref="RunProcessAsync(string, string, ProcessDataReceivedCallback, ProcessDataReceivedCallback, bool, CancellationToken)"/>
|
||||
/// method.
|
||||
/// <code>
|
||||
/// class Example
|
||||
/// {
|
||||
/// using System.Diagnostics;
|
||||
/// using System.Text;
|
||||
/// using System.Threading.Tasks;
|
||||
/// using Unosquare.Swan;
|
||||
/// using Unosquare.Swan.Components;
|
||||
///
|
||||
/// static async Task Main()
|
||||
/// {
|
||||
/// // Execute a process asynchronously
|
||||
/// var data = await ProcessRunner
|
||||
/// .RunProcessAsync("dotnet", "--help", Print, Print);
|
||||
///
|
||||
/// // flush all messages
|
||||
/// Terminal.Flush();
|
||||
/// }
|
||||
///
|
||||
/// // a callback to print both output or errors
|
||||
/// static void Print(byte[] data, Process proc) =>
|
||||
/// Encoding.GetEncoding(0).GetString(data).WriteLine();
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static Task<int> RunProcessAsync(
|
||||
string filename,
|
||||
string arguments,
|
||||
ProcessDataReceivedCallback onOutputData,
|
||||
ProcessDataReceivedCallback onErrorData,
|
||||
bool syncEvents = true,
|
||||
CancellationToken ct = default)
|
||||
=> RunProcessAsync(
|
||||
filename,
|
||||
arguments,
|
||||
null,
|
||||
onOutputData,
|
||||
onErrorData,
|
||||
Definitions.CurrentAnsiEncoding,
|
||||
syncEvents,
|
||||
ct);
|
||||
|
||||
/// <summary>
|
||||
/// Copies the stream asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="process">The process.</param>
|
||||
/// <param name="baseStream">The source stream.</param>
|
||||
/// <param name="onDataCallback">The on data callback.</param>
|
||||
/// <param name="syncEvents">if set to <c>true</c> [synchronize events].</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
/// <returns>Total copies stream.</returns>
|
||||
private static Task<ulong> CopyStreamAsync(
|
||||
Process process,
|
||||
Stream baseStream,
|
||||
ProcessDataReceivedCallback onDataCallback,
|
||||
bool syncEvents,
|
||||
CancellationToken ct)
|
||||
{
|
||||
return Task.Factory.StartNew(async () =>
|
||||
{
|
||||
// define some state variables
|
||||
var swapBuffer = new byte[2048]; // the buffer to copy data from one stream to the next
|
||||
ulong totalCount = 0; // the total amount of bytes read
|
||||
var hasExited = false;
|
||||
|
||||
while (ct.IsCancellationRequested == false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if process is no longer valid
|
||||
// if this condition holds, simply read the last bits of data available.
|
||||
int readCount; // the bytes read in any given event
|
||||
if (process.HasExited || process.WaitForExit(1))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
readCount = await baseStream.ReadAsync(swapBuffer, 0, swapBuffer.Length, ct);
|
||||
|
||||
if (readCount > 0)
|
||||
{
|
||||
totalCount += (ulong) readCount;
|
||||
onDataCallback?.Invoke(swapBuffer.Skip(0).Take(readCount).ToArray(), process);
|
||||
}
|
||||
else
|
||||
{
|
||||
hasExited = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
hasExited = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasExited) break;
|
||||
|
||||
// Try reading from the stream. < 0 means no read occurred.
|
||||
readCount = await baseStream.ReadAsync(swapBuffer, 0, swapBuffer.Length, ct);
|
||||
|
||||
// When no read is done, we need to let is rest for a bit
|
||||
if (readCount <= 0)
|
||||
{
|
||||
await Task.Delay(1, ct); // do not hog CPU cycles doing nothing.
|
||||
continue;
|
||||
}
|
||||
|
||||
totalCount += (ulong) readCount;
|
||||
if (onDataCallback == null) continue;
|
||||
|
||||
// Create the buffer to pass to the callback
|
||||
var eventBuffer = swapBuffer.Skip(0).Take(readCount).ToArray();
|
||||
|
||||
// Create the data processing callback invocation
|
||||
var eventTask =
|
||||
Task.Factory.StartNew(() => { onDataCallback.Invoke(eventBuffer, process); }, ct);
|
||||
|
||||
// wait for the event to process before the next read occurs
|
||||
if (syncEvents) eventTask.Wait(ct);
|
||||
}
|
||||
catch
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return totalCount;
|
||||
}, ct).Unwrap();
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if(!String.IsNullOrWhiteSpace(workingDirectory)) {
|
||||
process.StartInfo.WorkingDirectory = workingDirectory;
|
||||
}
|
||||
|
||||
// Launch the process and discard any buffered data for standard error and standard output
|
||||
process.Start();
|
||||
process.StandardError.DiscardBufferedData();
|
||||
process.StandardOutput.DiscardBufferedData();
|
||||
|
||||
// Launch the asynchronous stream reading tasks
|
||||
Task[] readTasks = new Task[2];
|
||||
readTasks[0] = CopyStreamAsync(
|
||||
process,
|
||||
process.StandardOutput.BaseStream,
|
||||
onOutputData,
|
||||
syncEvents,
|
||||
ct);
|
||||
readTasks[1] = CopyStreamAsync(
|
||||
process,
|
||||
process.StandardError.BaseStream,
|
||||
onErrorData,
|
||||
syncEvents,
|
||||
ct);
|
||||
|
||||
try {
|
||||
// Wait for all tasks to complete
|
||||
Task.WaitAll(readTasks, ct);
|
||||
} catch(TaskCanceledException) {
|
||||
// ignore
|
||||
} finally {
|
||||
// Wait for the process to exit
|
||||
while(ct.IsCancellationRequested == false) {
|
||||
if(process.HasExited || process.WaitForExit(5)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Forcefully kill the process if it do not exit
|
||||
try {
|
||||
if(process.HasExited == false) {
|
||||
process.Kill();
|
||||
}
|
||||
} catch {
|
||||
// swallow
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Retrieve and return the exit code.
|
||||
// -1 signals error
|
||||
return process.HasExited ? process.ExitCode : -1;
|
||||
} catch {
|
||||
return -1;
|
||||
}
|
||||
}, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs an external process asynchronously, providing callbacks to
|
||||
/// capture binary data from the standard error and standard output streams.
|
||||
/// The callbacks contain a reference to the process so you can respond to output or
|
||||
/// error streams by writing to the process' input stream.
|
||||
/// The exit code (return value) will be -1 for forceful termination of the process.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="arguments">The arguments.</param>
|
||||
/// <param name="onOutputData">The on output data.</param>
|
||||
/// <param name="onErrorData">The on error data.</param>
|
||||
/// <param name="syncEvents">if set to <c>true</c> the next data callback will wait until the current one completes.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
/// <returns>Value type will be -1 for forceful termination of the process.</returns>
|
||||
/// <example>
|
||||
/// The following example illustrates how to run an external process using the
|
||||
/// <see cref="RunProcessAsync(String, String, ProcessDataReceivedCallback, ProcessDataReceivedCallback, Boolean, CancellationToken)"/>
|
||||
/// method.
|
||||
/// <code>
|
||||
/// class Example
|
||||
/// {
|
||||
/// using System.Diagnostics;
|
||||
/// using System.Text;
|
||||
/// using System.Threading.Tasks;
|
||||
/// using Unosquare.Swan;
|
||||
/// using Unosquare.Swan.Components;
|
||||
///
|
||||
/// static async Task Main()
|
||||
/// {
|
||||
/// // Execute a process asynchronously
|
||||
/// var data = await ProcessRunner
|
||||
/// .RunProcessAsync("dotnet", "--help", Print, Print);
|
||||
///
|
||||
/// // flush all messages
|
||||
/// Terminal.Flush();
|
||||
/// }
|
||||
///
|
||||
/// // a callback to print both output or errors
|
||||
/// static void Print(byte[] data, Process proc) =>
|
||||
/// Encoding.GetEncoding(0).GetString(data).WriteLine();
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static Task<Int32> RunProcessAsync(
|
||||
String filename,
|
||||
String arguments,
|
||||
ProcessDataReceivedCallback onOutputData,
|
||||
ProcessDataReceivedCallback onErrorData,
|
||||
Boolean syncEvents = true,
|
||||
CancellationToken ct = default)
|
||||
=> RunProcessAsync(
|
||||
filename,
|
||||
arguments,
|
||||
null,
|
||||
onOutputData,
|
||||
onErrorData,
|
||||
Definitions.CurrentAnsiEncoding,
|
||||
syncEvents,
|
||||
ct);
|
||||
|
||||
/// <summary>
|
||||
/// Copies the stream asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="process">The process.</param>
|
||||
/// <param name="baseStream">The source stream.</param>
|
||||
/// <param name="onDataCallback">The on data callback.</param>
|
||||
/// <param name="syncEvents">if set to <c>true</c> [synchronize events].</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
/// <returns>Total copies stream.</returns>
|
||||
private static Task<UInt64> CopyStreamAsync(
|
||||
Process process,
|
||||
Stream baseStream,
|
||||
ProcessDataReceivedCallback onDataCallback,
|
||||
Boolean syncEvents,
|
||||
CancellationToken ct) => Task.Factory.StartNew(async () => {
|
||||
// define some state variables
|
||||
Byte[] swapBuffer = new Byte[2048]; // the buffer to copy data from one stream to the next
|
||||
UInt64 totalCount = 0; // the total amount of bytes read
|
||||
Boolean hasExited = false;
|
||||
|
||||
while(ct.IsCancellationRequested == false) {
|
||||
try {
|
||||
// Check if process is no longer valid
|
||||
// if this condition holds, simply read the last bits of data available.
|
||||
Int32 readCount; // the bytes read in any given event
|
||||
if(process.HasExited || process.WaitForExit(1)) {
|
||||
while(true) {
|
||||
try {
|
||||
readCount = await baseStream.ReadAsync(swapBuffer, 0, swapBuffer.Length, ct);
|
||||
|
||||
if(readCount > 0) {
|
||||
totalCount += (UInt64)readCount;
|
||||
onDataCallback?.Invoke(swapBuffer.Skip(0).Take(readCount).ToArray(), process);
|
||||
} else {
|
||||
hasExited = true;
|
||||
break;
|
||||
}
|
||||
} catch {
|
||||
hasExited = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(hasExited) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Try reading from the stream. < 0 means no read occurred.
|
||||
readCount = await baseStream.ReadAsync(swapBuffer, 0, swapBuffer.Length, ct);
|
||||
|
||||
// When no read is done, we need to let is rest for a bit
|
||||
if(readCount <= 0) {
|
||||
await Task.Delay(1, ct); // do not hog CPU cycles doing nothing.
|
||||
continue;
|
||||
}
|
||||
|
||||
totalCount += (UInt64)readCount;
|
||||
if(onDataCallback == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create the buffer to pass to the callback
|
||||
Byte[] eventBuffer = swapBuffer.Skip(0).Take(readCount).ToArray();
|
||||
|
||||
// Create the data processing callback invocation
|
||||
Task eventTask =
|
||||
Task.Factory.StartNew(() => onDataCallback.Invoke(eventBuffer, process), ct);
|
||||
|
||||
// wait for the event to process before the next read occurs
|
||||
if(syncEvents) {
|
||||
eventTask.Wait(ct);
|
||||
}
|
||||
} catch {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return totalCount;
|
||||
}, ct).Unwrap();
|
||||
}
|
||||
}
|
||||
@@ -1,143 +1,128 @@
|
||||
namespace Unosquare.Swan.Components
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Abstractions;
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Unosquare.Swan.Abstractions;
|
||||
|
||||
namespace Unosquare.Swan.Components {
|
||||
/// <summary>
|
||||
/// A time measurement artifact.
|
||||
/// </summary>
|
||||
internal sealed class RealTimeClock : IDisposable {
|
||||
private readonly Stopwatch _chrono = new Stopwatch();
|
||||
private ISyncLocker _locker = SyncLockerFactory.Create(useSlim: true);
|
||||
private Int64 _offsetTicks;
|
||||
private Double _speedRatio = 1.0d;
|
||||
private Boolean _isDisposed;
|
||||
|
||||
/// <summary>
|
||||
/// A time measurement artifact.
|
||||
/// Initializes a new instance of the <see cref="RealTimeClock"/> class.
|
||||
/// The clock starts paused and at the 0 position.
|
||||
/// </summary>
|
||||
internal sealed class RealTimeClock : IDisposable
|
||||
{
|
||||
private readonly Stopwatch _chrono = new Stopwatch();
|
||||
private ISyncLocker _locker = SyncLockerFactory.Create(useSlim: true);
|
||||
private long _offsetTicks;
|
||||
private double _speedRatio = 1.0d;
|
||||
private bool _isDisposed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RealTimeClock"/> class.
|
||||
/// The clock starts paused and at the 0 position.
|
||||
/// </summary>
|
||||
public RealTimeClock()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the clock position.
|
||||
/// </summary>
|
||||
public TimeSpan Position
|
||||
{
|
||||
get
|
||||
{
|
||||
using (_locker.AcquireReaderLock())
|
||||
{
|
||||
return TimeSpan.FromTicks(
|
||||
_offsetTicks + Convert.ToInt64(_chrono.Elapsed.Ticks * SpeedRatio));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the clock is running.
|
||||
/// </summary>
|
||||
public bool IsRunning
|
||||
{
|
||||
get
|
||||
{
|
||||
using (_locker.AcquireReaderLock())
|
||||
{
|
||||
return _chrono.IsRunning;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the speed ratio at which the clock runs.
|
||||
/// </summary>
|
||||
public double SpeedRatio
|
||||
{
|
||||
get
|
||||
{
|
||||
using (_locker.AcquireReaderLock())
|
||||
{
|
||||
return _speedRatio;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
using (_locker.AcquireWriterLock())
|
||||
{
|
||||
if (value < 0d) value = 0d;
|
||||
|
||||
// Capture the initial position se we set it even after the speedratio has changed
|
||||
// this ensures a smooth position transition
|
||||
var initialPosition = Position;
|
||||
_speedRatio = value;
|
||||
Update(initialPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a new position value atomically.
|
||||
/// </summary>
|
||||
/// <param name="value">The new value that the position porperty will hold.</param>
|
||||
public void Update(TimeSpan value)
|
||||
{
|
||||
using (_locker.AcquireWriterLock())
|
||||
{
|
||||
var resume = _chrono.IsRunning;
|
||||
_chrono.Reset();
|
||||
_offsetTicks = value.Ticks;
|
||||
if (resume) _chrono.Start();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts or resumes the clock.
|
||||
/// </summary>
|
||||
public void Play()
|
||||
{
|
||||
using (_locker.AcquireWriterLock())
|
||||
{
|
||||
if (_chrono.IsRunning) return;
|
||||
_chrono.Start();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pauses the clock.
|
||||
/// </summary>
|
||||
public void Pause()
|
||||
{
|
||||
using (_locker.AcquireWriterLock())
|
||||
{
|
||||
_chrono.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the clock position to 0 and stops it.
|
||||
/// The speed ratio is not modified.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
using (_locker.AcquireWriterLock())
|
||||
{
|
||||
_offsetTicks = 0;
|
||||
_chrono.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
_isDisposed = true;
|
||||
_locker?.Dispose();
|
||||
_locker = null;
|
||||
}
|
||||
}
|
||||
public RealTimeClock() => this.Reset();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the clock position.
|
||||
/// </summary>
|
||||
public TimeSpan Position {
|
||||
get {
|
||||
using(this._locker.AcquireReaderLock()) {
|
||||
return TimeSpan.FromTicks(
|
||||
this._offsetTicks + Convert.ToInt64(this._chrono.Elapsed.Ticks * this.SpeedRatio));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the clock is running.
|
||||
/// </summary>
|
||||
public Boolean IsRunning {
|
||||
get {
|
||||
using(this._locker.AcquireReaderLock()) {
|
||||
return this._chrono.IsRunning;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the speed ratio at which the clock runs.
|
||||
/// </summary>
|
||||
public Double SpeedRatio {
|
||||
get {
|
||||
using(this._locker.AcquireReaderLock()) {
|
||||
return this._speedRatio;
|
||||
}
|
||||
}
|
||||
set {
|
||||
using(this._locker.AcquireWriterLock()) {
|
||||
if(value < 0d) {
|
||||
value = 0d;
|
||||
}
|
||||
|
||||
// Capture the initial position se we set it even after the speedratio has changed
|
||||
// this ensures a smooth position transition
|
||||
TimeSpan initialPosition = this.Position;
|
||||
this._speedRatio = value;
|
||||
this.Update(initialPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a new position value atomically.
|
||||
/// </summary>
|
||||
/// <param name="value">The new value that the position porperty will hold.</param>
|
||||
public void Update(TimeSpan value) {
|
||||
using(this._locker.AcquireWriterLock()) {
|
||||
Boolean resume = this._chrono.IsRunning;
|
||||
this._chrono.Reset();
|
||||
this._offsetTicks = value.Ticks;
|
||||
if(resume) {
|
||||
this._chrono.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts or resumes the clock.
|
||||
/// </summary>
|
||||
public void Play() {
|
||||
using(this._locker.AcquireWriterLock()) {
|
||||
if(this._chrono.IsRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._chrono.Start();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pauses the clock.
|
||||
/// </summary>
|
||||
public void Pause() {
|
||||
using(this._locker.AcquireWriterLock()) {
|
||||
this._chrono.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the clock position to 0 and stops it.
|
||||
/// The speed ratio is not modified.
|
||||
/// </summary>
|
||||
public void Reset() {
|
||||
using(this._locker.AcquireWriterLock()) {
|
||||
this._offsetTicks = 0;
|
||||
this._chrono.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() {
|
||||
if(this._isDisposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._isDisposed = true;
|
||||
this._locker?.Dispose();
|
||||
this._locker = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,132 +1,120 @@
|
||||
namespace Unosquare.Swan.Components
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Exceptions;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unosquare.Swan.Exceptions;
|
||||
|
||||
namespace Unosquare.Swan.Components {
|
||||
/// <summary>
|
||||
/// Registration options for "fluent" API.
|
||||
/// </summary>
|
||||
public sealed class RegisterOptions {
|
||||
private readonly TypesConcurrentDictionary _registeredTypes;
|
||||
private readonly DependencyContainer.TypeRegistration _registration;
|
||||
|
||||
/// <summary>
|
||||
/// Registration options for "fluent" API.
|
||||
/// Initializes a new instance of the <see cref="RegisterOptions" /> class.
|
||||
/// </summary>
|
||||
public sealed class RegisterOptions
|
||||
{
|
||||
private readonly TypesConcurrentDictionary _registeredTypes;
|
||||
private readonly DependencyContainer.TypeRegistration _registration;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RegisterOptions" /> class.
|
||||
/// </summary>
|
||||
/// <param name="registeredTypes">The registered types.</param>
|
||||
/// <param name="registration">The registration.</param>
|
||||
public RegisterOptions(TypesConcurrentDictionary registeredTypes, DependencyContainer.TypeRegistration registration)
|
||||
{
|
||||
_registeredTypes = registeredTypes;
|
||||
_registration = registration;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make registration a singleton (single instance) if possible.
|
||||
/// </summary>
|
||||
/// <returns>A registration options for fluent API.</returns>
|
||||
/// <exception cref="DependencyContainerRegistrationException">Generic constraint registration exception.</exception>
|
||||
public RegisterOptions AsSingleton()
|
||||
{
|
||||
var currentFactory = _registeredTypes.GetCurrentFactory(_registration);
|
||||
|
||||
if (currentFactory == null)
|
||||
throw new DependencyContainerRegistrationException(_registration.Type, "singleton");
|
||||
|
||||
return _registeredTypes.AddUpdateRegistration(_registration, currentFactory.SingletonVariant);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make registration multi-instance if possible.
|
||||
/// </summary>
|
||||
/// <returns>A registration options for fluent API.</returns>
|
||||
/// <exception cref="DependencyContainerRegistrationException">Generic constraint registration exception.</exception>
|
||||
public RegisterOptions AsMultiInstance()
|
||||
{
|
||||
var currentFactory = _registeredTypes.GetCurrentFactory(_registration);
|
||||
|
||||
if (currentFactory == null)
|
||||
throw new DependencyContainerRegistrationException(_registration.Type, "multi-instance");
|
||||
|
||||
return _registeredTypes.AddUpdateRegistration(_registration, currentFactory.MultiInstanceVariant);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make registration hold a weak reference if possible.
|
||||
/// </summary>
|
||||
/// <returns>A registration options for fluent API.</returns>
|
||||
/// <exception cref="DependencyContainerRegistrationException">Generic constraint registration exception.</exception>
|
||||
public RegisterOptions WithWeakReference()
|
||||
{
|
||||
var currentFactory = _registeredTypes.GetCurrentFactory(_registration);
|
||||
|
||||
if (currentFactory == null)
|
||||
throw new DependencyContainerRegistrationException(_registration.Type, "weak reference");
|
||||
|
||||
return _registeredTypes.AddUpdateRegistration(_registration, currentFactory.WeakReferenceVariant);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make registration hold a strong reference if possible.
|
||||
/// </summary>
|
||||
/// <returns>A registration options for fluent API.</returns>
|
||||
/// <exception cref="DependencyContainerRegistrationException">Generic constraint registration exception.</exception>
|
||||
public RegisterOptions WithStrongReference()
|
||||
{
|
||||
var currentFactory = _registeredTypes.GetCurrentFactory(_registration);
|
||||
|
||||
if (currentFactory == null)
|
||||
throw new DependencyContainerRegistrationException(_registration.Type, "strong reference");
|
||||
|
||||
return _registeredTypes.AddUpdateRegistration(_registration, currentFactory.StrongReferenceVariant);
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="registeredTypes">The registered types.</param>
|
||||
/// <param name="registration">The registration.</param>
|
||||
public RegisterOptions(TypesConcurrentDictionary registeredTypes, DependencyContainer.TypeRegistration registration) {
|
||||
this._registeredTypes = registeredTypes;
|
||||
this._registration = registration;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registration options for "fluent" API when registering multiple implementations.
|
||||
/// Make registration a singleton (single instance) if possible.
|
||||
/// </summary>
|
||||
public sealed class MultiRegisterOptions
|
||||
{
|
||||
private IEnumerable<RegisterOptions> _registerOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MultiRegisterOptions"/> class.
|
||||
/// </summary>
|
||||
/// <param name="registerOptions">The register options.</param>
|
||||
public MultiRegisterOptions(IEnumerable<RegisterOptions> registerOptions)
|
||||
{
|
||||
_registerOptions = registerOptions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make registration a singleton (single instance) if possible.
|
||||
/// </summary>
|
||||
/// <returns>A registration multi-instance for fluent API.</returns>
|
||||
/// <exception cref="DependencyContainerRegistrationException">Generic Constraint Registration Exception.</exception>
|
||||
public MultiRegisterOptions AsSingleton()
|
||||
{
|
||||
_registerOptions = ExecuteOnAllRegisterOptions(ro => ro.AsSingleton());
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make registration multi-instance if possible.
|
||||
/// </summary>
|
||||
/// <returns>A registration multi-instance for fluent API.</returns>
|
||||
/// <exception cref="DependencyContainerRegistrationException">Generic Constraint Registration Exception.</exception>
|
||||
public MultiRegisterOptions AsMultiInstance()
|
||||
{
|
||||
_registerOptions = ExecuteOnAllRegisterOptions(ro => ro.AsMultiInstance());
|
||||
return this;
|
||||
}
|
||||
|
||||
private IEnumerable<RegisterOptions> ExecuteOnAllRegisterOptions(
|
||||
Func<RegisterOptions, RegisterOptions> action)
|
||||
{
|
||||
return _registerOptions.Select(action).ToList();
|
||||
}
|
||||
}
|
||||
/// <returns>A registration options for fluent API.</returns>
|
||||
/// <exception cref="DependencyContainerRegistrationException">Generic constraint registration exception.</exception>
|
||||
public RegisterOptions AsSingleton() {
|
||||
ObjectFactoryBase currentFactory = this._registeredTypes.GetCurrentFactory(this._registration);
|
||||
|
||||
if(currentFactory == null) {
|
||||
throw new DependencyContainerRegistrationException(this._registration.Type, "singleton");
|
||||
}
|
||||
|
||||
return this._registeredTypes.AddUpdateRegistration(this._registration, currentFactory.SingletonVariant);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make registration multi-instance if possible.
|
||||
/// </summary>
|
||||
/// <returns>A registration options for fluent API.</returns>
|
||||
/// <exception cref="DependencyContainerRegistrationException">Generic constraint registration exception.</exception>
|
||||
public RegisterOptions AsMultiInstance() {
|
||||
ObjectFactoryBase currentFactory = this._registeredTypes.GetCurrentFactory(this._registration);
|
||||
|
||||
if(currentFactory == null) {
|
||||
throw new DependencyContainerRegistrationException(this._registration.Type, "multi-instance");
|
||||
}
|
||||
|
||||
return this._registeredTypes.AddUpdateRegistration(this._registration, currentFactory.MultiInstanceVariant);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make registration hold a weak reference if possible.
|
||||
/// </summary>
|
||||
/// <returns>A registration options for fluent API.</returns>
|
||||
/// <exception cref="DependencyContainerRegistrationException">Generic constraint registration exception.</exception>
|
||||
public RegisterOptions WithWeakReference() {
|
||||
ObjectFactoryBase currentFactory = this._registeredTypes.GetCurrentFactory(this._registration);
|
||||
|
||||
if(currentFactory == null) {
|
||||
throw new DependencyContainerRegistrationException(this._registration.Type, "weak reference");
|
||||
}
|
||||
|
||||
return this._registeredTypes.AddUpdateRegistration(this._registration, currentFactory.WeakReferenceVariant);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make registration hold a strong reference if possible.
|
||||
/// </summary>
|
||||
/// <returns>A registration options for fluent API.</returns>
|
||||
/// <exception cref="DependencyContainerRegistrationException">Generic constraint registration exception.</exception>
|
||||
public RegisterOptions WithStrongReference() {
|
||||
ObjectFactoryBase currentFactory = this._registeredTypes.GetCurrentFactory(this._registration);
|
||||
|
||||
if(currentFactory == null) {
|
||||
throw new DependencyContainerRegistrationException(this._registration.Type, "strong reference");
|
||||
}
|
||||
|
||||
return this._registeredTypes.AddUpdateRegistration(this._registration, currentFactory.StrongReferenceVariant);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registration options for "fluent" API when registering multiple implementations.
|
||||
/// </summary>
|
||||
public sealed class MultiRegisterOptions {
|
||||
private IEnumerable<RegisterOptions> _registerOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MultiRegisterOptions"/> class.
|
||||
/// </summary>
|
||||
/// <param name="registerOptions">The register options.</param>
|
||||
public MultiRegisterOptions(IEnumerable<RegisterOptions> registerOptions) => this._registerOptions = registerOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Make registration a singleton (single instance) if possible.
|
||||
/// </summary>
|
||||
/// <returns>A registration multi-instance for fluent API.</returns>
|
||||
/// <exception cref="DependencyContainerRegistrationException">Generic Constraint Registration Exception.</exception>
|
||||
public MultiRegisterOptions AsSingleton() {
|
||||
this._registerOptions = this.ExecuteOnAllRegisterOptions(ro => ro.AsSingleton());
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make registration multi-instance if possible.
|
||||
/// </summary>
|
||||
/// <returns>A registration multi-instance for fluent API.</returns>
|
||||
/// <exception cref="DependencyContainerRegistrationException">Generic Constraint Registration Exception.</exception>
|
||||
public MultiRegisterOptions AsMultiInstance() {
|
||||
this._registerOptions = this.ExecuteOnAllRegisterOptions(ro => ro.AsMultiInstance());
|
||||
return this;
|
||||
}
|
||||
|
||||
private IEnumerable<RegisterOptions> ExecuteOnAllRegisterOptions(
|
||||
Func<RegisterOptions, RegisterOptions> action) => this._registerOptions.Select(action).ToList();
|
||||
}
|
||||
}
|
||||
@@ -1,67 +1,63 @@
|
||||
namespace Unosquare.Swan.Components
|
||||
{
|
||||
using System;
|
||||
|
||||
public partial class DependencyContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Type Registration within the IoC Container.
|
||||
/// </summary>
|
||||
public sealed class TypeRegistration
|
||||
{
|
||||
private readonly int _hashCode;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TypeRegistration"/> class.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="name">The name.</param>
|
||||
public TypeRegistration(Type type, string name = null)
|
||||
{
|
||||
Type = type;
|
||||
Name = name ?? string.Empty;
|
||||
|
||||
_hashCode = string.Concat(Type.FullName, "|", Name).GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The type.
|
||||
/// </value>
|
||||
public Type Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name.
|
||||
/// </value>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="System.Object" />, is equal to this instance.
|
||||
/// </summary>
|
||||
/// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is TypeRegistration typeRegistration) || typeRegistration.Type != Type)
|
||||
return false;
|
||||
|
||||
return string.Compare(Name, typeRegistration.Name, StringComparison.Ordinal) == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
|
||||
/// </returns>
|
||||
public override int GetHashCode() => _hashCode;
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Unosquare.Swan.Components {
|
||||
public partial class DependencyContainer {
|
||||
/// <summary>
|
||||
/// Represents a Type Registration within the IoC Container.
|
||||
/// </summary>
|
||||
public sealed class TypeRegistration {
|
||||
private readonly Int32 _hashCode;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TypeRegistration"/> class.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="name">The name.</param>
|
||||
public TypeRegistration(Type type, String name = null) {
|
||||
this.Type = type;
|
||||
this.Name = name ?? String.Empty;
|
||||
|
||||
this._hashCode = String.Concat(this.Type.FullName, "|", this.Name).GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The type.
|
||||
/// </value>
|
||||
public Type Type {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name.
|
||||
/// </value>
|
||||
public String Name {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="System.Object" />, is equal to this instance.
|
||||
/// </summary>
|
||||
/// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override Boolean Equals(Object obj) => !(obj is TypeRegistration typeRegistration) || typeRegistration.Type != this.Type
|
||||
? false
|
||||
: String.Compare(this.Name, typeRegistration.Name, StringComparison.Ordinal) == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
|
||||
/// </returns>
|
||||
public override Int32 GetHashCode() => this._hashCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,352 +1,308 @@
|
||||
namespace Unosquare.Swan.Components
|
||||
{
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Exceptions;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unosquare.Swan.Exceptions;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Unosquare.Swan.Components {
|
||||
/// <summary>
|
||||
/// Represents a Concurrent Dictionary for TypeRegistration.
|
||||
/// </summary>
|
||||
public class TypesConcurrentDictionary : ConcurrentDictionary<DependencyContainer.TypeRegistration, ObjectFactoryBase> {
|
||||
private static readonly ConcurrentDictionary<ConstructorInfo, ObjectConstructor> ObjectConstructorCache =
|
||||
new ConcurrentDictionary<ConstructorInfo, ObjectConstructor>();
|
||||
|
||||
private readonly DependencyContainer _dependencyContainer;
|
||||
|
||||
internal TypesConcurrentDictionary(DependencyContainer dependencyContainer) => this._dependencyContainer = dependencyContainer;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Concurrent Dictionary for TypeRegistration.
|
||||
/// Represents a delegate to build an object with the parameters.
|
||||
/// </summary>
|
||||
public class TypesConcurrentDictionary : ConcurrentDictionary<DependencyContainer.TypeRegistration, ObjectFactoryBase>
|
||||
{
|
||||
private static readonly ConcurrentDictionary<ConstructorInfo, ObjectConstructor> ObjectConstructorCache =
|
||||
new ConcurrentDictionary<ConstructorInfo, ObjectConstructor>();
|
||||
|
||||
private readonly DependencyContainer _dependencyContainer;
|
||||
|
||||
internal TypesConcurrentDictionary(DependencyContainer dependencyContainer)
|
||||
{
|
||||
_dependencyContainer = dependencyContainer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a delegate to build an object with the parameters.
|
||||
/// </summary>
|
||||
/// <param name="parameters">The parameters.</param>
|
||||
/// <returns>The built object.</returns>
|
||||
public delegate object ObjectConstructor(params object[] parameters);
|
||||
|
||||
internal IEnumerable<object> Resolve(Type resolveType, bool includeUnnamed)
|
||||
{
|
||||
var registrations = Keys.Where(tr => tr.Type == resolveType)
|
||||
.Concat(GetParentRegistrationsForType(resolveType)).Distinct();
|
||||
|
||||
if (!includeUnnamed)
|
||||
registrations = registrations.Where(tr => tr.Name != string.Empty);
|
||||
|
||||
return registrations.Select(registration =>
|
||||
ResolveInternal(registration, DependencyContainerResolveOptions.Default));
|
||||
}
|
||||
|
||||
internal ObjectFactoryBase GetCurrentFactory(DependencyContainer.TypeRegistration registration)
|
||||
{
|
||||
TryGetValue(registration, out var current);
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
internal RegisterOptions Register(Type registerType, string name, ObjectFactoryBase factory)
|
||||
=> AddUpdateRegistration(new DependencyContainer.TypeRegistration(registerType, name), factory);
|
||||
|
||||
internal RegisterOptions AddUpdateRegistration(DependencyContainer.TypeRegistration typeRegistration, ObjectFactoryBase factory)
|
||||
{
|
||||
this[typeRegistration] = factory;
|
||||
|
||||
return new RegisterOptions(this, typeRegistration);
|
||||
}
|
||||
|
||||
internal bool RemoveRegistration(DependencyContainer.TypeRegistration typeRegistration)
|
||||
=> TryRemove(typeRegistration, out _);
|
||||
|
||||
internal object ResolveInternal(
|
||||
DependencyContainer.TypeRegistration registration,
|
||||
DependencyContainerResolveOptions options = null)
|
||||
{
|
||||
if (options == null)
|
||||
options = DependencyContainerResolveOptions.Default;
|
||||
|
||||
// Attempt container resolution
|
||||
if (TryGetValue(registration, out var factory))
|
||||
{
|
||||
try
|
||||
{
|
||||
return factory.GetObject(registration.Type, _dependencyContainer, options);
|
||||
}
|
||||
catch (DependencyContainerResolutionException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new DependencyContainerResolutionException(registration.Type, ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to get a factory from parent if we can
|
||||
var bubbledObjectFactory = GetParentObjectFactory(registration);
|
||||
if (bubbledObjectFactory != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return bubbledObjectFactory.GetObject(registration.Type, _dependencyContainer, options);
|
||||
}
|
||||
catch (DependencyContainerResolutionException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new DependencyContainerResolutionException(registration.Type, ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Fail if requesting named resolution and settings set to fail if unresolved
|
||||
if (!string.IsNullOrEmpty(registration.Name) && options.NamedResolutionFailureAction ==
|
||||
DependencyContainerNamedResolutionFailureActions.Fail)
|
||||
throw new DependencyContainerResolutionException(registration.Type);
|
||||
|
||||
// Attempted unnamed fallback container resolution if relevant and requested
|
||||
if (!string.IsNullOrEmpty(registration.Name) && options.NamedResolutionFailureAction ==
|
||||
DependencyContainerNamedResolutionFailureActions.AttemptUnnamedResolution)
|
||||
{
|
||||
if (TryGetValue(new DependencyContainer.TypeRegistration(registration.Type, string.Empty), out factory))
|
||||
{
|
||||
try
|
||||
{
|
||||
return factory.GetObject(registration.Type, _dependencyContainer, options);
|
||||
}
|
||||
catch (DependencyContainerResolutionException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new DependencyContainerResolutionException(registration.Type, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt unregistered construction if possible and requested
|
||||
var isValid = (options.UnregisteredResolutionAction ==
|
||||
DependencyContainerUnregisteredResolutionActions.AttemptResolve) ||
|
||||
(registration.Type.IsGenericType() && options.UnregisteredResolutionAction ==
|
||||
DependencyContainerUnregisteredResolutionActions.GenericsOnly);
|
||||
|
||||
return isValid && !registration.Type.IsAbstract() && !registration.Type.IsInterface()
|
||||
? ConstructType(registration.Type, null, options)
|
||||
: throw new DependencyContainerResolutionException(registration.Type);
|
||||
}
|
||||
|
||||
internal bool CanResolve(
|
||||
DependencyContainer.TypeRegistration registration,
|
||||
DependencyContainerResolveOptions options = null)
|
||||
{
|
||||
if (options == null)
|
||||
options = DependencyContainerResolveOptions.Default;
|
||||
|
||||
var checkType = registration.Type;
|
||||
var name = registration.Name;
|
||||
|
||||
if (TryGetValue(new DependencyContainer.TypeRegistration(checkType, name), out var factory))
|
||||
{
|
||||
if (factory.AssumeConstruction)
|
||||
return true;
|
||||
|
||||
if (factory.Constructor == null)
|
||||
return GetBestConstructor(factory.CreatesType, options) != null;
|
||||
|
||||
return CanConstruct(factory.Constructor, options);
|
||||
}
|
||||
|
||||
// Fail if requesting named resolution and settings set to fail if unresolved
|
||||
// Or bubble up if we have a parent
|
||||
if (!string.IsNullOrEmpty(name) && options.NamedResolutionFailureAction ==
|
||||
DependencyContainerNamedResolutionFailureActions.Fail)
|
||||
return _dependencyContainer.Parent?.RegisteredTypes.CanResolve(registration, options.Clone()) ?? false;
|
||||
|
||||
// Attempted unnamed fallback container resolution if relevant and requested
|
||||
if (!string.IsNullOrEmpty(name) && options.NamedResolutionFailureAction ==
|
||||
DependencyContainerNamedResolutionFailureActions.AttemptUnnamedResolution)
|
||||
{
|
||||
if (TryGetValue(new DependencyContainer.TypeRegistration(checkType), out factory))
|
||||
{
|
||||
if (factory.AssumeConstruction)
|
||||
return true;
|
||||
|
||||
return GetBestConstructor(factory.CreatesType, options) != null;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if type is an automatic lazy factory request or an IEnumerable<ResolveType>
|
||||
if (IsAutomaticLazyFactoryRequest(checkType) || registration.Type.IsIEnumerable())
|
||||
return true;
|
||||
|
||||
// Attempt unregistered construction if possible and requested
|
||||
// If we cant', bubble if we have a parent
|
||||
if ((options.UnregisteredResolutionAction ==
|
||||
DependencyContainerUnregisteredResolutionActions.AttemptResolve) ||
|
||||
(checkType.IsGenericType() && options.UnregisteredResolutionAction ==
|
||||
DependencyContainerUnregisteredResolutionActions.GenericsOnly))
|
||||
{
|
||||
return (GetBestConstructor(checkType, options) != null) ||
|
||||
(_dependencyContainer.Parent?.RegisteredTypes.CanResolve(registration, options.Clone()) ?? false);
|
||||
}
|
||||
|
||||
// Bubble resolution up the container tree if we have a parent
|
||||
return _dependencyContainer.Parent != null && _dependencyContainer.Parent.RegisteredTypes.CanResolve(registration, options.Clone());
|
||||
}
|
||||
|
||||
internal object ConstructType(
|
||||
Type implementationType,
|
||||
ConstructorInfo constructor,
|
||||
DependencyContainerResolveOptions options = null)
|
||||
{
|
||||
var typeToConstruct = implementationType;
|
||||
|
||||
if (constructor == null)
|
||||
{
|
||||
// Try and get the best constructor that we can construct
|
||||
// if we can't construct any then get the constructor
|
||||
// with the least number of parameters so we can throw a meaningful
|
||||
// resolve exception
|
||||
constructor = GetBestConstructor(typeToConstruct, options) ??
|
||||
GetTypeConstructors(typeToConstruct).LastOrDefault();
|
||||
}
|
||||
|
||||
if (constructor == null)
|
||||
throw new DependencyContainerResolutionException(typeToConstruct);
|
||||
|
||||
var ctorParams = constructor.GetParameters();
|
||||
var args = new object[ctorParams.Length];
|
||||
|
||||
for (var parameterIndex = 0; parameterIndex < ctorParams.Length; parameterIndex++)
|
||||
{
|
||||
var currentParam = ctorParams[parameterIndex];
|
||||
|
||||
try
|
||||
{
|
||||
args[parameterIndex] = options?.ConstructorParameters.GetValueOrDefault(currentParam.Name, ResolveInternal(new DependencyContainer.TypeRegistration(currentParam.ParameterType), options.Clone()));
|
||||
}
|
||||
catch (DependencyContainerResolutionException ex)
|
||||
{
|
||||
// If a constructor parameter can't be resolved
|
||||
// it will throw, so wrap it and throw that this can't
|
||||
// be resolved.
|
||||
throw new DependencyContainerResolutionException(typeToConstruct, ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new DependencyContainerResolutionException(typeToConstruct, ex);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return CreateObjectConstructionDelegateWithCache(constructor).Invoke(args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new DependencyContainerResolutionException(typeToConstruct, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static ObjectConstructor CreateObjectConstructionDelegateWithCache(ConstructorInfo constructor)
|
||||
{
|
||||
if (ObjectConstructorCache.TryGetValue(constructor, out var objectConstructor))
|
||||
return objectConstructor;
|
||||
|
||||
// We could lock the cache here, but there's no real side
|
||||
// effect to two threads creating the same ObjectConstructor
|
||||
// at the same time, compared to the cost of a lock for
|
||||
// every creation.
|
||||
var constructorParams = constructor.GetParameters();
|
||||
var lambdaParams = Expression.Parameter(typeof(object[]), "parameters");
|
||||
var newParams = new Expression[constructorParams.Length];
|
||||
|
||||
for (var i = 0; i < constructorParams.Length; i++)
|
||||
{
|
||||
var paramsParameter = Expression.ArrayIndex(lambdaParams, Expression.Constant(i));
|
||||
|
||||
newParams[i] = Expression.Convert(paramsParameter, constructorParams[i].ParameterType);
|
||||
}
|
||||
|
||||
var newExpression = Expression.New(constructor, newParams);
|
||||
|
||||
var constructionLambda = Expression.Lambda(typeof(ObjectConstructor), newExpression, lambdaParams);
|
||||
|
||||
objectConstructor = (ObjectConstructor)constructionLambda.Compile();
|
||||
|
||||
ObjectConstructorCache[constructor] = objectConstructor;
|
||||
return objectConstructor;
|
||||
}
|
||||
|
||||
private static IEnumerable<ConstructorInfo> GetTypeConstructors(Type type)
|
||||
=> type.GetConstructors().OrderByDescending(ctor => ctor.GetParameters().Length);
|
||||
|
||||
private static bool IsAutomaticLazyFactoryRequest(Type type)
|
||||
{
|
||||
if (!type.IsGenericType())
|
||||
return false;
|
||||
|
||||
var genericType = type.GetGenericTypeDefinition();
|
||||
|
||||
// Just a func
|
||||
if (genericType == typeof(Func<>))
|
||||
return true;
|
||||
|
||||
// 2 parameter func with string as first parameter (name)
|
||||
if (genericType == typeof(Func<,>) && type.GetGenericArguments()[0] == typeof(string))
|
||||
return true;
|
||||
|
||||
// 3 parameter func with string as first parameter (name) and IDictionary<string, object> as second (parameters)
|
||||
return genericType == typeof(Func<,,>) && type.GetGenericArguments()[0] == typeof(string) &&
|
||||
type.GetGenericArguments()[1] == typeof(IDictionary<string, object>);
|
||||
}
|
||||
|
||||
private ObjectFactoryBase GetParentObjectFactory(DependencyContainer.TypeRegistration registration)
|
||||
{
|
||||
if (_dependencyContainer.Parent == null)
|
||||
return null;
|
||||
|
||||
return _dependencyContainer.Parent.RegisteredTypes.TryGetValue(registration, out var factory)
|
||||
? factory.GetFactoryForChildContainer(registration.Type, _dependencyContainer.Parent, _dependencyContainer)
|
||||
: _dependencyContainer.Parent.RegisteredTypes.GetParentObjectFactory(registration);
|
||||
}
|
||||
|
||||
private ConstructorInfo GetBestConstructor(
|
||||
Type type,
|
||||
DependencyContainerResolveOptions options)
|
||||
=> type.IsValueType() ? null : GetTypeConstructors(type).FirstOrDefault(ctor => CanConstruct(ctor, options));
|
||||
|
||||
private bool CanConstruct(
|
||||
ConstructorInfo ctor,
|
||||
DependencyContainerResolveOptions options)
|
||||
{
|
||||
foreach (var parameter in ctor.GetParameters())
|
||||
{
|
||||
if (string.IsNullOrEmpty(parameter.Name))
|
||||
return false;
|
||||
|
||||
var isParameterOverload = options.ConstructorParameters.ContainsKey(parameter.Name);
|
||||
|
||||
if (parameter.ParameterType.IsPrimitive() && !isParameterOverload)
|
||||
return false;
|
||||
|
||||
if (!isParameterOverload &&
|
||||
!CanResolve(new DependencyContainer.TypeRegistration(parameter.ParameterType), options.Clone()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private IEnumerable<DependencyContainer.TypeRegistration> GetParentRegistrationsForType(Type resolveType)
|
||||
=> _dependencyContainer.Parent == null
|
||||
? new DependencyContainer.TypeRegistration[] { }
|
||||
: _dependencyContainer.Parent.RegisteredTypes.Keys.Where(tr => tr.Type == resolveType).Concat(_dependencyContainer.Parent.RegisteredTypes.GetParentRegistrationsForType(resolveType));
|
||||
}
|
||||
/// <param name="parameters">The parameters.</param>
|
||||
/// <returns>The built object.</returns>
|
||||
public delegate Object ObjectConstructor(params Object[] parameters);
|
||||
|
||||
internal IEnumerable<Object> Resolve(Type resolveType, Boolean includeUnnamed) {
|
||||
IEnumerable<DependencyContainer.TypeRegistration> registrations = this.Keys.Where(tr => tr.Type == resolveType)
|
||||
.Concat(this.GetParentRegistrationsForType(resolveType)).Distinct();
|
||||
|
||||
if(!includeUnnamed) {
|
||||
registrations = registrations.Where(tr => tr.Name != String.Empty);
|
||||
}
|
||||
|
||||
return registrations.Select(registration =>
|
||||
this.ResolveInternal(registration, DependencyContainerResolveOptions.Default));
|
||||
}
|
||||
|
||||
internal ObjectFactoryBase GetCurrentFactory(DependencyContainer.TypeRegistration registration) {
|
||||
_ = this.TryGetValue(registration, out ObjectFactoryBase current);
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
internal RegisterOptions Register(Type registerType, String name, ObjectFactoryBase factory)
|
||||
=> this.AddUpdateRegistration(new DependencyContainer.TypeRegistration(registerType, name), factory);
|
||||
|
||||
internal RegisterOptions AddUpdateRegistration(DependencyContainer.TypeRegistration typeRegistration, ObjectFactoryBase factory) {
|
||||
this[typeRegistration] = factory;
|
||||
|
||||
return new RegisterOptions(this, typeRegistration);
|
||||
}
|
||||
|
||||
internal Boolean RemoveRegistration(DependencyContainer.TypeRegistration typeRegistration)
|
||||
=> this.TryRemove(typeRegistration, out _);
|
||||
|
||||
internal Object ResolveInternal(
|
||||
DependencyContainer.TypeRegistration registration,
|
||||
DependencyContainerResolveOptions options = null) {
|
||||
if(options == null) {
|
||||
options = DependencyContainerResolveOptions.Default;
|
||||
}
|
||||
|
||||
// Attempt container resolution
|
||||
if(this.TryGetValue(registration, out ObjectFactoryBase factory)) {
|
||||
try {
|
||||
return factory.GetObject(registration.Type, this._dependencyContainer, options);
|
||||
} catch(DependencyContainerResolutionException) {
|
||||
throw;
|
||||
} catch(Exception ex) {
|
||||
throw new DependencyContainerResolutionException(registration.Type, ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to get a factory from parent if we can
|
||||
ObjectFactoryBase bubbledObjectFactory = this.GetParentObjectFactory(registration);
|
||||
if(bubbledObjectFactory != null) {
|
||||
try {
|
||||
return bubbledObjectFactory.GetObject(registration.Type, this._dependencyContainer, options);
|
||||
} catch(DependencyContainerResolutionException) {
|
||||
throw;
|
||||
} catch(Exception ex) {
|
||||
throw new DependencyContainerResolutionException(registration.Type, ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Fail if requesting named resolution and settings set to fail if unresolved
|
||||
if(!String.IsNullOrEmpty(registration.Name) && options.NamedResolutionFailureAction ==
|
||||
DependencyContainerNamedResolutionFailureActions.Fail) {
|
||||
throw new DependencyContainerResolutionException(registration.Type);
|
||||
}
|
||||
|
||||
// Attempted unnamed fallback container resolution if relevant and requested
|
||||
if(!String.IsNullOrEmpty(registration.Name) && options.NamedResolutionFailureAction ==
|
||||
DependencyContainerNamedResolutionFailureActions.AttemptUnnamedResolution) {
|
||||
if(this.TryGetValue(new DependencyContainer.TypeRegistration(registration.Type, String.Empty), out factory)) {
|
||||
try {
|
||||
return factory.GetObject(registration.Type, this._dependencyContainer, options);
|
||||
} catch(DependencyContainerResolutionException) {
|
||||
throw;
|
||||
} catch(Exception ex) {
|
||||
throw new DependencyContainerResolutionException(registration.Type, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt unregistered construction if possible and requested
|
||||
Boolean isValid = options.UnregisteredResolutionAction ==
|
||||
DependencyContainerUnregisteredResolutionActions.AttemptResolve ||
|
||||
registration.Type.IsGenericType() && options.UnregisteredResolutionAction ==
|
||||
DependencyContainerUnregisteredResolutionActions.GenericsOnly;
|
||||
|
||||
return isValid && !registration.Type.IsAbstract() && !registration.Type.IsInterface()
|
||||
? this.ConstructType(registration.Type, null, options)
|
||||
: throw new DependencyContainerResolutionException(registration.Type);
|
||||
}
|
||||
|
||||
internal Boolean CanResolve(
|
||||
DependencyContainer.TypeRegistration registration,
|
||||
DependencyContainerResolveOptions options = null) {
|
||||
if(options == null) {
|
||||
options = DependencyContainerResolveOptions.Default;
|
||||
}
|
||||
|
||||
Type checkType = registration.Type;
|
||||
String name = registration.Name;
|
||||
|
||||
if(this.TryGetValue(new DependencyContainer.TypeRegistration(checkType, name), out ObjectFactoryBase factory)) {
|
||||
return factory.AssumeConstruction
|
||||
? true
|
||||
: factory.Constructor == null
|
||||
? this.GetBestConstructor(factory.CreatesType, options) != null
|
||||
: this.CanConstruct(factory.Constructor, options);
|
||||
}
|
||||
|
||||
// Fail if requesting named resolution and settings set to fail if unresolved
|
||||
// Or bubble up if we have a parent
|
||||
if(!String.IsNullOrEmpty(name) && options.NamedResolutionFailureAction ==
|
||||
DependencyContainerNamedResolutionFailureActions.Fail) {
|
||||
return this._dependencyContainer.Parent?.RegisteredTypes.CanResolve(registration, options.Clone()) ?? false;
|
||||
}
|
||||
|
||||
// Attempted unnamed fallback container resolution if relevant and requested
|
||||
if(!String.IsNullOrEmpty(name) && options.NamedResolutionFailureAction ==
|
||||
DependencyContainerNamedResolutionFailureActions.AttemptUnnamedResolution) {
|
||||
if(this.TryGetValue(new DependencyContainer.TypeRegistration(checkType), out factory)) {
|
||||
return factory.AssumeConstruction ? true : this.GetBestConstructor(factory.CreatesType, options) != null;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if type is an automatic lazy factory request or an IEnumerable<ResolveType>
|
||||
if(IsAutomaticLazyFactoryRequest(checkType) || registration.Type.IsIEnumerable()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Attempt unregistered construction if possible and requested
|
||||
// If we cant', bubble if we have a parent
|
||||
if(options.UnregisteredResolutionAction ==
|
||||
DependencyContainerUnregisteredResolutionActions.AttemptResolve ||
|
||||
checkType.IsGenericType() && options.UnregisteredResolutionAction ==
|
||||
DependencyContainerUnregisteredResolutionActions.GenericsOnly) {
|
||||
return this.GetBestConstructor(checkType, options) != null ||
|
||||
(this._dependencyContainer.Parent?.RegisteredTypes.CanResolve(registration, options.Clone()) ?? false);
|
||||
}
|
||||
|
||||
// Bubble resolution up the container tree if we have a parent
|
||||
return this._dependencyContainer.Parent != null && this._dependencyContainer.Parent.RegisteredTypes.CanResolve(registration, options.Clone());
|
||||
}
|
||||
|
||||
internal Object ConstructType(
|
||||
Type implementationType,
|
||||
ConstructorInfo constructor,
|
||||
DependencyContainerResolveOptions options = null) {
|
||||
Type typeToConstruct = implementationType;
|
||||
|
||||
if(constructor == null) {
|
||||
// Try and get the best constructor that we can construct
|
||||
// if we can't construct any then get the constructor
|
||||
// with the least number of parameters so we can throw a meaningful
|
||||
// resolve exception
|
||||
constructor = this.GetBestConstructor(typeToConstruct, options) ??
|
||||
GetTypeConstructors(typeToConstruct).LastOrDefault();
|
||||
}
|
||||
|
||||
if(constructor == null) {
|
||||
throw new DependencyContainerResolutionException(typeToConstruct);
|
||||
}
|
||||
|
||||
ParameterInfo[] ctorParams = constructor.GetParameters();
|
||||
Object[] args = new Object[ctorParams.Length];
|
||||
|
||||
for(Int32 parameterIndex = 0; parameterIndex < ctorParams.Length; parameterIndex++) {
|
||||
ParameterInfo currentParam = ctorParams[parameterIndex];
|
||||
|
||||
try {
|
||||
args[parameterIndex] = options?.ConstructorParameters.GetValueOrDefault(currentParam.Name, this.ResolveInternal(new DependencyContainer.TypeRegistration(currentParam.ParameterType), options.Clone()));
|
||||
} catch(DependencyContainerResolutionException ex) {
|
||||
// If a constructor parameter can't be resolved
|
||||
// it will throw, so wrap it and throw that this can't
|
||||
// be resolved.
|
||||
throw new DependencyContainerResolutionException(typeToConstruct, ex);
|
||||
} catch(Exception ex) {
|
||||
throw new DependencyContainerResolutionException(typeToConstruct, ex);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return CreateObjectConstructionDelegateWithCache(constructor).Invoke(args);
|
||||
} catch(Exception ex) {
|
||||
throw new DependencyContainerResolutionException(typeToConstruct, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static ObjectConstructor CreateObjectConstructionDelegateWithCache(ConstructorInfo constructor) {
|
||||
if(ObjectConstructorCache.TryGetValue(constructor, out ObjectConstructor objectConstructor)) {
|
||||
return objectConstructor;
|
||||
}
|
||||
|
||||
// We could lock the cache here, but there's no real side
|
||||
// effect to two threads creating the same ObjectConstructor
|
||||
// at the same time, compared to the cost of a lock for
|
||||
// every creation.
|
||||
ParameterInfo[] constructorParams = constructor.GetParameters();
|
||||
ParameterExpression lambdaParams = Expression.Parameter(typeof(Object[]), "parameters");
|
||||
Expression[] newParams = new Expression[constructorParams.Length];
|
||||
|
||||
for(Int32 i = 0; i < constructorParams.Length; i++) {
|
||||
BinaryExpression paramsParameter = Expression.ArrayIndex(lambdaParams, Expression.Constant(i));
|
||||
|
||||
newParams[i] = Expression.Convert(paramsParameter, constructorParams[i].ParameterType);
|
||||
}
|
||||
|
||||
NewExpression newExpression = Expression.New(constructor, newParams);
|
||||
|
||||
LambdaExpression constructionLambda = Expression.Lambda(typeof(ObjectConstructor), newExpression, lambdaParams);
|
||||
|
||||
objectConstructor = (ObjectConstructor)constructionLambda.Compile();
|
||||
|
||||
ObjectConstructorCache[constructor] = objectConstructor;
|
||||
return objectConstructor;
|
||||
}
|
||||
|
||||
private static IEnumerable<ConstructorInfo> GetTypeConstructors(Type type)
|
||||
=> type.GetConstructors().OrderByDescending(ctor => ctor.GetParameters().Length);
|
||||
|
||||
private static Boolean IsAutomaticLazyFactoryRequest(Type type) {
|
||||
if(!type.IsGenericType()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Type genericType = type.GetGenericTypeDefinition();
|
||||
|
||||
// Just a func
|
||||
if(genericType == typeof(Func<>)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2 parameter func with string as first parameter (name)
|
||||
if(genericType == typeof(Func<,>) && type.GetGenericArguments()[0] == typeof(String)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3 parameter func with string as first parameter (name) and IDictionary<string, object> as second (parameters)
|
||||
return genericType == typeof(Func<,,>) && type.GetGenericArguments()[0] == typeof(String) &&
|
||||
type.GetGenericArguments()[1] == typeof(IDictionary<String, Object>);
|
||||
}
|
||||
|
||||
private ObjectFactoryBase GetParentObjectFactory(DependencyContainer.TypeRegistration registration) => this._dependencyContainer.Parent == null
|
||||
? null
|
||||
: this._dependencyContainer.Parent.RegisteredTypes.TryGetValue(registration, out ObjectFactoryBase factory)
|
||||
? factory.GetFactoryForChildContainer(registration.Type, this._dependencyContainer.Parent, this._dependencyContainer)
|
||||
: this._dependencyContainer.Parent.RegisteredTypes.GetParentObjectFactory(registration);
|
||||
|
||||
private ConstructorInfo GetBestConstructor(
|
||||
Type type,
|
||||
DependencyContainerResolveOptions options)
|
||||
=> type.IsValueType() ? null : GetTypeConstructors(type).FirstOrDefault(ctor => this.CanConstruct(ctor, options));
|
||||
|
||||
private Boolean CanConstruct(
|
||||
ConstructorInfo ctor,
|
||||
DependencyContainerResolveOptions options) {
|
||||
foreach(ParameterInfo parameter in ctor.GetParameters()) {
|
||||
if(String.IsNullOrEmpty(parameter.Name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Boolean isParameterOverload = options.ConstructorParameters.ContainsKey(parameter.Name);
|
||||
|
||||
if(parameter.ParameterType.IsPrimitive() && !isParameterOverload) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!isParameterOverload &&
|
||||
!this.CanResolve(new DependencyContainer.TypeRegistration(parameter.ParameterType), options.Clone())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private IEnumerable<DependencyContainer.TypeRegistration> GetParentRegistrationsForType(Type resolveType)
|
||||
=> this._dependencyContainer.Parent == null
|
||||
? new DependencyContainer.TypeRegistration[] { }
|
||||
: this._dependencyContainer.Parent.RegisteredTypes.Keys.Where(tr => tr.Type == resolveType).Concat(this._dependencyContainer.Parent.RegisteredTypes.GetParentRegistrationsForType(resolveType));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user