diff --git a/.gitignore b/.gitignore index 6ea4e1a..224ca86 100644 --- a/.gitignore +++ b/.gitignore @@ -215,3 +215,9 @@ pip-log.txt #Mr Developer .mr.developer.cfg + +############# +## Rider +############# + +.idea diff --git a/CHANGELOG.md b/CHANGELOG.md index 69781a4..d642a6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog for CodeProject.ObjectPool # +### v3.0.4 (2017-06-24) ### + +* Removed dependency on Thrower. + ### v3.0.3 (2017-04-08) ### * Added a timed object pool (issue #1). diff --git a/README.md b/README.md index 093001a..20ecfe4 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ## Summary ## -* Latest release version: `v3.0.3` +* Latest release version: `v3.0.4` * Build status on [AppVeyor](https://ci.appveyor.com): [![Build status](https://ci.appveyor.com/api/projects/status/r4qnqaqj9ri6cicn?svg=true)](https://ci.appveyor.com/project/pomma89/objectpool) * [Doxygen](http://www.stack.nl/~dimitri/doxygen/index.html) documentation: + [HTML](http://pomma89.altervista.org/objectpool/doc/html/index.html) diff --git a/appveyor.yml b/appveyor.yml index 5740c9b..510cd1d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,7 +9,7 @@ #---------------------------------# # version format -version: 3.0.3.{build} +version: 3.0.4.{build} # branches to build branches: @@ -25,7 +25,7 @@ branches: assembly_info: patch: true file: AssemblyInfo.* - assembly_version: "3.0.3.{build}" + assembly_version: "3.0.4.{build}" assembly_file_version: "{version}" assembly_informational_version: "{version}" diff --git a/build.cake b/build.cake index c2bee66..5be347d 100644 --- a/build.cake +++ b/build.cake @@ -37,32 +37,32 @@ Task("Build-Debug") Build("Debug"); }); -Task("Test-Debug") +Task("Build-Release") .IsDependentOn("Build-Debug") - .Does(() => + .Does(() => { - Test("Debug"); + Build("Release"); }); -Task("Build-Release") - .IsDependentOn("Test-Debug") +Task("Pack-Release") + .IsDependentOn("Build-Release") .Does(() => { - Build("Release"); + Pack("Release"); }); -Task("Test-Release") - .IsDependentOn("Build-Release") +Task("Test-Debug") + .IsDependentOn("Pack-Release") .Does(() => { - Test("Release"); + Test("Debug"); }); -Task("Pack-Release") - .IsDependentOn("Test-Release") - .Does(() => +Task("Test-Release") + .IsDependentOn("Test-Debug") + .Does(() => { - Pack("Release"); + Test("Release"); }); ////////////////////////////////////////////////////////////////////// @@ -70,7 +70,7 @@ Task("Pack-Release") ////////////////////////////////////////////////////////////////////// Task("Default") - .IsDependentOn("Pack-Release"); + .IsDependentOn("Test-Release"); ////////////////////////////////////////////////////////////////////// // EXECUTION @@ -107,10 +107,10 @@ private void Test(string cfg) // NoResults = true //}); - const string flags = "--noheader --noresult"; + const string flags = "--noheader --noresult --stoponerror"; const string errMsg = " - Unit test failure - "; - Parallel.ForEach(GetFiles("./test/**/bin/{cfg}/*/*.UnitTests.exe".Replace("{cfg}", cfg)), netExe => + Parallel.ForEach(GetFiles("./test/*.UnitTests/**/bin/{cfg}/*/*.UnitTests.exe".Replace("{cfg}", cfg)), netExe => { if (StartProcess(netExe, flags) != 0) { @@ -118,7 +118,7 @@ private void Test(string cfg) } }); - Parallel.ForEach(GetFiles("./test/**/bin/{cfg}/*/*.UnitTests.dll".Replace("{cfg}", cfg)), netCoreDll => + Parallel.ForEach(GetFiles("./test/*.UnitTests/**/bin/{cfg}/*/*.UnitTests.dll".Replace("{cfg}", cfg)), netCoreDll => { DotNetCoreExecute(netCoreDll, flags); }); diff --git a/src/CodeProject.ObjectPool/App_Packages/LibLog.4.2/LibLog.cs b/src/CodeProject.ObjectPool/App_Packages/LibLog.4.2/LibLog.cs new file mode 100644 index 0000000..4d8384a --- /dev/null +++ b/src/CodeProject.ObjectPool/App_Packages/LibLog.4.2/LibLog.cs @@ -0,0 +1,2353 @@ +//=============================================================================== +// LibLog +// +// https://github.com/damianh/LibLog +//=============================================================================== +// Copyright © 2011-2015 Damian Hickey. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//=============================================================================== + +// ReSharper disable PossibleNullReferenceException + +// Define LIBLOG_PORTABLE conditional compilation symbol for PCL compatibility +// +// Define LIBLOG_PUBLIC to enable ability to GET a logger (LogProvider.For<>() etc) from outside this +// library. NOTE: this can have unintended consequences of consumers of your library using your +// library to resolve a logger. If the reason is because you want to open this functionality to other +// projects within your solution, consider [InternalsVisibleTo] instead. +// +// Define LIBLOG_PROVIDERS_ONLY if your library provides its own logging API and you just want to use +// the LibLog providers internally to provide built in support for popular logging frameworks. + +#if !NET35 + +#pragma warning disable 1591 + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "CodeProject.ObjectPool.Logging")] +[assembly: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "CodeProject.ObjectPool.Logging.Logger.#Invoke(CodeProject.ObjectPool.Logging.LogLevel,System.Func`1,System.Exception,System.Object[])")] + +// If you copied this file manually, you need to change all "YourRootNameSpace" so not to clash with +// other libraries that use LibLog +#if LIBLOG_PROVIDERS_ONLY +namespace CodeProject.ObjectPool.LibLog +#else + +namespace CodeProject.ObjectPool.Logging +#endif +{ + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + +#if LIBLOG_PROVIDERS_ONLY + using CodeProject.ObjectPool.LibLog.LogProviders; +#else + + using CodeProject.ObjectPool.Logging.LogProviders; + +#endif + + using System; + +#if !LIBLOG_PROVIDERS_ONLY + + using System.Diagnostics; + +#if !LIBLOG_PORTABLE + using System.Runtime.CompilerServices; +#endif +#endif + +#if LIBLOG_PROVIDERS_ONLY + internal +#else + + public +#endif + delegate bool Logger(LogLevel logLevel, Func messageFunc, Exception exception = null, params object[] formatParameters); + +#if !LIBLOG_PROVIDERS_ONLY + /// + /// Simple interface that represent a logger. + /// +#if LIBLOG_PUBLIC + public +#else + + internal +#endif + interface ILog + { + /// + /// Log a message the specified log level. + /// + /// The log level. + /// The message function. + /// An optional exception. + /// + /// Optional format parameters for the message generated by the messagefunc. + /// + /// true if the message was logged. Otherwise false. + /// + /// Note to implementers: the message func should not be called if the loglevel is not + /// enabled so as not to incur performance penalties. + /// + /// To check IsEnabled call Log with only LogLevel and check the return value, no event + /// will be written. + /// + bool Log(LogLevel logLevel, Func messageFunc, Exception exception = null, params object[] formatParameters); + } + +#endif + + /// + /// The log level. + /// +#if LIBLOG_PROVIDERS_ONLY + internal +#else + + public +#endif + enum LogLevel + { + Trace, + Debug, + Info, + Warn, + Error, + Fatal + } + +#if !LIBLOG_PROVIDERS_ONLY +#if LIBLOG_PUBLIC + public +#else + + internal +#endif + static partial class LogExtensions + { + public static bool IsDebugEnabled(this ILog logger) + { + GuardAgainstNullLogger(logger); + return logger.Log(LogLevel.Debug, null); + } + + public static bool IsErrorEnabled(this ILog logger) + { + GuardAgainstNullLogger(logger); + return logger.Log(LogLevel.Error, null); + } + + public static bool IsFatalEnabled(this ILog logger) + { + GuardAgainstNullLogger(logger); + return logger.Log(LogLevel.Fatal, null); + } + + public static bool IsInfoEnabled(this ILog logger) + { + GuardAgainstNullLogger(logger); + return logger.Log(LogLevel.Info, null); + } + + public static bool IsTraceEnabled(this ILog logger) + { + GuardAgainstNullLogger(logger); + return logger.Log(LogLevel.Trace, null); + } + + public static bool IsWarnEnabled(this ILog logger) + { + GuardAgainstNullLogger(logger); + return logger.Log(LogLevel.Warn, null); + } + + public static void Debug(this ILog logger, Func messageFunc) + { + GuardAgainstNullLogger(logger); + logger.Log(LogLevel.Debug, messageFunc); + } + + public static void Debug(this ILog logger, string message) + { + if (logger.IsDebugEnabled()) + { + logger.Log(LogLevel.Debug, message.AsFunc()); + } + } + + public static void DebugFormat(this ILog logger, string message, params object[] args) + { + if (logger.IsDebugEnabled()) + { + logger.LogFormat(LogLevel.Debug, message, args); + } + } + + public static void DebugException(this ILog logger, string message, Exception exception) + { + if (logger.IsDebugEnabled()) + { + logger.Log(LogLevel.Debug, message.AsFunc(), exception); + } + } + + public static void DebugException(this ILog logger, string message, Exception exception, params object[] formatParams) + { + if (logger.IsDebugEnabled()) + { + logger.Log(LogLevel.Debug, message.AsFunc(), exception, formatParams); + } + } + + public static void Error(this ILog logger, Func messageFunc) + { + GuardAgainstNullLogger(logger); + logger.Log(LogLevel.Error, messageFunc); + } + + public static void Error(this ILog logger, string message) + { + if (logger.IsErrorEnabled()) + { + logger.Log(LogLevel.Error, message.AsFunc()); + } + } + + public static void ErrorFormat(this ILog logger, string message, params object[] args) + { + if (logger.IsErrorEnabled()) + { + logger.LogFormat(LogLevel.Error, message, args); + } + } + + public static void ErrorException(this ILog logger, string message, Exception exception, params object[] formatParams) + { + if (logger.IsErrorEnabled()) + { + logger.Log(LogLevel.Error, message.AsFunc(), exception, formatParams); + } + } + + public static void Fatal(this ILog logger, Func messageFunc) + { + logger.Log(LogLevel.Fatal, messageFunc); + } + + public static void Fatal(this ILog logger, string message) + { + if (logger.IsFatalEnabled()) + { + logger.Log(LogLevel.Fatal, message.AsFunc()); + } + } + + public static void FatalFormat(this ILog logger, string message, params object[] args) + { + if (logger.IsFatalEnabled()) + { + logger.LogFormat(LogLevel.Fatal, message, args); + } + } + + public static void FatalException(this ILog logger, string message, Exception exception, params object[] formatParams) + { + if (logger.IsFatalEnabled()) + { + logger.Log(LogLevel.Fatal, message.AsFunc(), exception, formatParams); + } + } + + public static void Info(this ILog logger, Func messageFunc) + { + GuardAgainstNullLogger(logger); + logger.Log(LogLevel.Info, messageFunc); + } + + public static void Info(this ILog logger, string message) + { + if (logger.IsInfoEnabled()) + { + logger.Log(LogLevel.Info, message.AsFunc()); + } + } + + public static void InfoFormat(this ILog logger, string message, params object[] args) + { + if (logger.IsInfoEnabled()) + { + logger.LogFormat(LogLevel.Info, message, args); + } + } + + public static void InfoException(this ILog logger, string message, Exception exception, params object[] formatParams) + { + if (logger.IsInfoEnabled()) + { + logger.Log(LogLevel.Info, message.AsFunc(), exception, formatParams); + } + } + + public static void Trace(this ILog logger, Func messageFunc) + { + GuardAgainstNullLogger(logger); + logger.Log(LogLevel.Trace, messageFunc); + } + + public static void Trace(this ILog logger, string message) + { + if (logger.IsTraceEnabled()) + { + logger.Log(LogLevel.Trace, message.AsFunc()); + } + } + + public static void TraceFormat(this ILog logger, string message, params object[] args) + { + if (logger.IsTraceEnabled()) + { + logger.LogFormat(LogLevel.Trace, message, args); + } + } + + public static void TraceException(this ILog logger, string message, Exception exception, params object[] formatParams) + { + if (logger.IsTraceEnabled()) + { + logger.Log(LogLevel.Trace, message.AsFunc(), exception, formatParams); + } + } + + public static void Warn(this ILog logger, Func messageFunc) + { + GuardAgainstNullLogger(logger); + logger.Log(LogLevel.Warn, messageFunc); + } + + public static void Warn(this ILog logger, string message) + { + if (logger.IsWarnEnabled()) + { + logger.Log(LogLevel.Warn, message.AsFunc()); + } + } + + public static void WarnFormat(this ILog logger, string message, params object[] args) + { + if (logger.IsWarnEnabled()) + { + logger.LogFormat(LogLevel.Warn, message, args); + } + } + + public static void WarnException(this ILog logger, string message, Exception exception, params object[] formatParams) + { + if (logger.IsWarnEnabled()) + { + logger.Log(LogLevel.Warn, message.AsFunc(), exception, formatParams); + } + } + + // ReSharper disable once UnusedParameter.Local + private static void GuardAgainstNullLogger(ILog logger) + { + if (logger == null) + { + throw new ArgumentNullException("logger"); + } + } + + private static void LogFormat(this ILog logger, LogLevel logLevel, string message, params object[] args) + { + logger.Log(logLevel, message.AsFunc(), null, args); + } + + // Avoid the closure allocation, see https://gist.github.com/AArnott/d285feef75c18f6ecd2b + private static Func AsFunc(this T value) where T : class + { + return value.Return; + } + + private static T Return(this T value) + { + return value; + } + } + +#endif + + /// + /// Represents a way to get a + /// +#if LIBLOG_PROVIDERS_ONLY + internal +#else + + public +#endif + interface ILogProvider + { + /// + /// Gets the specified named logger. + /// + /// Name of the logger. + /// The logger reference. + Logger GetLogger(string name); + + /// + /// Opens a nested diagnostics context. Not supported in EntLib logging. + /// + /// The message to add to the diagnostics context. + /// A disposable that when disposed removes the message from the context. + IDisposable OpenNestedContext(string message); + + /// + /// Opens a mapped diagnostics context. Not supported in EntLib logging. + /// + /// A key. + /// A value. + /// A disposable that when disposed removes the map from the context. + IDisposable OpenMappedContext(string key, string value); + } + + /// + /// Provides a mechanism to create instances of objects. + /// +#if LIBLOG_PROVIDERS_ONLY + internal +#else + + public +#endif + static class LogProvider + { +#if !LIBLOG_PROVIDERS_ONLY + + private const string NullLogProvider = "Current Log Provider is not set. Call SetCurrentLogProvider " + + "with a non-null value first."; + + private static dynamic s_currentLogProvider; + private static Action s_onCurrentLogProviderSet; + + [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] + static LogProvider() + { + IsDisabled = false; + } + + /// + /// Sets the current log provider. + /// + /// The log provider. + public static void SetCurrentLogProvider(ILogProvider logProvider) + { + s_currentLogProvider = logProvider; + + RaiseOnCurrentLogProviderSet(); + } + + /// + /// Gets or sets a value indicating whether this is logging is disabled. + /// + /// true if logging is disabled; otherwise, false. + public static bool IsDisabled { get; set; } + + /// + /// Sets an action that is invoked when a consumer of your library has called + /// SetCurrentLogProvider. It is important that hook into this if you are using child + /// libraries (especially ilmerged ones) that are using LibLog (or other logging + /// abstraction) so you adapt and delegate to them. + /// + internal static Action OnCurrentLogProviderSet + { + set + { + s_onCurrentLogProviderSet = value; + RaiseOnCurrentLogProviderSet(); + } + } + + internal static ILogProvider CurrentLogProvider + { + get + { + return s_currentLogProvider; + } + } + + /// + /// Gets a logger for the specified type. + /// + /// The type whose name will be used for the logger. + /// An instance of +#if LIBLOG_PUBLIC + public +#else + + internal +#endif + static ILog For() + { + return GetLogger(typeof(T)); + } + +#if !LIBLOG_PORTABLE + /// + /// Gets a logger for the current class. + /// + /// An instance of + [MethodImpl(MethodImplOptions.NoInlining)] +#if LIBLOG_PUBLIC + public +#else + internal +#endif + static ILog GetCurrentClassLogger() + { + var stackFrame = new StackFrame(1, false); + return GetLogger(stackFrame.GetMethod().DeclaringType); + } +#endif + + /// + /// Gets a logger for the specified type. + /// + /// The type whose name will be used for the logger. + /// + /// If the type is null then this name will be used as the log name instead + /// + /// An instance of +#if LIBLOG_PUBLIC + public +#else + + internal +#endif + static ILog GetLogger(Type type, string fallbackTypeName = "System.Object") + { + // If the type passed in is null then fallback to the type name specified + return GetLogger(type != null ? type.FullName : fallbackTypeName); + } + + /// + /// Gets a logger with the specified name. + /// + /// The name. + /// An instance of +#if LIBLOG_PUBLIC + public +#else + + internal +#endif + static ILog GetLogger(string name) + { + ILogProvider logProvider = CurrentLogProvider ?? ResolveLogProvider(); + return logProvider == null + ? NoOpLogger.Instance + : (ILog) new LoggerExecutionWrapper(logProvider.GetLogger(name), () => IsDisabled); + } + + /// + /// Opens a nested diagnostics context. + /// + /// A message. + /// An that closes context when disposed. + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "SetCurrentLogProvider")] +#if LIBLOG_PUBLIC + public +#else + internal +#endif + static IDisposable OpenNestedContext(string message) + { + ILogProvider logProvider = CurrentLogProvider ?? ResolveLogProvider(); + + return logProvider == null + ? new DisposableAction(() => { }) + : logProvider.OpenNestedContext(message); + } + + /// + /// Opens a mapped diagnostics context. + /// + /// A key. + /// A value. + /// An that closes context when disposed. + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "SetCurrentLogProvider")] +#if LIBLOG_PUBLIC + public +#else + internal +#endif + static IDisposable OpenMappedContext(string key, string value) + { + ILogProvider logProvider = CurrentLogProvider ?? ResolveLogProvider(); + + return logProvider == null + ? new DisposableAction(() => { }) + : logProvider.OpenMappedContext(key, value); + } + +#endif + +#if LIBLOG_PROVIDERS_ONLY + private +#else + + internal +#endif + delegate bool IsLoggerAvailable(); + +#if LIBLOG_PROVIDERS_ONLY + private +#else + + internal +#endif + delegate ILogProvider CreateLogProvider(); + +#if LIBLOG_PROVIDERS_ONLY + private +#else + + internal +#endif + static readonly List> LogProviderResolvers = + new List> + { + new Tuple(SerilogLogProvider.IsLoggerAvailable, () => new SerilogLogProvider()), + new Tuple(NLogLogProvider.IsLoggerAvailable, () => new NLogLogProvider()), + new Tuple(Log4NetLogProvider.IsLoggerAvailable, () => new Log4NetLogProvider()), + new Tuple(EntLibLogProvider.IsLoggerAvailable, () => new EntLibLogProvider()), + new Tuple(LoupeLogProvider.IsLoggerAvailable, () => new LoupeLogProvider()), + }; + +#if !LIBLOG_PROVIDERS_ONLY + + private static void RaiseOnCurrentLogProviderSet() + { + if (s_onCurrentLogProviderSet != null) + { + s_onCurrentLogProviderSet(s_currentLogProvider); + } + } + +#endif + + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Console.WriteLine(System.String,System.Object,System.Object)")] + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + internal static ILogProvider ResolveLogProvider() + { + try + { + foreach (var providerResolver in LogProviderResolvers) + { + if (providerResolver.Item1()) + { + return providerResolver.Item2(); + } + } + } + catch (Exception ex) + { +#if LIBLOG_PORTABLE + Debug.WriteLine( +#else + Console.WriteLine( +#endif + "Exception occurred resolving a log provider. Logging for this assembly {0} is disabled. {1}", + typeof(LogProvider).GetAssemblyPortable().FullName, + ex); + } + return null; + } + +#if !LIBLOG_PROVIDERS_ONLY + + internal class NoOpLogger : ILog + { + internal static readonly NoOpLogger Instance = new NoOpLogger(); + + public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) + { + return false; + } + } + +#endif + } + +#if !LIBLOG_PROVIDERS_ONLY + + internal class LoggerExecutionWrapper : ILog + { + private readonly Logger _logger; + private readonly Func _getIsDisabled; + internal const string FailedToGenerateLogMessage = "Failed to generate log message"; + + internal LoggerExecutionWrapper(Logger logger, Func getIsDisabled = null) + { + _logger = logger; + _getIsDisabled = getIsDisabled ?? (() => false); + } + + internal Logger WrappedLogger + { + get { return _logger; } + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public bool Log(LogLevel logLevel, Func messageFunc, Exception exception = null, params object[] formatParameters) + { + if (_getIsDisabled()) + { + return false; + } + if (messageFunc == null) + { + return _logger(logLevel, null); + } + + Func wrappedMessageFunc = () => + { + try + { + return messageFunc(); + } + catch (Exception ex) + { + Log(LogLevel.Error, () => FailedToGenerateLogMessage, ex); + } + return null; + }; + return _logger(logLevel, wrappedMessageFunc, exception, formatParameters); + } + } + +#endif +} + +#if LIBLOG_PROVIDERS_ONLY +namespace CodeProject.ObjectPool.LibLog.LogProviders +#else + +namespace CodeProject.ObjectPool.Logging.LogProviders +#endif +{ + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + +#if !LIBLOG_PORTABLE + using System.Diagnostics; +#endif + + using System.Globalization; + using System.Linq; + using System.Linq.Expressions; + using System.Reflection; + +#if !LIBLOG_PORTABLE + using System.Text; +#endif + + using System.Text.RegularExpressions; + + internal abstract class LogProviderBase : ILogProvider + { + protected delegate IDisposable OpenNdc(string message); + + protected delegate IDisposable OpenMdc(string key, string value); + + private readonly Lazy _lazyOpenNdcMethod; + private readonly Lazy _lazyOpenMdcMethod; + private static readonly IDisposable NoopDisposableInstance = new DisposableAction(); + + protected LogProviderBase() + { + _lazyOpenNdcMethod + = new Lazy(GetOpenNdcMethod); + _lazyOpenMdcMethod + = new Lazy(GetOpenMdcMethod); + } + + public abstract Logger GetLogger(string name); + + public IDisposable OpenNestedContext(string message) + { + return _lazyOpenNdcMethod.Value(message); + } + + public IDisposable OpenMappedContext(string key, string value) + { + return _lazyOpenMdcMethod.Value(key, value); + } + + protected virtual OpenNdc GetOpenNdcMethod() + { + return _ => NoopDisposableInstance; + } + + protected virtual OpenMdc GetOpenMdcMethod() + { + return (_, __) => NoopDisposableInstance; + } + } + + internal class NLogLogProvider : LogProviderBase + { + private readonly Func _getLoggerByNameDelegate; + private static bool s_providerIsAvailableOverride = true; + + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "LogManager")] + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "NLog")] + public NLogLogProvider() + { + if (!IsLoggerAvailable()) + { + throw new InvalidOperationException("NLog.LogManager not found"); + } + _getLoggerByNameDelegate = GetGetLoggerMethodCall(); + } + + public static bool ProviderIsAvailableOverride + { + get { return s_providerIsAvailableOverride; } + set { s_providerIsAvailableOverride = value; } + } + + public override Logger GetLogger(string name) + { + return new NLogLogger(_getLoggerByNameDelegate(name)).Log; + } + + public static bool IsLoggerAvailable() + { + return ProviderIsAvailableOverride && GetLogManagerType() != null; + } + + protected override OpenNdc GetOpenNdcMethod() + { + Type ndcContextType = Type.GetType("NLog.NestedDiagnosticsContext, NLog"); + MethodInfo pushMethod = ndcContextType.GetMethodPortable("Push", typeof(string)); + ParameterExpression messageParam = Expression.Parameter(typeof(string), "message"); + MethodCallExpression pushMethodCall = Expression.Call(null, pushMethod, messageParam); + return Expression.Lambda(pushMethodCall, messageParam).Compile(); + } + + protected override OpenMdc GetOpenMdcMethod() + { + Type mdcContextType = Type.GetType("NLog.MappedDiagnosticsContext, NLog"); + + MethodInfo setMethod = mdcContextType.GetMethodPortable("Set", typeof(string), typeof(string)); + MethodInfo removeMethod = mdcContextType.GetMethodPortable("Remove", typeof(string)); + ParameterExpression keyParam = Expression.Parameter(typeof(string), "key"); + ParameterExpression valueParam = Expression.Parameter(typeof(string), "value"); + + MethodCallExpression setMethodCall = Expression.Call(null, setMethod, keyParam, valueParam); + MethodCallExpression removeMethodCall = Expression.Call(null, removeMethod, keyParam); + + Action set = Expression + .Lambda>(setMethodCall, keyParam, valueParam) + .Compile(); + Action remove = Expression + .Lambda>(removeMethodCall, keyParam) + .Compile(); + + return (key, value) => + { + set(key, value); + return new DisposableAction(() => remove(key)); + }; + } + + private static Type GetLogManagerType() + { + return Type.GetType("NLog.LogManager, NLog"); + } + + private static Func GetGetLoggerMethodCall() + { + Type logManagerType = GetLogManagerType(); + MethodInfo method = logManagerType.GetMethodPortable("GetLogger", typeof(string)); + ParameterExpression nameParam = Expression.Parameter(typeof(string), "name"); + MethodCallExpression methodCall = Expression.Call(null, method, nameParam); + return Expression.Lambda>(methodCall, nameParam).Compile(); + } + + internal class NLogLogger + { + private readonly dynamic _logger; + + private static Func _logEventInfoFact; + + private static readonly object _levelTrace; + private static readonly object _levelDebug; + private static readonly object _levelInfo; + private static readonly object _levelWarn; + private static readonly object _levelError; + private static readonly object _levelFatal; + + static NLogLogger() + { + try + { + var logEventLevelType = Type.GetType("NLog.LogLevel, NLog"); + if (logEventLevelType == null) + { + throw new InvalidOperationException("Type NLog.LogLevel was not found."); + } + + var levelFields = logEventLevelType.GetFieldsPortable().ToList(); + _levelTrace = levelFields.First(x => x.Name == "Trace").GetValue(null); + _levelDebug = levelFields.First(x => x.Name == "Debug").GetValue(null); + _levelInfo = levelFields.First(x => x.Name == "Info").GetValue(null); + _levelWarn = levelFields.First(x => x.Name == "Warn").GetValue(null); + _levelError = levelFields.First(x => x.Name == "Error").GetValue(null); + _levelFatal = levelFields.First(x => x.Name == "Fatal").GetValue(null); + + var logEventInfoType = Type.GetType("NLog.LogEventInfo, NLog"); + if (logEventInfoType == null) + { + throw new InvalidOperationException("Type NLog.LogEventInfo was not found."); + } + MethodInfo createLogEventInfoMethodInfo = logEventInfoType.GetMethodPortable("Create", + logEventLevelType, typeof(string), typeof(Exception), typeof(IFormatProvider), typeof(string), typeof(object[])); + ParameterExpression loggerNameParam = Expression.Parameter(typeof(string)); + ParameterExpression levelParam = Expression.Parameter(typeof(object)); + ParameterExpression messageParam = Expression.Parameter(typeof(string)); + ParameterExpression exceptionParam = Expression.Parameter(typeof(Exception)); + UnaryExpression levelCast = Expression.Convert(levelParam, logEventLevelType); + MethodCallExpression createLogEventInfoMethodCall = Expression.Call(null, + createLogEventInfoMethodInfo, + levelCast, loggerNameParam, exceptionParam, + Expression.Constant(null, typeof(IFormatProvider)), messageParam, Expression.Constant(null, typeof(object[]))); + _logEventInfoFact = Expression.Lambda>(createLogEventInfoMethodCall, + loggerNameParam, levelParam, messageParam, exceptionParam).Compile(); + } + catch { } + } + + internal NLogLogger(dynamic logger) + { + _logger = logger; + } + + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) + { + if (messageFunc == null) + { + return IsLogLevelEnable(logLevel); + } + messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters); + + if (_logEventInfoFact != null) + { + if (IsLogLevelEnable(logLevel)) + { + var nlogLevel = this.TranslateLevel(logLevel); + Type s_callerStackBoundaryType; +#if !LIBLOG_PORTABLE + StackTrace stack = new StackTrace(); + Type thisType = GetType(); + Type knownType0 = typeof(LoggerExecutionWrapper); + Type knownType1 = typeof(LogExtensions); + //Maybe inline, so we may can't found any LibLog classes in stack + s_callerStackBoundaryType = null; + for (var i = 0; i < stack.FrameCount; i++) + { + var declaringType = stack.GetFrame(i).GetMethod().DeclaringType; + if (!IsInTypeHierarchy(thisType, declaringType) && + !IsInTypeHierarchy(knownType0, declaringType) && + !IsInTypeHierarchy(knownType1, declaringType)) + { + if (i > 1) + s_callerStackBoundaryType = stack.GetFrame(i - 1).GetMethod().DeclaringType; + break; + } + } +#else + s_callerStackBoundaryType = null; +#endif + if (s_callerStackBoundaryType != null) + _logger.Log(s_callerStackBoundaryType, _logEventInfoFact(_logger.Name, nlogLevel, messageFunc(), exception)); + else + _logger.Log(_logEventInfoFact(_logger.Name, nlogLevel, messageFunc(), exception)); + return true; + } + return false; + } + + if (exception != null) + { + return LogException(logLevel, messageFunc, exception); + } + switch (logLevel) + { + case LogLevel.Debug: + if (_logger.IsDebugEnabled) + { + _logger.Debug(messageFunc()); + return true; + } + break; + + case LogLevel.Info: + if (_logger.IsInfoEnabled) + { + _logger.Info(messageFunc()); + return true; + } + break; + + case LogLevel.Warn: + if (_logger.IsWarnEnabled) + { + _logger.Warn(messageFunc()); + return true; + } + break; + + case LogLevel.Error: + if (_logger.IsErrorEnabled) + { + _logger.Error(messageFunc()); + return true; + } + break; + + case LogLevel.Fatal: + if (_logger.IsFatalEnabled) + { + _logger.Fatal(messageFunc()); + return true; + } + break; + + default: + if (_logger.IsTraceEnabled) + { + _logger.Trace(messageFunc()); + return true; + } + break; + } + return false; + } + + private static bool IsInTypeHierarchy(Type currentType, Type checkType) + { + while (currentType != null && currentType != typeof(object)) + { + if (currentType == checkType) + { + return true; + } + currentType = currentType.GetBaseTypePortable(); + } + return false; + } + + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + private bool LogException(LogLevel logLevel, Func messageFunc, Exception exception) + { + switch (logLevel) + { + case LogLevel.Debug: + if (_logger.IsDebugEnabled) + { + _logger.DebugException(messageFunc(), exception); + return true; + } + break; + + case LogLevel.Info: + if (_logger.IsInfoEnabled) + { + _logger.InfoException(messageFunc(), exception); + return true; + } + break; + + case LogLevel.Warn: + if (_logger.IsWarnEnabled) + { + _logger.WarnException(messageFunc(), exception); + return true; + } + break; + + case LogLevel.Error: + if (_logger.IsErrorEnabled) + { + _logger.ErrorException(messageFunc(), exception); + return true; + } + break; + + case LogLevel.Fatal: + if (_logger.IsFatalEnabled) + { + _logger.FatalException(messageFunc(), exception); + return true; + } + break; + + default: + if (_logger.IsTraceEnabled) + { + _logger.TraceException(messageFunc(), exception); + return true; + } + break; + } + return false; + } + + private bool IsLogLevelEnable(LogLevel logLevel) + { + switch (logLevel) + { + case LogLevel.Debug: + return _logger.IsDebugEnabled; + + case LogLevel.Info: + return _logger.IsInfoEnabled; + + case LogLevel.Warn: + return _logger.IsWarnEnabled; + + case LogLevel.Error: + return _logger.IsErrorEnabled; + + case LogLevel.Fatal: + return _logger.IsFatalEnabled; + + default: + return _logger.IsTraceEnabled; + } + } + + private object TranslateLevel(LogLevel logLevel) + { + switch (logLevel) + { + case LogLevel.Trace: + return _levelTrace; + + case LogLevel.Debug: + return _levelDebug; + + case LogLevel.Info: + return _levelInfo; + + case LogLevel.Warn: + return _levelWarn; + + case LogLevel.Error: + return _levelError; + + case LogLevel.Fatal: + return _levelFatal; + + default: + throw new ArgumentOutOfRangeException("logLevel", logLevel, null); + } + } + } + } + + internal class Log4NetLogProvider : LogProviderBase + { + private readonly Func _getLoggerByNameDelegate; + private static bool s_providerIsAvailableOverride = true; + + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "LogManager")] + public Log4NetLogProvider() + { + if (!IsLoggerAvailable()) + { + throw new InvalidOperationException("log4net.LogManager not found"); + } + _getLoggerByNameDelegate = GetGetLoggerMethodCall(); + } + + public static bool ProviderIsAvailableOverride + { + get { return s_providerIsAvailableOverride; } + set { s_providerIsAvailableOverride = value; } + } + + public override Logger GetLogger(string name) + { + return new Log4NetLogger(_getLoggerByNameDelegate(name)).Log; + } + + internal static bool IsLoggerAvailable() + { + return ProviderIsAvailableOverride && GetLogManagerType() != null; + } + + protected override OpenNdc GetOpenNdcMethod() + { + Type logicalThreadContextType = Type.GetType("log4net.LogicalThreadContext, log4net"); + PropertyInfo stacksProperty = logicalThreadContextType.GetPropertyPortable("Stacks"); + Type logicalThreadContextStacksType = stacksProperty.PropertyType; + PropertyInfo stacksIndexerProperty = logicalThreadContextStacksType.GetPropertyPortable("Item"); + Type stackType = stacksIndexerProperty.PropertyType; + MethodInfo pushMethod = stackType.GetMethodPortable("Push"); + + ParameterExpression messageParameter = + Expression.Parameter(typeof(string), "message"); + + // message => LogicalThreadContext.Stacks.Item["NDC"].Push(message); + MethodCallExpression callPushBody = + Expression.Call( + Expression.Property(Expression.Property(null, stacksProperty), + stacksIndexerProperty, + Expression.Constant("NDC")), + pushMethod, + messageParameter); + + OpenNdc result = + Expression.Lambda(callPushBody, messageParameter) + .Compile(); + + return result; + } + + protected override OpenMdc GetOpenMdcMethod() + { + Type logicalThreadContextType = Type.GetType("log4net.LogicalThreadContext, log4net"); + PropertyInfo propertiesProperty = logicalThreadContextType.GetPropertyPortable("Properties"); + Type logicalThreadContextPropertiesType = propertiesProperty.PropertyType; + PropertyInfo propertiesIndexerProperty = logicalThreadContextPropertiesType.GetPropertyPortable("Item"); + + MethodInfo removeMethod = logicalThreadContextPropertiesType.GetMethodPortable("Remove"); + + ParameterExpression keyParam = Expression.Parameter(typeof(string), "key"); + ParameterExpression valueParam = Expression.Parameter(typeof(string), "value"); + + MemberExpression propertiesExpression = Expression.Property(null, propertiesProperty); + + // (key, value) => LogicalThreadContext.Properties.Item[key] = value; + BinaryExpression setProperties = Expression.Assign(Expression.Property(propertiesExpression, propertiesIndexerProperty, keyParam), valueParam); + + // key => LogicalThreadContext.Properties.Remove(key); + MethodCallExpression removeMethodCall = Expression.Call(propertiesExpression, removeMethod, keyParam); + + Action set = Expression + .Lambda>(setProperties, keyParam, valueParam) + .Compile(); + + Action remove = Expression + .Lambda>(removeMethodCall, keyParam) + .Compile(); + + return (key, value) => + { + set(key, value); + return new DisposableAction(() => remove(key)); + }; + } + + private static Type GetLogManagerType() + { + return Type.GetType("log4net.LogManager, log4net"); + } + + private static Func GetGetLoggerMethodCall() + { + Type logManagerType = GetLogManagerType(); + MethodInfo method = logManagerType.GetMethodPortable("GetLogger", typeof(string)); + ParameterExpression nameParam = Expression.Parameter(typeof(string), "name"); + MethodCallExpression methodCall = Expression.Call(null, method, nameParam); + return Expression.Lambda>(methodCall, nameParam).Compile(); + } + + internal class Log4NetLogger + { + private readonly dynamic _logger; + private static Type s_callerStackBoundaryType; + private static readonly object CallerStackBoundaryTypeSync = new object(); + + private readonly object _levelDebug; + private readonly object _levelInfo; + private readonly object _levelWarn; + private readonly object _levelError; + private readonly object _levelFatal; + private readonly Func _isEnabledForDelegate; + private readonly Action _logDelegate; + private readonly Func _createLoggingEvent; + private Action _loggingEventPropertySetter; + + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "ILogger")] + internal Log4NetLogger(dynamic logger) + { + _logger = logger.Logger; + + var logEventLevelType = Type.GetType("log4net.Core.Level, log4net"); + if (logEventLevelType == null) + { + throw new InvalidOperationException("Type log4net.Core.Level was not found."); + } + + var levelFields = logEventLevelType.GetFieldsPortable().ToList(); + _levelDebug = levelFields.First(x => x.Name == "Debug").GetValue(null); + _levelInfo = levelFields.First(x => x.Name == "Info").GetValue(null); + _levelWarn = levelFields.First(x => x.Name == "Warn").GetValue(null); + _levelError = levelFields.First(x => x.Name == "Error").GetValue(null); + _levelFatal = levelFields.First(x => x.Name == "Fatal").GetValue(null); + + // Func isEnabledFor = (logger, level) => { return + // ((log4net.Core.ILogger)logger).IsEnabled(level); } + var loggerType = Type.GetType("log4net.Core.ILogger, log4net"); + if (loggerType == null) + { + throw new InvalidOperationException("Type log4net.Core.ILogger, was not found."); + } + ParameterExpression instanceParam = Expression.Parameter(typeof(object)); + UnaryExpression instanceCast = Expression.Convert(instanceParam, loggerType); + ParameterExpression levelParam = Expression.Parameter(typeof(object)); + UnaryExpression levelCast = Expression.Convert(levelParam, logEventLevelType); + _isEnabledForDelegate = GetIsEnabledFor(loggerType, logEventLevelType, instanceCast, levelCast, instanceParam, levelParam); + + Type loggingEventType = Type.GetType("log4net.Core.LoggingEvent, log4net"); + + _createLoggingEvent = GetCreateLoggingEvent(instanceParam, instanceCast, levelParam, levelCast, loggingEventType); + + _logDelegate = GetLogDelegate(loggerType, loggingEventType, instanceCast, instanceParam); + + _loggingEventPropertySetter = GetLoggingEventPropertySetter(loggingEventType); + } + + private static Action GetLogDelegate(Type loggerType, Type loggingEventType, UnaryExpression instanceCast, + ParameterExpression instanceParam) + { + //Action Log = + //(logger, callerStackBoundaryDeclaringType, level, message, exception) => { ((ILogger)logger).Log(new LoggingEvent(callerStackBoundaryDeclaringType, logger.Repository, logger.Name, level, message, exception)); } + MethodInfo writeExceptionMethodInfo = loggerType.GetMethodPortable("Log", + loggingEventType); + + ParameterExpression loggingEventParameter = + Expression.Parameter(typeof(object), "loggingEvent"); + + UnaryExpression loggingEventCasted = + Expression.Convert(loggingEventParameter, loggingEventType); + + var writeMethodExp = Expression.Call( + instanceCast, + writeExceptionMethodInfo, + loggingEventCasted); + + var logDelegate = Expression.Lambda>( + writeMethodExp, + instanceParam, + loggingEventParameter).Compile(); + + return logDelegate; + } + + private static Func GetCreateLoggingEvent(ParameterExpression instanceParam, UnaryExpression instanceCast, ParameterExpression levelParam, UnaryExpression levelCast, Type loggingEventType) + { + ParameterExpression callerStackBoundaryDeclaringTypeParam = Expression.Parameter(typeof(Type)); + ParameterExpression messageParam = Expression.Parameter(typeof(string)); + ParameterExpression exceptionParam = Expression.Parameter(typeof(Exception)); + + PropertyInfo repositoryProperty = loggingEventType.GetPropertyPortable("Repository"); + PropertyInfo levelProperty = loggingEventType.GetPropertyPortable("Level"); + + ConstructorInfo loggingEventConstructor = + loggingEventType.GetConstructorPortable(typeof(Type), repositoryProperty.PropertyType, typeof(string), levelProperty.PropertyType, typeof(object), typeof(Exception)); + + //Func Log = + //(logger, callerStackBoundaryDeclaringType, level, message, exception) => new LoggingEvent(callerStackBoundaryDeclaringType, ((ILogger)logger).Repository, ((ILogger)logger).Name, (Level)level, message, exception); } + NewExpression newLoggingEventExpression = + Expression.New(loggingEventConstructor, + callerStackBoundaryDeclaringTypeParam, + Expression.Property(instanceCast, "Repository"), + Expression.Property(instanceCast, "Name"), + levelCast, + messageParam, + exceptionParam); + + var createLoggingEvent = + Expression.Lambda>( + newLoggingEventExpression, + instanceParam, + callerStackBoundaryDeclaringTypeParam, + levelParam, + messageParam, + exceptionParam) + .Compile(); + + return createLoggingEvent; + } + + private static Func GetIsEnabledFor(Type loggerType, Type logEventLevelType, + UnaryExpression instanceCast, + UnaryExpression levelCast, + ParameterExpression instanceParam, + ParameterExpression levelParam) + { + MethodInfo isEnabledMethodInfo = loggerType.GetMethodPortable("IsEnabledFor", logEventLevelType); + MethodCallExpression isEnabledMethodCall = Expression.Call(instanceCast, isEnabledMethodInfo, levelCast); + + Func result = + Expression.Lambda>(isEnabledMethodCall, instanceParam, levelParam) + .Compile(); + + return result; + } + + private static Action GetLoggingEventPropertySetter(Type loggingEventType) + { + ParameterExpression loggingEventParameter = Expression.Parameter(typeof(object), "loggingEvent"); + ParameterExpression keyParameter = Expression.Parameter(typeof(string), "key"); + ParameterExpression valueParameter = Expression.Parameter(typeof(object), "value"); + + PropertyInfo propertiesProperty = loggingEventType.GetPropertyPortable("Properties"); + PropertyInfo item = propertiesProperty.PropertyType.GetPropertyPortable("Item"); + + // ((LoggingEvent)loggingEvent).Properties[key] = value; + var body = + Expression.Assign( + Expression.Property( + Expression.Property(Expression.Convert(loggingEventParameter, loggingEventType), + propertiesProperty), item, keyParameter), valueParameter); + + Action result = + Expression.Lambda> + (body, loggingEventParameter, keyParameter, + valueParameter) + .Compile(); + + return result; + } + + public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) + { + if (messageFunc == null) + { + return IsLogLevelEnable(logLevel); + } + + if (!IsLogLevelEnable(logLevel)) + { + return false; + } + + string message = messageFunc(); + + IEnumerable patternMatches; + + string formattedMessage = + LogMessageFormatter.FormatStructuredMessage(message, + formatParameters, + out patternMatches); + + // determine correct caller - this might change due to jit optimizations with method inlining + if (s_callerStackBoundaryType == null) + { + lock (CallerStackBoundaryTypeSync) + { +#if !LIBLOG_PORTABLE + StackTrace stack = new StackTrace(); + Type thisType = GetType(); + s_callerStackBoundaryType = Type.GetType("LoggerExecutionWrapper"); + for (var i = 1; i < stack.FrameCount; i++) + { + if (!IsInTypeHierarchy(thisType, stack.GetFrame(i).GetMethod().DeclaringType)) + { + s_callerStackBoundaryType = stack.GetFrame(i - 1).GetMethod().DeclaringType; + break; + } + } +#else + s_callerStackBoundaryType = typeof(LoggerExecutionWrapper); +#endif + } + } + + var translatedLevel = TranslateLevel(logLevel); + + object loggingEvent = _createLoggingEvent(_logger, s_callerStackBoundaryType, translatedLevel, formattedMessage, exception); + + PopulateProperties(loggingEvent, patternMatches, formatParameters); + + _logDelegate(_logger, loggingEvent); + + return true; + } + + private void PopulateProperties(object loggingEvent, IEnumerable patternMatches, object[] formatParameters) + { + IEnumerable> keyToValue = + patternMatches.Zip(formatParameters, + (key, value) => new KeyValuePair(key, value)); + + foreach (KeyValuePair keyValuePair in keyToValue) + { + _loggingEventPropertySetter(loggingEvent, keyValuePair.Key, keyValuePair.Value); + } + } + + private static bool IsInTypeHierarchy(Type currentType, Type checkType) + { + while (currentType != null && currentType != typeof(object)) + { + if (currentType == checkType) + { + return true; + } + currentType = currentType.GetBaseTypePortable(); + } + return false; + } + + private bool IsLogLevelEnable(LogLevel logLevel) + { + var level = TranslateLevel(logLevel); + return _isEnabledForDelegate(_logger, level); + } + + private object TranslateLevel(LogLevel logLevel) + { + switch (logLevel) + { + case LogLevel.Trace: + case LogLevel.Debug: + return _levelDebug; + + case LogLevel.Info: + return _levelInfo; + + case LogLevel.Warn: + return _levelWarn; + + case LogLevel.Error: + return _levelError; + + case LogLevel.Fatal: + return _levelFatal; + + default: + throw new ArgumentOutOfRangeException("logLevel", logLevel, null); + } + } + } + } + + internal class EntLibLogProvider : LogProviderBase + { + private const string TypeTemplate = "Microsoft.Practices.EnterpriseLibrary.Logging.{0}, Microsoft.Practices.EnterpriseLibrary.Logging"; + private static bool s_providerIsAvailableOverride = true; + private static readonly Type LogEntryType; + private static readonly Type LoggerType; + private static readonly Type TraceEventTypeType; + private static readonly Action WriteLogEntry; + private static readonly Func ShouldLogEntry; + + [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] + static EntLibLogProvider() + { + LogEntryType = Type.GetType(string.Format(CultureInfo.InvariantCulture, TypeTemplate, "LogEntry")); + LoggerType = Type.GetType(string.Format(CultureInfo.InvariantCulture, TypeTemplate, "Logger")); + TraceEventTypeType = TraceEventTypeValues.Type; + if (LogEntryType == null + || TraceEventTypeType == null + || LoggerType == null) + { + return; + } + WriteLogEntry = GetWriteLogEntry(); + ShouldLogEntry = GetShouldLogEntry(); + } + + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "EnterpriseLibrary")] + public EntLibLogProvider() + { + if (!IsLoggerAvailable()) + { + throw new InvalidOperationException("Microsoft.Practices.EnterpriseLibrary.Logging.Logger not found"); + } + } + + public static bool ProviderIsAvailableOverride + { + get { return s_providerIsAvailableOverride; } + set { s_providerIsAvailableOverride = value; } + } + + public override Logger GetLogger(string name) + { + return new EntLibLogger(name, WriteLogEntry, ShouldLogEntry).Log; + } + + internal static bool IsLoggerAvailable() + { + return ProviderIsAvailableOverride + && TraceEventTypeType != null + && LogEntryType != null; + } + + private static Action GetWriteLogEntry() + { + // new LogEntry(...) + var logNameParameter = Expression.Parameter(typeof(string), "logName"); + var messageParameter = Expression.Parameter(typeof(string), "message"); + var severityParameter = Expression.Parameter(typeof(int), "severity"); + + MemberInitExpression memberInit = GetWriteLogExpression( + messageParameter, + Expression.Convert(severityParameter, TraceEventTypeType), + logNameParameter); + + //Logger.Write(new LogEntry(....)); + MethodInfo writeLogEntryMethod = LoggerType.GetMethodPortable("Write", LogEntryType); + var writeLogEntryExpression = Expression.Call(writeLogEntryMethod, memberInit); + + return Expression.Lambda>( + writeLogEntryExpression, + logNameParameter, + messageParameter, + severityParameter).Compile(); + } + + private static Func GetShouldLogEntry() + { + // new LogEntry(...) + var logNameParameter = Expression.Parameter(typeof(string), "logName"); + var severityParameter = Expression.Parameter(typeof(int), "severity"); + + MemberInitExpression memberInit = GetWriteLogExpression( + Expression.Constant("***dummy***"), + Expression.Convert(severityParameter, TraceEventTypeType), + logNameParameter); + + //Logger.Write(new LogEntry(....)); + MethodInfo writeLogEntryMethod = LoggerType.GetMethodPortable("ShouldLog", LogEntryType); + var writeLogEntryExpression = Expression.Call(writeLogEntryMethod, memberInit); + + return Expression.Lambda>( + writeLogEntryExpression, + logNameParameter, + severityParameter).Compile(); + } + + private static MemberInitExpression GetWriteLogExpression(Expression message, + Expression severityParameter, ParameterExpression logNameParameter) + { + var entryType = LogEntryType; + MemberInitExpression memberInit = Expression.MemberInit(Expression.New(entryType), + Expression.Bind(entryType.GetPropertyPortable("Message"), message), + Expression.Bind(entryType.GetPropertyPortable("Severity"), severityParameter), + Expression.Bind( + entryType.GetPropertyPortable("TimeStamp"), + Expression.Property(null, typeof(DateTime).GetPropertyPortable("UtcNow"))), + Expression.Bind( + entryType.GetPropertyPortable("Categories"), + Expression.ListInit( + Expression.New(typeof(List)), + typeof(List).GetMethodPortable("Add", typeof(string)), + logNameParameter))); + return memberInit; + } + + internal class EntLibLogger + { + private readonly string _loggerName; + private readonly Action _writeLog; + private readonly Func _shouldLog; + + internal EntLibLogger(string loggerName, Action writeLog, Func shouldLog) + { + _loggerName = loggerName; + _writeLog = writeLog; + _shouldLog = shouldLog; + } + + public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) + { + var severity = MapSeverity(logLevel); + if (messageFunc == null) + { + return _shouldLog(_loggerName, severity); + } + + messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters); + if (exception != null) + { + return LogException(logLevel, messageFunc, exception); + } + _writeLog(_loggerName, messageFunc(), severity); + return true; + } + + public bool LogException(LogLevel logLevel, Func messageFunc, Exception exception) + { + var severity = MapSeverity(logLevel); + var message = messageFunc() + Environment.NewLine + exception; + _writeLog(_loggerName, message, severity); + return true; + } + + private static int MapSeverity(LogLevel logLevel) + { + switch (logLevel) + { + case LogLevel.Fatal: + return TraceEventTypeValues.Critical; + + case LogLevel.Error: + return TraceEventTypeValues.Error; + + case LogLevel.Warn: + return TraceEventTypeValues.Warning; + + case LogLevel.Info: + return TraceEventTypeValues.Information; + + default: + return TraceEventTypeValues.Verbose; + } + } + } + } + + internal class SerilogLogProvider : LogProviderBase + { + private readonly Func _getLoggerByNameDelegate; + private static bool s_providerIsAvailableOverride = true; + + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "Serilog")] + public SerilogLogProvider() + { + if (!IsLoggerAvailable()) + { + throw new InvalidOperationException("Serilog.Log not found"); + } + _getLoggerByNameDelegate = GetForContextMethodCall(); + } + + public static bool ProviderIsAvailableOverride + { + get { return s_providerIsAvailableOverride; } + set { s_providerIsAvailableOverride = value; } + } + + public override Logger GetLogger(string name) + { + return new SerilogLogger(_getLoggerByNameDelegate(name)).Log; + } + + internal static bool IsLoggerAvailable() + { + return ProviderIsAvailableOverride && GetLogManagerType() != null; + } + + protected override OpenNdc GetOpenNdcMethod() + { + return message => GetPushProperty()("NDC", message); + } + + protected override OpenMdc GetOpenMdcMethod() + { + return (key, value) => GetPushProperty()(key, value); + } + + private static Func GetPushProperty() + { + Type ndcContextType = Type.GetType("Serilog.Context.LogContext, Serilog") ?? + Type.GetType("Serilog.Context.LogContext, Serilog.FullNetFx"); + + MethodInfo pushPropertyMethod = ndcContextType.GetMethodPortable( + "PushProperty", + typeof(string), + typeof(object), + typeof(bool)); + + ParameterExpression nameParam = Expression.Parameter(typeof(string), "name"); + ParameterExpression valueParam = Expression.Parameter(typeof(object), "value"); + ParameterExpression destructureObjectParam = Expression.Parameter(typeof(bool), "destructureObjects"); + MethodCallExpression pushPropertyMethodCall = Expression + .Call(null, pushPropertyMethod, nameParam, valueParam, destructureObjectParam); + var pushProperty = Expression + .Lambda>( + pushPropertyMethodCall, + nameParam, + valueParam, + destructureObjectParam) + .Compile(); + + return (key, value) => pushProperty(key, value, false); + } + + private static Type GetLogManagerType() + { + return Type.GetType("Serilog.Log, Serilog"); + } + + private static Func GetForContextMethodCall() + { + Type logManagerType = GetLogManagerType(); + MethodInfo method = logManagerType.GetMethodPortable("ForContext", typeof(string), typeof(object), typeof(bool)); + ParameterExpression propertyNameParam = Expression.Parameter(typeof(string), "propertyName"); + ParameterExpression valueParam = Expression.Parameter(typeof(object), "value"); + ParameterExpression destructureObjectsParam = Expression.Parameter(typeof(bool), "destructureObjects"); + MethodCallExpression methodCall = Expression.Call(null, method, new Expression[] + { + propertyNameParam, + valueParam, + destructureObjectsParam + }); + var func = Expression.Lambda>( + methodCall, + propertyNameParam, + valueParam, + destructureObjectsParam) + .Compile(); + return name => func("SourceContext", name, false); + } + + internal class SerilogLogger + { + private readonly object _logger; + private static readonly object DebugLevel; + private static readonly object ErrorLevel; + private static readonly object FatalLevel; + private static readonly object InformationLevel; + private static readonly object VerboseLevel; + private static readonly object WarningLevel; + private static readonly Func IsEnabled; + private static readonly Action Write; + private static readonly Action WriteException; + + [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] + [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "ILogger")] + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "LogEventLevel")] + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "Serilog")] + static SerilogLogger() + { + var logEventLevelType = Type.GetType("Serilog.Events.LogEventLevel, Serilog"); + if (logEventLevelType == null) + { + throw new InvalidOperationException("Type Serilog.Events.LogEventLevel was not found."); + } + DebugLevel = Enum.Parse(logEventLevelType, "Debug", false); + ErrorLevel = Enum.Parse(logEventLevelType, "Error", false); + FatalLevel = Enum.Parse(logEventLevelType, "Fatal", false); + InformationLevel = Enum.Parse(logEventLevelType, "Information", false); + VerboseLevel = Enum.Parse(logEventLevelType, "Verbose", false); + WarningLevel = Enum.Parse(logEventLevelType, "Warning", false); + + // Func isEnabled = (logger, level) => { return + // ((SeriLog.ILogger)logger).IsEnabled(level); } + var loggerType = Type.GetType("Serilog.ILogger, Serilog"); + if (loggerType == null) + { + throw new InvalidOperationException("Type Serilog.ILogger was not found."); + } + MethodInfo isEnabledMethodInfo = loggerType.GetMethodPortable("IsEnabled", logEventLevelType); + ParameterExpression instanceParam = Expression.Parameter(typeof(object)); + UnaryExpression instanceCast = Expression.Convert(instanceParam, loggerType); + ParameterExpression levelParam = Expression.Parameter(typeof(object)); + UnaryExpression levelCast = Expression.Convert(levelParam, logEventLevelType); + MethodCallExpression isEnabledMethodCall = Expression.Call(instanceCast, isEnabledMethodInfo, levelCast); + IsEnabled = Expression.Lambda>(isEnabledMethodCall, instanceParam, levelParam).Compile(); + + // Action Write = (logger, level, message, params) => { + // ((SeriLog.ILoggerILogger)logger).Write(level, message, params); } + MethodInfo writeMethodInfo = loggerType.GetMethodPortable("Write", logEventLevelType, typeof(string), typeof(object[])); + ParameterExpression messageParam = Expression.Parameter(typeof(string)); + ParameterExpression propertyValuesParam = Expression.Parameter(typeof(object[])); + MethodCallExpression writeMethodExp = Expression.Call( + instanceCast, + writeMethodInfo, + levelCast, + messageParam, + propertyValuesParam); + var expression = Expression.Lambda>( + writeMethodExp, + instanceParam, + levelParam, + messageParam, + propertyValuesParam); + Write = expression.Compile(); + + // Action WriteException = (logger, level, + // exception, message) => { ((ILogger)logger).Write(level, exception, message, new + // object[]); } + MethodInfo writeExceptionMethodInfo = loggerType.GetMethodPortable("Write", + logEventLevelType, + typeof(Exception), + typeof(string), + typeof(object[])); + ParameterExpression exceptionParam = Expression.Parameter(typeof(Exception)); + writeMethodExp = Expression.Call( + instanceCast, + writeExceptionMethodInfo, + levelCast, + exceptionParam, + messageParam, + propertyValuesParam); + WriteException = Expression.Lambda>( + writeMethodExp, + instanceParam, + levelParam, + exceptionParam, + messageParam, + propertyValuesParam).Compile(); + } + + internal SerilogLogger(object logger) + { + _logger = logger; + } + + public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) + { + var translatedLevel = TranslateLevel(logLevel); + if (messageFunc == null) + { + return IsEnabled(_logger, translatedLevel); + } + + if (!IsEnabled(_logger, translatedLevel)) + { + return false; + } + + if (exception != null) + { + LogException(translatedLevel, messageFunc, exception, formatParameters); + } + else + { + LogMessage(translatedLevel, messageFunc, formatParameters); + } + + return true; + } + + private void LogMessage(object translatedLevel, Func messageFunc, object[] formatParameters) + { + Write(_logger, translatedLevel, messageFunc(), formatParameters); + } + + private void LogException(object logLevel, Func messageFunc, Exception exception, object[] formatParams) + { + WriteException(_logger, logLevel, exception, messageFunc(), formatParams); + } + + private static object TranslateLevel(LogLevel logLevel) + { + switch (logLevel) + { + case LogLevel.Fatal: + return FatalLevel; + + case LogLevel.Error: + return ErrorLevel; + + case LogLevel.Warn: + return WarningLevel; + + case LogLevel.Info: + return InformationLevel; + + case LogLevel.Trace: + return VerboseLevel; + + default: + return DebugLevel; + } + } + } + } + + internal class LoupeLogProvider : LogProviderBase + { + /// + /// The form of the Loupe Log.Write method we're using + /// + internal delegate void WriteDelegate( + int severity, + string logSystem, + int skipFrames, + Exception exception, + bool attributeToException, + int writeMode, + string detailsXml, + string category, + string caption, + string description, + params object[] args + ); + + private static bool s_providerIsAvailableOverride = true; + private readonly WriteDelegate _logWriteDelegate; + + public LoupeLogProvider() + { + if (!IsLoggerAvailable()) + { + throw new InvalidOperationException("Gibraltar.Agent.Log (Loupe) not found"); + } + + _logWriteDelegate = GetLogWriteDelegate(); + } + + /// + /// Gets or sets a value indicating whether [provider is available override]. Used in tests. + /// + /// true if [provider is available override]; otherwise, false. + public static bool ProviderIsAvailableOverride + { + get { return s_providerIsAvailableOverride; } + set { s_providerIsAvailableOverride = value; } + } + + public override Logger GetLogger(string name) + { + return new LoupeLogger(name, _logWriteDelegate).Log; + } + + public static bool IsLoggerAvailable() + { + return ProviderIsAvailableOverride && GetLogManagerType() != null; + } + + private static Type GetLogManagerType() + { + return Type.GetType("Gibraltar.Agent.Log, Gibraltar.Agent"); + } + + private static WriteDelegate GetLogWriteDelegate() + { + Type logManagerType = GetLogManagerType(); + Type logMessageSeverityType = Type.GetType("Gibraltar.Agent.LogMessageSeverity, Gibraltar.Agent"); + Type logWriteModeType = Type.GetType("Gibraltar.Agent.LogWriteMode, Gibraltar.Agent"); + + MethodInfo method = logManagerType.GetMethodPortable( + "Write", + logMessageSeverityType, typeof(string), typeof(int), typeof(Exception), typeof(bool), + logWriteModeType, typeof(string), typeof(string), typeof(string), typeof(string), typeof(object[])); + + var callDelegate = (WriteDelegate) method.CreateDelegate(typeof(WriteDelegate)); + return callDelegate; + } + + internal class LoupeLogger + { + private const string LogSystem = "LibLog"; + + private readonly string _category; + private readonly WriteDelegate _logWriteDelegate; + private readonly int _skipLevel; + + internal LoupeLogger(string category, WriteDelegate logWriteDelegate) + { + _category = category; + _logWriteDelegate = logWriteDelegate; +#if DEBUG + _skipLevel = 2; +#else + _skipLevel = 1; +#endif + } + + public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters) + { + if (messageFunc == null) + { + //nothing to log.. + return true; + } + + messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters); + + _logWriteDelegate(ToLogMessageSeverity(logLevel), LogSystem, _skipLevel, exception, true, 0, null, + _category, null, messageFunc.Invoke()); + + return true; + } + + private static int ToLogMessageSeverity(LogLevel logLevel) + { + switch (logLevel) + { + case LogLevel.Trace: + return TraceEventTypeValues.Verbose; + + case LogLevel.Debug: + return TraceEventTypeValues.Verbose; + + case LogLevel.Info: + return TraceEventTypeValues.Information; + + case LogLevel.Warn: + return TraceEventTypeValues.Warning; + + case LogLevel.Error: + return TraceEventTypeValues.Error; + + case LogLevel.Fatal: + return TraceEventTypeValues.Critical; + + default: + throw new ArgumentOutOfRangeException("logLevel"); + } + } + } + } + + internal static class TraceEventTypeValues + { + internal static readonly Type Type; + internal static readonly int Verbose; + internal static readonly int Information; + internal static readonly int Warning; + internal static readonly int Error; + internal static readonly int Critical; + + [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] + static TraceEventTypeValues() + { + var assembly = typeof(Uri).GetAssemblyPortable(); // This is to get to the System.dll assembly in a PCL compatible way. + if (assembly == null) + { + return; + } + Type = assembly.GetType("System.Diagnostics.TraceEventType"); + if (Type == null) return; + Verbose = (int) Enum.Parse(Type, "Verbose", false); + Information = (int) Enum.Parse(Type, "Information", false); + Warning = (int) Enum.Parse(Type, "Warning", false); + Error = (int) Enum.Parse(Type, "Error", false); + Critical = (int) Enum.Parse(Type, "Critical", false); + } + } + + internal static class LogMessageFormatter + { + //private static readonly Regex Pattern = new Regex(@"\{@?\w{1,}\}"); +#if LIBLOG_PORTABLE + private static readonly Regex Pattern = new Regex(@"(?[^\d{][^ }]*)}"); +#else + private static readonly Regex Pattern = new Regex(@"(?[^ :{}]+)(?:[^}]+)?}", RegexOptions.Compiled); +#endif + + /// + /// Some logging frameworks support structured logging, such as serilog. This will allow + /// you to add names to structured data in a format string: For example: Log("Log message + /// to {user}", user). This only works with serilog, but as the user of LibLog, you don't + /// know if serilog is actually used. So, this class simulates that. it will replace any + /// text in {curly braces} with an index number. + /// + /// "Log {message} to {user}" would turn into => "Log {0} to {1}". Then the format + /// parameters are handled using regular .net string.Format. + /// + /// The message builder. + /// The format parameters. + /// + public static Func SimulateStructuredLogging(Func messageBuilder, object[] formatParameters) + { + if (formatParameters == null || formatParameters.Length == 0) + { + return messageBuilder; + } + + return () => + { + string targetMessage = messageBuilder(); + IEnumerable patternMatches; + return FormatStructuredMessage(targetMessage, formatParameters, out patternMatches); + }; + } + + private static string ReplaceFirst(string text, string search, string replace) + { + int pos = text.IndexOf(search, StringComparison.Ordinal); + if (pos < 0) + { + return text; + } + return text.Substring(0, pos) + replace + text.Substring(pos + search.Length); + } + + public static string FormatStructuredMessage(string targetMessage, object[] formatParameters, out IEnumerable patternMatches) + { + if (formatParameters.Length == 0) + { + patternMatches = Enumerable.Empty(); + return targetMessage; + } + + List processedArguments = new List(); + patternMatches = processedArguments; + + foreach (Match match in Pattern.Matches(targetMessage)) + { + var arg = match.Groups["arg"].Value; + + int notUsed; + if (!int.TryParse(arg, out notUsed)) + { + int argumentIndex = processedArguments.IndexOf(arg); + if (argumentIndex == -1) + { + argumentIndex = processedArguments.Count; + processedArguments.Add(arg); + } + + targetMessage = ReplaceFirst(targetMessage, match.Value, + "{" + argumentIndex + match.Groups["format"].Value + "}"); + } + } + try + { + return string.Format(CultureInfo.InvariantCulture, targetMessage, formatParameters); + } + catch (FormatException ex) + { + throw new FormatException("The input string '" + targetMessage + "' could not be formatted using string.Format", ex); + } + } + } + + internal static class TypeExtensions + { + internal static ConstructorInfo GetConstructorPortable(this Type type, params Type[] types) + { +#if LIBLOG_PORTABLE + return type.GetTypeInfo().DeclaredConstructors.FirstOrDefault + (constructor => + constructor.GetParameters() + .Select(parameter => parameter.ParameterType) + .SequenceEqual(types)); +#else + return type.GetConstructor(types); +#endif + } + + internal static MethodInfo GetMethodPortable(this Type type, string name) + { +#if LIBLOG_PORTABLE + return type.GetRuntimeMethods().SingleOrDefault(m => m.Name == name); +#else + return type.GetMethod(name); +#endif + } + + internal static MethodInfo GetMethodPortable(this Type type, string name, params Type[] types) + { +#if LIBLOG_PORTABLE + return type.GetRuntimeMethod(name, types); +#else + return type.GetMethod(name, types); +#endif + } + + internal static PropertyInfo GetPropertyPortable(this Type type, string name) + { +#if LIBLOG_PORTABLE + return type.GetRuntimeProperty(name); +#else + return type.GetProperty(name); +#endif + } + + internal static IEnumerable GetFieldsPortable(this Type type) + { +#if LIBLOG_PORTABLE + return type.GetRuntimeFields(); +#else + return type.GetFields(); +#endif + } + + internal static Type GetBaseTypePortable(this Type type) + { +#if LIBLOG_PORTABLE + return type.GetTypeInfo().BaseType; +#else + return type.BaseType; +#endif + } + +#if LIBLOG_PORTABLE + + internal static MethodInfo GetGetMethod(this PropertyInfo propertyInfo) + { + return propertyInfo.GetMethod; + } + + internal static MethodInfo GetSetMethod(this PropertyInfo propertyInfo) + { + return propertyInfo.SetMethod; + } + +#endif + +#if !LIBLOG_PORTABLE + internal static object CreateDelegate(this MethodInfo methodInfo, Type delegateType) + { + return Delegate.CreateDelegate(delegateType, methodInfo); + } +#endif + + internal static Assembly GetAssemblyPortable(this Type type) + { +#if LIBLOG_PORTABLE + return type.GetTypeInfo().Assembly; +#else + return type.Assembly; +#endif + } + } + + internal class DisposableAction : IDisposable + { + private readonly Action _onDispose; + + public DisposableAction(Action onDispose = null) + { + _onDispose = onDispose; + } + + public void Dispose() + { + if (_onDispose != null) + { + _onDispose(); + } + } + } +} + +#endif \ No newline at end of file diff --git a/src/CodeProject.ObjectPool/CodeProject.ObjectPool.csproj b/src/CodeProject.ObjectPool/CodeProject.ObjectPool.csproj index 625aeef..195679d 100644 --- a/src/CodeProject.ObjectPool/CodeProject.ObjectPool.csproj +++ b/src/CodeProject.ObjectPool/CodeProject.ObjectPool.csproj @@ -2,17 +2,13 @@ CodeProject.ObjectPool Generic and concurrent Object Pool - 3.0.3 - netstandard1.0;netstandard1.1;netstandard1.2;netstandard1.3;net35;net40;net45 + 3.0.4 + netstandard1.0;netstandard1.3;net35;net40;net45 true ../../pomma89.snk true true - 1.6.1 false - false - false - false @@ -37,32 +33,27 @@ - - - - $(DefineConstants);NETSTD10;LIBLOG_PORTABLE $(PackageTargetFallback);dnxcore50 - - $(DefineConstants);NETSTD11;LIBLOG_PORTABLE - $(PackageTargetFallback);dnxcore50 - - - - $(DefineConstants);NETSTD12;LIBLOG_PORTABLE - $(PackageTargetFallback);dnxcore50 - + + + $(DefineConstants);NETSTD13;LIBLOG_PORTABLE $(PackageTargetFallback);dnxcore50 + + + + + - $(DefineConstants);NET35 + $(DefineConstants);NET35;HAS_SERIALIZABLE @@ -70,7 +61,7 @@ - $(DefineConstants);NET40 + $(DefineConstants);NET40;HAS_SERIALIZABLE @@ -79,7 +70,7 @@ - $(DefineConstants);NET45 + $(DefineConstants);NET45;HAS_SERIALIZABLE diff --git a/src/CodeProject.ObjectPool/Core/PooledObjectInfo.cs b/src/CodeProject.ObjectPool/Core/PooledObjectInfo.cs index 6518559..6dd6e7c 100644 --- a/src/CodeProject.ObjectPool/Core/PooledObjectInfo.cs +++ b/src/CodeProject.ObjectPool/Core/PooledObjectInfo.cs @@ -21,12 +21,14 @@ // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT // OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +using System; + namespace CodeProject.ObjectPool.Core { /// /// Core information about a specific . /// - public sealed class PooledObjectInfo + public sealed class PooledObjectInfo : IEquatable { /// /// An identifier which is unique inside the pool to which this object belongs. Moreover, @@ -50,5 +52,64 @@ public sealed class PooledObjectInfo /// that object to re-add itself back to the pool. /// internal IObjectPoolHandle Handle { get; set; } + + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + public override string ToString() => $"{nameof(Id)}: {Id}, {nameof(Payload)}: {Payload}"; + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// + /// true if the current object is equal to the parameter; + /// otherwise, false. + /// + /// An object to compare with this object. + public bool Equals(PooledObjectInfo other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Id == other.Id; + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified is equal to the current + /// ; otherwise, false. + /// + /// The to compare with the current . + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + var info = obj as PooledObjectInfo; + return info != null && Equals(info); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// A hash code for the current . + public override int GetHashCode() => Id; + + /// + /// Compares to pooled objects info by . + /// + /// Left object. + /// Right object. + /// True if given pooled objects info are equal, false otherwise. + public static bool operator ==(PooledObjectInfo left, PooledObjectInfo right) => Equals(left, right); + + /// + /// Compares to pooled objects info by . + /// + /// Left object. + /// Right object. + /// True if given pooled objects info are not equal, false otherwise. + public static bool operator !=(PooledObjectInfo left, PooledObjectInfo right) => !Equals(left, right); } } \ No newline at end of file diff --git a/src/CodeProject.ObjectPool/ObjectPool.cs b/src/CodeProject.ObjectPool/ObjectPool.cs index 96b6050..9356ad6 100644 --- a/src/CodeProject.ObjectPool/ObjectPool.cs +++ b/src/CodeProject.ObjectPool/ObjectPool.cs @@ -9,7 +9,6 @@ */ using CodeProject.ObjectPool.Core; -using PommaLabs.Thrower; using System; using System.Linq; using System.Threading; @@ -79,7 +78,7 @@ public ObjectPool(Func factoryMethod) public ObjectPool(int maximumPoolSize, Func factoryMethod) { // Preconditions - Raise.ArgumentOutOfRangeException.IfIsLessOrEqual(maximumPoolSize, 0, nameof(maximumPoolSize), ErrorMessages.NegativeOrZeroMaximumPoolSize); + if (maximumPoolSize <= 0) throw new ArgumentOutOfRangeException(nameof(maximumPoolSize), ErrorMessages.NegativeOrZeroMaximumPoolSize); // Throws an exception if the type does not have default constructor - on purpose! We // could have added a generic constraint with new (), but we did not want to limit the @@ -122,7 +121,7 @@ public int MaximumPoolSize set { // Preconditions - Raise.ArgumentOutOfRangeException.If(value < 1, nameof(value), ErrorMessages.NegativeOrZeroMaximumPoolSize); + if (value <= 0) throw new ArgumentOutOfRangeException(nameof(value), ErrorMessages.NegativeOrZeroMaximumPoolSize); // Resize the pool and destroy exceeding items, if any. foreach (var exceedingItem in PooledObjects.Resize(value)) diff --git a/src/CodeProject.ObjectPool/ParameterizedObjectPool.cs b/src/CodeProject.ObjectPool/ParameterizedObjectPool.cs index 52b4f3d..abb54b7 100644 --- a/src/CodeProject.ObjectPool/ParameterizedObjectPool.cs +++ b/src/CodeProject.ObjectPool/ParameterizedObjectPool.cs @@ -9,7 +9,6 @@ */ using CodeProject.ObjectPool.Core; -using PommaLabs.Thrower; using System; using System.Diagnostics; using System.Linq; @@ -69,7 +68,7 @@ public int MaximumPoolSize set { // Preconditions - Raise.ArgumentOutOfRangeException.If(value < 1, nameof(value), ErrorMessages.NegativeOrZeroMaximumPoolSize); + if (value < 1) throw new ArgumentOutOfRangeException(nameof(value), ErrorMessages.NegativeOrZeroMaximumPoolSize); _maximumPoolSize = value; } @@ -123,7 +122,7 @@ public ParameterizedObjectPool(Func factoryMethod) public ParameterizedObjectPool(int maximumPoolSize, Func factoryMethod) { // Preconditions - Raise.ArgumentOutOfRangeException.If(maximumPoolSize < 1, nameof(maximumPoolSize), ErrorMessages.NegativeOrZeroMaximumPoolSize); + if (maximumPoolSize < 1) throw new ArgumentOutOfRangeException(nameof(maximumPoolSize), ErrorMessages.NegativeOrZeroMaximumPoolSize); // Assigning properties Diagnostics = new ObjectPoolDiagnostics(); @@ -165,7 +164,7 @@ public TValue GetObject(TKey key) #region Low-level Pooling -#if (NETSTD10 || NETSTD11 || NETSTD12) +#if NETSTD10 private readonly System.Collections.Generic.Dictionary> _pools = new System.Collections.Generic.Dictionary>(); #else private readonly System.Collections.Hashtable _pools = new System.Collections.Hashtable(); @@ -191,7 +190,7 @@ private void ClearPools() private bool TryGetPool(TKey key, out ObjectPool objectPool) { -#if (NETSTD10 || NETSTD11 || NETSTD12) +#if NETSTD10 // Dictionary requires locking even for readers. lock (_pools) { diff --git a/src/CodeProject.ObjectPool/PooledObject.cs b/src/CodeProject.ObjectPool/PooledObject.cs index 1ceb2b4..cc800da 100644 --- a/src/CodeProject.ObjectPool/PooledObject.cs +++ b/src/CodeProject.ObjectPool/PooledObject.cs @@ -9,13 +9,12 @@ */ using CodeProject.ObjectPool.Core; -using PommaLabs.Thrower.Goodies; using System; using System.Collections.Generic; #if !NET35 -using PommaLabs.Thrower.Logging; +using CodeProject.ObjectPool.Logging; #endif @@ -24,8 +23,11 @@ namespace CodeProject.ObjectPool /// /// PooledObject base class. /// +#if HAS_SERIALIZABLE [Serializable] - public abstract class PooledObject : EquatableObject, IDisposable +#endif + + public abstract class PooledObject : IDisposable, IEquatable { #region Logging @@ -191,35 +193,63 @@ private void HandleReAddingToPool(bool reRegisterForFinalization) #region Formatting and equality /// - /// Returns all property (or field) values, along with their names, so that they can be - /// used to produce a meaningful . + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + public override string ToString() => PooledObjectInfo.ToString(); + + /// + /// Indicates whether the current object is equal to another object of the same type. /// /// - /// All property (or field) values, along with their names, so that they can be used to - /// produce a meaningful . + /// true if the current object is equal to the parameter; + /// otherwise, false. /// - protected override IEnumerable> GetFormattingMembers() + /// An object to compare with this object. + public virtual bool Equals(PooledObject other) { - yield return new KeyValuePair(nameof(PooledObjectInfo.Id), PooledObjectInfo.Id); - if (PooledObjectInfo.Payload != null) - { - yield return new KeyValuePair(nameof(PooledObjectInfo.Payload), PooledObjectInfo.Payload); - } + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return PooledObjectInfo.Equals(other.PooledObjectInfo); } /// - /// Returns all property (or field) values that should be used inside - /// or . + /// Determines whether the specified is equal to the current . /// /// - /// All property (or field) values that should be used inside - /// or . + /// true if the specified is equal to the current + /// ; otherwise, false. /// - protected override IEnumerable GetIdentifyingMembers() + /// The to compare with the current . + public override bool Equals(object obj) { - yield return PooledObjectInfo.Id; + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals(obj as PooledObject); } + /// + /// Serves as a hash function for a particular type. + /// + /// A hash code for the current . + public override int GetHashCode() => PooledObjectInfo.GetHashCode(); + + /// + /// Compares to pooled objects. + /// + /// Left object. + /// Right object. + /// True if given pooled objects are equal, false otherwise. + public static bool operator ==(PooledObject left, PooledObject right) => Equals(left, right); + + /// + /// Compares to pooled objects. + /// + /// Left object. + /// Right object. + /// True if given pooled objects are not equal, false otherwise. + public static bool operator !=(PooledObject left, PooledObject right) => !Equals(left, right); + #endregion Formatting and equality } } \ No newline at end of file diff --git a/src/CodeProject.ObjectPool/PooledObjectWrapper.cs b/src/CodeProject.ObjectPool/PooledObjectWrapper.cs index 9c1ce86..eb84d1c 100644 --- a/src/CodeProject.ObjectPool/PooledObjectWrapper.cs +++ b/src/CodeProject.ObjectPool/PooledObjectWrapper.cs @@ -9,7 +9,6 @@ */ using CodeProject.ObjectPool.Core; -using PommaLabs.Thrower; using System; namespace CodeProject.ObjectPool @@ -17,7 +16,10 @@ namespace CodeProject.ObjectPool /// /// PooledObject wrapper, for classes which cannot inherit from that class. /// +#if HAS_SERIALIZABLE [Serializable] +#endif + public sealed class PooledObjectWrapper : PooledObject where T : class { /// @@ -27,10 +29,7 @@ public sealed class PooledObjectWrapper : PooledObject where T : class /// Given resource is null. public PooledObjectWrapper(T resource) { - // Preconditions - Raise.ArgumentNullException.IfIsNull(resource, nameof(resource), ErrorMessages.NullResource); - - InternalResource = resource; + InternalResource = resource ?? throw new ArgumentNullException(nameof(resource), ErrorMessages.NullResource); base.OnReleaseResources += () => OnReleaseResources?.Invoke(InternalResource); base.OnResetState += () => OnResetState?.Invoke(InternalResource); diff --git a/src/CodeProject.ObjectPool/Properties/AssemblyInfo.cs b/src/CodeProject.ObjectPool/Properties/AssemblyInfo.cs index 628d201..3b842b4 100644 --- a/src/CodeProject.ObjectPool/Properties/AssemblyInfo.cs +++ b/src/CodeProject.ObjectPool/Properties/AssemblyInfo.cs @@ -10,18 +10,12 @@ using System; using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; -[assembly: AssemblyVersion("3.0.0")] - // General Information about an assembly is controlled through the following set of attributes. // Change these attribute values to modify the information associated with an assembly. -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("CodeProject")] -[assembly: AssemblyProduct("CodeProject.ObjectPool")] -[assembly: AssemblyTrademark("")] +[assembly: AssemblyVersion("3.0.0")] // Setting ComVisible to false makes the types in this assembly not visible to COM components. If you // need to access a type in this assembly from COM, set the ComVisible attribute to true on that type. @@ -33,7 +27,4 @@ // Allows an assembly to be called by partially trusted code. Without this declaration, only fully // trusted callers are able to use the assembly. -[assembly: AllowPartiallyTrustedCallers] - -// To allow simpler unit testing. -[assembly: InternalsVisibleTo("CodeProject.ObjectPool.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100efe22a8787b348e219d6b501b425d79f31502681d5a9f7d11030852e44e7ef2b29ddbc7dfcd6461fc3e67a6d7a186dea40535dad461679209a079a45440a99440bd292498b623e9a7fc5161c519c8d45b79fdfbd95a6e0ac5e211d5438d47b8635662d108448cb84bf1212983fab0010c68b41d5168bcbe5a59f9149974852c4")] \ No newline at end of file +[assembly: AllowPartiallyTrustedCallers] \ No newline at end of file diff --git a/src/CodeProject.ObjectPool/TimedObjectPool.cs b/src/CodeProject.ObjectPool/TimedObjectPool.cs index 70b944c..05d5ddd 100644 --- a/src/CodeProject.ObjectPool/TimedObjectPool.cs +++ b/src/CodeProject.ObjectPool/TimedObjectPool.cs @@ -21,10 +21,9 @@ // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT // OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -#if !(NETSTD10 || NETSTD11) +#if !NETSTD10 using CodeProject.ObjectPool.Core; -using PommaLabs.Thrower; using System; using System.Linq; using System.Threading; @@ -109,7 +108,7 @@ public TimedObjectPool(Func factoryMethod, TimeSpan timeout) public TimedObjectPool(int maximumPoolSize, Func factoryMethod, TimeSpan timeout) : base(maximumPoolSize, factoryMethod) { // Preconditions - Raise.ArgumentOutOfRangeException.IfIsLessOrEqual(timeout, TimeSpan.Zero, nameof(timeout), ErrorMessages.NegativeOrZeroTimeout); + if (timeout <= TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(timeout), ErrorMessages.NegativeOrZeroTimeout); // Assigning properties. Timeout = timeout; diff --git a/src/CodeProject.ObjectPool/doxyfile.txt b/src/CodeProject.ObjectPool/doxyfile.txt index 20f4a6b..487a6f4 100644 --- a/src/CodeProject.ObjectPool/doxyfile.txt +++ b/src/CodeProject.ObjectPool/doxyfile.txt @@ -38,7 +38,7 @@ PROJECT_NAME = "Generic and concurrent Object Pool" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 3.0.3 +PROJECT_NUMBER = 3.0.4 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/test/CodeProject.ObjectPool.Benchmarks/CodeProject.ObjectPool.Benchmarks.csproj b/test/CodeProject.ObjectPool.Benchmarks/CodeProject.ObjectPool.Benchmarks.csproj index d76ad76..56cb3a5 100644 --- a/test/CodeProject.ObjectPool.Benchmarks/CodeProject.ObjectPool.Benchmarks.csproj +++ b/test/CodeProject.ObjectPool.Benchmarks/CodeProject.ObjectPool.Benchmarks.csproj @@ -1,13 +1,7 @@  - net46 - CodeProject.ObjectPool.Benchmarks Exe - CodeProject.ObjectPool.Benchmarks - false - false - false @@ -15,14 +9,13 @@ - - - + + + - + - - + \ No newline at end of file diff --git a/test/CodeProject.ObjectPool.Benchmarks/Properties/AssemblyInfo.cs b/test/CodeProject.ObjectPool.Benchmarks/Properties/AssemblyInfo.cs index 6d78cfa..3181cef 100644 --- a/test/CodeProject.ObjectPool.Benchmarks/Properties/AssemblyInfo.cs +++ b/test/CodeProject.ObjectPool.Benchmarks/Properties/AssemblyInfo.cs @@ -1,19 +1,31 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +// File name: AssemblyInfo.cs +// +// Author(s): Alessio Parma +// +// The MIT License (MIT) +// +// Copyright (c) 2013-2018 Alessio Parma +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +// OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Hewlett-Packard")] -[assembly: AssemblyProduct("CodeProject.ObjectPool.Benchmarks")] -[assembly: AssemblyTrademark("")] +using System.Runtime.InteropServices; -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. +// Setting ComVisible to false makes the types in this assembly not visible to COM components. If you +// need to access a type in this assembly from COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("9c66b27c-8424-4d2e-a033-612776d59202")] +[assembly: Guid("9c66b27c-8424-4d2e-a033-612776d59202")] \ No newline at end of file diff --git a/test/CodeProject.ObjectPool.Examples/CodeProject.ObjectPool.Examples.csproj b/test/CodeProject.ObjectPool.Examples/CodeProject.ObjectPool.Examples.csproj index 538a48c..58400ff 100644 --- a/test/CodeProject.ObjectPool.Examples/CodeProject.ObjectPool.Examples.csproj +++ b/test/CodeProject.ObjectPool.Examples/CodeProject.ObjectPool.Examples.csproj @@ -1,14 +1,8 @@  netcoreapp1.1 - CodeProject.ObjectPool.Examples Exe - CodeProject.ObjectPool.Examples - 1.1.1 $(PackageTargetFallback);dnxcore50 - false - false - false diff --git a/test/CodeProject.ObjectPool.Examples/Properties/AssemblyInfo.cs b/test/CodeProject.ObjectPool.Examples/Properties/AssemblyInfo.cs index ef49568..d55a5cc 100644 --- a/test/CodeProject.ObjectPool.Examples/Properties/AssemblyInfo.cs +++ b/test/CodeProject.ObjectPool.Examples/Properties/AssemblyInfo.cs @@ -1,19 +1,31 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +// File name: AssemblyInfo.cs +// +// Author(s): Alessio Parma +// +// The MIT License (MIT) +// +// Copyright (c) 2013-2018 Alessio Parma +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +// OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Hewlett-Packard")] -[assembly: AssemblyProduct("CodeProject.ObjectPool.Examples")] -[assembly: AssemblyTrademark("")] +using System.Runtime.InteropServices; -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. +// Setting ComVisible to false makes the types in this assembly not visible to COM components. If you +// need to access a type in this assembly from COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("362c2e4a-3ee7-41be-a233-70649b258439")] +[assembly: Guid("362c2e4a-3ee7-41be-a233-70649b258439")] \ No newline at end of file diff --git a/test/CodeProject.ObjectPool.UnitTests/CodeProject.ObjectPool.UnitTests.csproj b/test/CodeProject.ObjectPool.UnitTests/CodeProject.ObjectPool.UnitTests.csproj index ffe6ef3..5a3b472 100644 --- a/test/CodeProject.ObjectPool.UnitTests/CodeProject.ObjectPool.UnitTests.csproj +++ b/test/CodeProject.ObjectPool.UnitTests/CodeProject.ObjectPool.UnitTests.csproj @@ -1,13 +1,7 @@  - CodeProject.ObjectPool.UnitTests - CodeProject.ObjectPool.UnitTests netcoreapp1.1;net35;net40;net45;net46 - 1.1.1 Exe - false - false - false @@ -15,12 +9,12 @@ - + - + - - + + @@ -44,8 +38,8 @@ - + @@ -53,8 +47,8 @@ - + @@ -62,7 +56,7 @@ - + \ No newline at end of file diff --git a/test/CodeProject.ObjectPool.UnitTests/Properties/AssemblyInfo.cs b/test/CodeProject.ObjectPool.UnitTests/Properties/AssemblyInfo.cs index 04906d8..e499881 100644 --- a/test/CodeProject.ObjectPool.UnitTests/Properties/AssemblyInfo.cs +++ b/test/CodeProject.ObjectPool.UnitTests/Properties/AssemblyInfo.cs @@ -1,19 +1,31 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +// File name: AssemblyInfo.cs +// +// Author(s): Alessio Parma +// +// The MIT License (MIT) +// +// Copyright (c) 2013-2018 Alessio Parma +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +// OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Hewlett-Packard")] -[assembly: AssemblyProduct("CodeProject.ObjectPool.UnitTests")] -[assembly: AssemblyTrademark("")] +using System.Runtime.InteropServices; -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. +// Setting ComVisible to false makes the types in this assembly not visible to COM components. If you +// need to access a type in this assembly from COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("3d1139bd-b992-487b-9e55-cf052d815450")] +[assembly: Guid("3d1139bd-b992-487b-9e55-cf052d815450")] \ No newline at end of file diff --git a/tools/packages.config b/tools/packages.config index 8e39b57..a630116 100644 --- a/tools/packages.config +++ b/tools/packages.config @@ -1,4 +1,4 @@ - + \ No newline at end of file