From db877f617a59bacbe28513854b98da397f66dfd0 Mon Sep 17 00:00:00 2001 From: James South Date: Sat, 30 Nov 2013 16:24:35 +0000 Subject: [PATCH] Improved memory usage Former-commit-id: 8547d9f8f2fcb4b3fda6e5f3b8373994c4ba5dda --- .gitignore | 2 +- .../NET4/ImageProcessor.Web.csproj | 12 +- .../NET45/Caching/CacheManager.cs | 231 +++++++++++++ .../NET45/Caching/CleanupImage.cs | 32 ++ .../NET45/Caching/DiskCache.cs | 72 ++-- ...PersistantDictionary.cs => MemoryCache.cs} | 86 +++-- .../NET45/Caching/SQLContext.cs | 48 ++- .../NET45/Config/ImageCacheSection.cs | 4 +- .../NET45/Helpers/LockedDictionary.cs | 321 ------------------ .../HttpModules/ImageProcessingModule.cs | 11 +- .../NET45/ImageProcessor.Web_NET45.csproj | 6 +- .../NET45/Settings.StyleCop | 11 + src/ImageProcessor/Imaging/ImageUtils.cs | 1 - src/ImageProcessor/Processors/Crop.cs | 11 +- 14 files changed, 420 insertions(+), 428 deletions(-) create mode 100644 src/ImageProcessor.Web/NET45/Caching/CacheManager.cs create mode 100644 src/ImageProcessor.Web/NET45/Caching/CleanupImage.cs rename src/ImageProcessor.Web/NET45/Caching/{PersistantDictionary.cs => MemoryCache.cs} (62%) delete mode 100644 src/ImageProcessor.Web/NET45/Helpers/LockedDictionary.cs create mode 100644 src/ImageProcessor.Web/NET45/Settings.StyleCop diff --git a/.gitignore b/.gitignore index 6178c90d3..a39e0d3a0 100644 --- a/.gitignore +++ b/.gitignore @@ -65,7 +65,7 @@ local.properties **/*.dotCover ## TODO: If you have NuGet Package Restore enabled, uncomment this -**/packages/ +src/packages/ # Visual C++ cache files ipch/ diff --git a/src/ImageProcessor.Web/NET4/ImageProcessor.Web.csproj b/src/ImageProcessor.Web/NET4/ImageProcessor.Web.csproj index c5ff34b28..29e5f62f8 100644 --- a/src/ImageProcessor.Web/NET4/ImageProcessor.Web.csproj +++ b/src/ImageProcessor.Web/NET4/ImageProcessor.Web.csproj @@ -65,6 +65,7 @@ ..\..\packages\Microsoft.Bcl.1.0.19\lib\net40\System.Runtime.dll + ..\..\packages\Microsoft.Bcl.1.0.19\lib\net40\System.Threading.Tasks.dll @@ -73,14 +74,21 @@ + + CacheManager.cs + + + CleanupImage.cs + - + + MemoryCache.cs + - diff --git a/src/ImageProcessor.Web/NET45/Caching/CacheManager.cs b/src/ImageProcessor.Web/NET45/Caching/CacheManager.cs new file mode 100644 index 000000000..0e1e517b5 --- /dev/null +++ b/src/ImageProcessor.Web/NET45/Caching/CacheManager.cs @@ -0,0 +1,231 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// +// Encapsulates methods that allow the caching and retrieval of objects. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Web.Caching +{ + #region Using + using System; + using System.Collections.Generic; + using System.Runtime.Caching; + #endregion + + /// + /// Encapsulates methods that allow the caching and retrieval of objects. + /// + public static class CacheManager + { + #region Fields + /// + /// The cache + /// + private static readonly ObjectCache Cache = System.Runtime.Caching.MemoryCache.Default; + + /// + /// An internal list of cache keys to allow bulk removal. + /// + private static readonly Dictionary CacheItems = new Dictionary(); + #endregion + + #region Methods + /// + /// Adds an item to the cache. + /// + /// + /// A unique identifier for the cache entry. + /// + /// + /// The object to insert. + /// + /// + /// Optional. An object that contains eviction details for the cache entry. This object + /// provides more options for eviction than a simple absolute expiration. The default value for the optional parameter + /// is null. + /// + /// + /// Optional. A named region in the cache to which the cache entry can be added, + /// if regions are implemented. The default value for the optional parameter + /// is null. + /// + /// + /// True if the insertion try succeeds, or false if there is an already an entry + /// in the cache with the same key as key. + /// + public static bool AddItem(string key, object value, CacheItemPolicy policy = null, string regionName = null) + { + bool isAdded; + + lock (Cache) + { + if (policy == null) + { + // Create a new cache policy with the default values + policy = new CacheItemPolicy(); + } + + isAdded = Cache.Add(key, value, policy, regionName); + + if (isAdded) + { + CacheItems.Add(key, regionName); + } + } + + return isAdded; + } + + /// + /// Fetches an item matching the given key from the cache. + /// + /// + /// A unique identifier for the cache entry. + /// + /// + /// Optional. A named region in the cache to which the cache entry can be added, + /// if regions are implemented. The default value for the optional parameter + /// is null. + /// + /// + /// The cache entry that is identified by key. + /// + public static object GetItem(string key, string regionName = null) + { + return Cache.Get(key, regionName); + } + + //public static bool + + /// + /// Updates an item to the cache. + /// + /// + /// A unique identifier for the cache entry. + /// + /// + /// The object to insert. + /// + /// + /// Optional. An object that contains eviction details for the cache entry. This object + /// provides more options for eviction than a simple absolute expiration. The default value for the optional parameter + /// is null. + /// + /// + /// Optional. A named region in the cache to which the cache entry can be added, + /// if regions are implemented. The default value for the optional parameter + /// is null. + /// + /// + /// True if the update try succeeds, or false if there is an already an entry + /// in the cache with the same key as key. + /// + public static bool UpdateItem(string key, object value, CacheItemPolicy policy = null, string regionName = null) + { + bool isUpDated = true; + + // Remove the item from the cache if it already exists. MemoryCache will + // not add an item with an existing name. + if (GetItem(key, regionName) != null) + { + isUpDated = RemoveItem(key, regionName); + } + + if (policy == null) + { + // Create a new cache policy with the default values + policy = new CacheItemPolicy(); + } + + if (isUpDated) + { + isUpDated = AddItem(key, value, policy, regionName); + } + + return isUpDated; + } + + /// + /// Removes an item matching the given key from the cache. + /// + /// + /// A unique identifier for the cache entry. + /// + /// + /// Optional. A named region in the cache to which the cache entry can be added, + /// if regions are implemented. The default value for the optional parameter + /// is null. + /// + /// + /// True if the removal try succeeds, or false if there is an already an entry + /// in the cache with the same key as key. + /// + public static bool RemoveItem(string key, string regionName = null) + { + bool isRemoved; + + lock (Cache) + { + isRemoved = Cache.Remove(key, regionName) != null; + + if (isRemoved) + { + CacheItems.Remove(key); + } + } + + return isRemoved; + } + + /// + /// Clears the cache. + /// + /// + /// The region name. + /// + /// + /// The . + /// + public static bool Clear(string regionName = null) + { + bool isCleared = false; + + lock (CacheItems) + { + // You can't remove items from a collection whilst you are iterating over it so you need to + // create a collection to store the items to remove. + Dictionary tempDictionary = new Dictionary(); + + foreach (KeyValuePair cacheItem in CacheItems) + { + // Does the cached key come with a region. + if ((cacheItem.Value == null) || (cacheItem.Value != null && cacheItem.Value.Equals(regionName, StringComparison.OrdinalIgnoreCase))) + { + isCleared = RemoveItem(cacheItem.Key, cacheItem.Value); + + if (isCleared) + { + tempDictionary.Add(cacheItem.Key, cacheItem.Value); + } + } + } + + if (isCleared) + { + // Loop through and clear out the dictionary of cache keys. + foreach (KeyValuePair cacheItem in tempDictionary) + { + CacheItems.Remove(cacheItem.Key); + } + } + } + + return isCleared; + } + #endregion + } +} \ No newline at end of file diff --git a/src/ImageProcessor.Web/NET45/Caching/CleanupImage.cs b/src/ImageProcessor.Web/NET45/Caching/CleanupImage.cs new file mode 100644 index 000000000..aa813bae3 --- /dev/null +++ b/src/ImageProcessor.Web/NET45/Caching/CleanupImage.cs @@ -0,0 +1,32 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// +// Describes a cached image for cleanup. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Web.Caching +{ + #region Using + using System; + #endregion + + /// + /// Describes a cached image for cleanup + /// + public sealed class CleanupImage + { + /// + /// Gets or sets the value of the cached image. + /// + public string Path { get; set; } + + /// + /// Gets or sets when the cached image should expire from the cache. + /// + public DateTime ExpiresUtc { get; set; } + } +} diff --git a/src/ImageProcessor.Web/NET45/Caching/DiskCache.cs b/src/ImageProcessor.Web/NET45/Caching/DiskCache.cs index 67445ee3a..3bbc0a42b 100644 --- a/src/ImageProcessor.Web/NET45/Caching/DiskCache.cs +++ b/src/ImageProcessor.Web/NET45/Caching/DiskCache.cs @@ -1,17 +1,18 @@ -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) James South. -// Licensed under the Apache License, Version 2.0. +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. // -// ----------------------------------------------------------------------- +// +// The disk cache. +// +// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Web.Caching { #region Using using System; - using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; - using System.Drawing; using System.Globalization; using System.IO; using System.Linq; @@ -238,7 +239,7 @@ namespace ImageProcessor.Web.Caching ExpiresUtc = expires }; - await PersistantDictionary.Instance.AddAsync(key, cachedImage); + await MemoryCache.Instance.AddAsync(key, cachedImage); } /// @@ -255,14 +256,16 @@ namespace ImageProcessor.Web.Caching if (this.isRemote) { - if (PersistantDictionary.Instance.TryGetValue(key, out cachedImage)) + cachedImage = await MemoryCache.Instance.GetValueAsync(key); + + if (cachedImage != null) { // Can't check the last write time so check to see if the cached image is set to expire // or if the max age is different. if (cachedImage.ExpiresUtc < DateTime.UtcNow.AddDays(-MaxFileCachedDuration) || cachedImage.MaxAge != MaxFileCachedDuration) { - if (await PersistantDictionary.Instance.TryRemoveAsync(key)) + if (await MemoryCache.Instance.RemoveAsync(key)) { isUpdated = true; } @@ -277,7 +280,9 @@ namespace ImageProcessor.Web.Caching else { // Test now for locally requested files. - if (PersistantDictionary.Instance.TryGetValue(key, out cachedImage)) + cachedImage = await MemoryCache.Instance.GetValueAsync(key); + + if (cachedImage != null) { FileInfo imageFileInfo = new FileInfo(this.requestPath); @@ -289,7 +294,7 @@ namespace ImageProcessor.Web.Caching || cachedImage.ExpiresUtc < DateTime.UtcNow.AddDays(-MaxFileCachedDuration) || cachedImage.MaxAge != MaxFileCachedDuration) { - if (await PersistantDictionary.Instance.TryRemoveAsync(key)) + if (await MemoryCache.Instance.RemoveAsync(key)) { isUpdated = true; } @@ -314,8 +319,17 @@ namespace ImageProcessor.Web.Caching /// internal async Task GetLastWriteTimeAsync() { - // Create Action delegate for TrimCachedFolders. - return await TaskHelpers.Run(this.GetLastWriteTime); + string key = Path.GetFileNameWithoutExtension(this.CachedPath); + DateTime dateTime = DateTime.UtcNow; + + CachedImage cachedImage = await MemoryCache.Instance.GetValueAsync(key); + + if (cachedImage != null) + { + dateTime = cachedImage.LastWriteTimeUtc; + } + + return dateTime; } /// @@ -383,17 +397,17 @@ namespace ImageProcessor.Web.Caching /// private async void TrimCachedFolders() { - // Group each cache folder and clear any expired items or any that exeed + // Group each cache folder and clear any expired items or any that exceed // the maximum allowable count. - var groups = PersistantDictionary.Instance.ToList() - .GroupBy(x => SubFolderRegex.Match(x.Value.Path).Value) + var groups = SQLContext.GetImagesForCleanup() + .GroupBy(x => SubFolderRegex.Match(x.Path).Value) .Where(g => g.Count() > MaxFilesCount); foreach (var group in groups) { int groupCount = group.Count(); - foreach (KeyValuePair pair in group.OrderBy(x => x.Value.ExpiresUtc)) + foreach (CleanupImage pair in group.OrderBy(x => x.ExpiresUtc)) { // If the group count is equal to the max count minus 1 then we know we // are counting down from a full directory not simply clearing out @@ -406,10 +420,10 @@ namespace ImageProcessor.Web.Caching try { // Remove from the cache and delete each CachedImage. - FileInfo fileInfo = new FileInfo(pair.Value.Path); + FileInfo fileInfo = new FileInfo(pair.Path); string key = Path.GetFileNameWithoutExtension(fileInfo.Name); - if (await PersistantDictionary.Instance.TryRemoveAsync(key)) + if (await MemoryCache.Instance.RemoveAsync(key)) { fileInfo.Delete(); groupCount -= 1; @@ -460,26 +474,6 @@ namespace ImageProcessor.Web.Caching return cachedPath; } - /// - /// Gets last modified time of the image. - /// - /// - /// The representing the last modified time of the image. - /// - private DateTime GetLastWriteTime() - { - string key = Path.GetFileNameWithoutExtension(this.CachedPath); - CachedImage cachedImage; - DateTime dateTime = DateTime.UtcNow; - - if (PersistantDictionary.Instance.TryGetValue(key, out cachedImage)) - { - dateTime = cachedImage.LastWriteTimeUtc; - } - - return dateTime; - } - /// /// The rough date time compare. /// diff --git a/src/ImageProcessor.Web/NET45/Caching/PersistantDictionary.cs b/src/ImageProcessor.Web/NET45/Caching/MemoryCache.cs similarity index 62% rename from src/ImageProcessor.Web/NET45/Caching/PersistantDictionary.cs rename to src/ImageProcessor.Web/NET45/Caching/MemoryCache.cs index aaa8f7c0b..dedaf5d56 100644 --- a/src/ImageProcessor.Web/NET45/Caching/PersistantDictionary.cs +++ b/src/ImageProcessor.Web/NET45/Caching/MemoryCache.cs @@ -1,31 +1,32 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) James South. -// Licensed under the Apache License, Version 2.0. +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. // -// ----------------------------------------------------------------------- +// +// +// +// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Web.Caching { #region Using using System; - using System.Collections.Generic; using System.Threading.Tasks; - using ImageProcessor.Web.Helpers; #endregion /// - /// Represents a collection of keys and values whose operations are concurrent. + /// Represents an in memory collection of keys and values whose operations are concurrent. /// - internal sealed class PersistantDictionary : LockedDictionary + internal sealed class MemoryCache { #region Fields /// - /// A new instance Initializes a new instance of the class. + /// A new instance Initializes a new instance of the class. /// initialized lazily. /// - private static readonly Lazy Lazy = - new Lazy(() => new PersistantDictionary()); + private static readonly Lazy Lazy = + new Lazy(() => new MemoryCache()); /// /// The object to lock against. @@ -35,19 +36,19 @@ namespace ImageProcessor.Web.Caching #region Constructors /// - /// Prevents a default instance of the class + /// Prevents a default instance of the class /// from being created. /// - private PersistantDictionary() + private MemoryCache() { this.LoadCache(); } #endregion /// - /// Gets the current instance of the class. + /// Gets the current instance of the class. /// - public static PersistantDictionary Instance + public static MemoryCache Instance { get { @@ -57,29 +58,47 @@ namespace ImageProcessor.Web.Caching #region Public /// - /// Tries to remove the value associated with the specified key. + /// Gets the associated with the specified key. /// /// - /// The key of the item to remove. + /// The key of the value to get. /// /// - /// true if the removes an element with - /// the specified key; otherwise, false. + /// The matching the given key if the contains an element with + /// the specified key; otherwise, null. /// - public async Task TryRemoveAsync(string key) + public async Task GetValueAsync(string key) { - // No CachedImage to remove. - if (!this.ContainsKey(key)) + CachedImage cachedImage = (CachedImage)CacheManager.GetItem(key); + + if (cachedImage == null) { - return false; + cachedImage = await SQLContext.GetImageAsync(key); + + if (cachedImage != null) + { + CacheManager.AddItem(key, cachedImage); + } } - // Remove the CachedImage. - CachedImage value = this[key]; + return cachedImage; + } - if (await this.SaveCacheAsync(key, value, true) > 0) + /// + /// Removes the value associated with the specified key. + /// + /// + /// The key of the item to remove. + /// + /// + /// true if the removes an element with + /// the specified key; otherwise, false. + /// + public async Task RemoveAsync(string key) + { + if (await this.SaveCacheAsync(key, null, true) > 0) { - this.Remove(key); + CacheManager.RemoveItem(key); return true; } @@ -103,7 +122,7 @@ namespace ImageProcessor.Web.Caching // Add the CachedImage. if (await this.SaveCacheAsync(key, cachedImage, false) > 0) { - this.Add(key, cachedImage); + CacheManager.AddItem(key, cachedImage); } return cachedImage; @@ -131,7 +150,7 @@ namespace ImageProcessor.Web.Caching { if (remove) { - return await SQLContext.RemoveImageAsync(cachedImage); + return await SQLContext.RemoveImageAsync(key); } return await SQLContext.AddImageAsync(cachedImage); @@ -150,13 +169,6 @@ namespace ImageProcessor.Web.Caching lock (SyncRoot) { SQLContext.CreateDatabase(); - - Dictionary dictionary = SQLContext.GetImages(); - - foreach (KeyValuePair pair in dictionary) - { - this.Add(pair); - } } } } diff --git a/src/ImageProcessor.Web/NET45/Caching/SQLContext.cs b/src/ImageProcessor.Web/NET45/Caching/SQLContext.cs index fe1da938d..deeb30658 100644 --- a/src/ImageProcessor.Web/NET45/Caching/SQLContext.cs +++ b/src/ImageProcessor.Web/NET45/Caching/SQLContext.cs @@ -83,29 +83,47 @@ namespace ImageProcessor.Web.Caching /// Gets all the images from the database. /// /// - /// The . + /// The . /// - internal static Dictionary GetImages() + internal static List GetImagesForCleanup() { - Dictionary dictionary = new Dictionary(); - try { + List images; using (SQLiteConnection connection = new SQLiteConnection(ConnectionString)) { - List images = connection.Query("SELECT * FROM CachedImage"); - - foreach (CachedImage cachedImage in images) - { - dictionary.Add(cachedImage.Key, cachedImage); - } + images = connection.Query("SELECT Path,ExpiresUtc FROM CachedImage"); } - return dictionary; + return images; + + } + catch + { + return new List(); + } + } + + /// + /// Gets a cached image from the database. + /// + /// + /// The key for the cached image to get. + /// + /// + /// The from the database. + /// + internal static async Task GetImageAsync(string key) + { + try + { + SQLiteAsyncConnection connection = new SQLiteAsyncConnection(ConnectionString); + + return await connection.GetAsync(c => c.Key == key); } catch { - return new Dictionary(); + return null; } } @@ -123,7 +141,6 @@ namespace ImageProcessor.Web.Caching try { SQLiteAsyncConnection connection = new SQLiteAsyncConnection(ConnectionString); - return await connection.InsertAsync(image); } catch @@ -135,17 +152,18 @@ namespace ImageProcessor.Web.Caching /// /// Removes a cached image from the database. /// - /// + /// /// The key for the cached image. /// /// /// The true if the addition of the cached image is removed; otherwise, false. /// - internal static async Task RemoveImageAsync(CachedImage cachedImage) + internal static async Task RemoveImageAsync(string key) { try { SQLiteAsyncConnection connection = new SQLiteAsyncConnection(ConnectionString); + CachedImage cachedImage = await connection.GetAsync(c => c.Key == key); return await connection.DeleteAsync(cachedImage); } diff --git a/src/ImageProcessor.Web/NET45/Config/ImageCacheSection.cs b/src/ImageProcessor.Web/NET45/Config/ImageCacheSection.cs index 6a28d3229..92015a83d 100644 --- a/src/ImageProcessor.Web/NET45/Config/ImageCacheSection.cs +++ b/src/ImageProcessor.Web/NET45/Config/ImageCacheSection.cs @@ -21,7 +21,7 @@ namespace ImageProcessor.Web.Config /// Gets or sets the virtual path of the cache folder. /// /// The name of the cache folder. - [ConfigurationProperty("virtualPath", DefaultValue = "~/cache", IsRequired = true)] + [ConfigurationProperty("virtualPath", DefaultValue = "~/app_data/cache", IsRequired = true)] [StringValidator(MinLength = 3, MaxLength = 200)] public string VirtualPath { @@ -29,7 +29,7 @@ namespace ImageProcessor.Web.Config { string virtualPath = (string)this["virtualPath"]; - return virtualPath.IsValidVirtualPathName() ? virtualPath : "~/cache"; + return virtualPath.IsValidVirtualPathName() ? virtualPath : "~/app_data/cache"; } set diff --git a/src/ImageProcessor.Web/NET45/Helpers/LockedDictionary.cs b/src/ImageProcessor.Web/NET45/Helpers/LockedDictionary.cs deleted file mode 100644 index 8cc26c06b..000000000 --- a/src/ImageProcessor.Web/NET45/Helpers/LockedDictionary.cs +++ /dev/null @@ -1,321 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) James South. -// Licensed under the Apache License, Version 2.0. -// -// ----------------------------------------------------------------------- - -namespace ImageProcessor.Web.Helpers -{ - #region Using - using System.Collections.Generic; - using System.Linq; - #endregion - - /// - /// Represents a collection of keys and values that are thread safe. - /// - /// - /// The type of the keys in the dictionary. - /// - /// - /// The type of the values in the dictionary. - /// - internal class LockedDictionary : IDictionary - { - /// - /// The _inner. - /// - private readonly Dictionary innerDictionary = new Dictionary(); - - #region Constructors - /// - /// Initializes a new instance of the class. - /// - /// - /// The value to initialize the LockedDictionary with. - /// - public LockedDictionary(IEnumerable> val = null) - { - if (val != null) - { - this.innerDictionary = val.ToDictionary(x => x.Key, x => x.Value); - } - } - #endregion - - #region Properties - /// - /// Gets a collection containing the keys in the . - /// - public ICollection Keys - { - get - { - lock (this.innerDictionary) - { - return this.innerDictionary.Keys.ToArray(); - } - } - } - - /// - /// Gets a collection containing the values in the . - /// - public ICollection Values - { - get - { - lock (this.innerDictionary) - { - return this.innerDictionary.Values.ToArray(); - } - } - } - - /// - /// Gets the number of key/value pairs contained in the . - /// - public int Count - { - get - { - lock (this.innerDictionary) - { - return this.innerDictionary.Count; - } - } - } - - /// - /// Gets a value indicating whether the is read only. - /// - public bool IsReadOnly - { - get { return false; } - } - - /// - /// Gets or sets the value associated with the specified key. - /// - /// - /// The key of the value to get or set. - /// - /// - /// TThe value associated with the specified key. If the specified key is not found, - /// a get operation throws a , - /// and a set operation creates a new element with the specified key. - /// - public TVal this[TKey key] - { - get - { - lock (this.innerDictionary) - { - return this.innerDictionary[key]; - } - } - - set - { - lock (this.innerDictionary) - { - this.innerDictionary[key] = value; - } - } - } - #endregion - - #region Methods - /// - /// Adds the specified key and value to the dictionary. - /// - /// - /// The key of the element to add. - /// - /// - /// The value of the element to add. The value can be null for reference types. - /// - public void Add(TKey key, TVal value) - { - lock (this.innerDictionary) - { - this.innerDictionary.Add(key, value); - } - } - - /// - /// Determines whether the LockedDictionary contains the specified key. - /// - /// - /// The key to locate in the LockedDictionary. - /// - /// - /// true if the LockedDictionary contains the key; otherwise, false. - /// - public bool ContainsKey(TKey key) - { - lock (this.innerDictionary) - { - return this.innerDictionary.ContainsKey(key); - } - } - - /// - /// Removes the value with the specified key from the . - /// - /// - /// The key of the element to remove. - /// - /// - /// true if the element is successfully found and removed; otherwise, false. - /// This method returns false if key is not found in the . - /// - public bool Remove(TKey key) - { - lock (this.innerDictionary) - { - return this.innerDictionary.Remove(key); - } - } - - /// - /// Gets the value associated with the specified key. - /// - /// - /// The key of the value to get. - /// - /// - /// When this method returns, contains the value associated with the specified key, if the key is found; - /// otherwise, the default value for the type of the value parameter. This parameter is passed uninitialized. - /// - /// - /// true if the contains an element with - /// the specified key; otherwise, false. - /// - public bool TryGetValue(TKey key, out TVal value) - { - lock (this.innerDictionary) - { - return this.innerDictionary.TryGetValue(key, out value); - } - } - - /// - /// Adds the specified key and value to the dictionary. - /// - /// - /// The representing - /// the item to add. - /// - public void Add(KeyValuePair item) - { - lock (this.innerDictionary) - { - this.innerDictionary.Add(item.Key, item.Value); - } - } - - /// - /// Removes all keys and values from the . - /// - public void Clear() - { - lock (this.innerDictionary) - { - this.innerDictionary.Clear(); - } - } - - /// - /// Determines whether the contains the specified key. - /// - /// - /// The representing - /// the item to locate. - /// - /// - /// true if the contains an element - /// with the specified key; otherwise, false. - /// - public bool Contains(KeyValuePair item) - { - lock (this.innerDictionary) - { - var inner = this.innerDictionary as IDictionary; - return inner.Contains(item); - } - } - - /// - /// Copies the elements of an to a one-dimensional - /// starting at a particular index. - /// - /// - /// The one-dimensional that is the destination of the elements copied - /// from .KeyCollection. - /// The must have zero-based indexing. - /// - /// - /// The zero-based index in array at which copying begins. - /// - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - lock (this.innerDictionary) - { - var inner = this.innerDictionary as IDictionary; - inner.CopyTo(array, arrayIndex); - } - } - - /// - /// Removes the item with the specified - /// from the - /// - /// - /// The representing the item to remove. - /// - /// - /// This method returns false if item is not found in the . - /// - public bool Remove(KeyValuePair item) - { - lock (this.innerDictionary) - { - var inner = this.innerDictionary as IDictionary; - return inner.Remove(item); - } - } - - /// - /// Returns an enumerator that iterates through the .KeyCollection. - /// - /// - /// A - /// for the . - /// - public IEnumerator> GetEnumerator() - { - lock (this.innerDictionary) - { - return this.innerDictionary.ToList().GetEnumerator(); - } - } - - /// - /// Returns an enumerator that iterates through the .KeyCollection. - /// - /// - /// A - /// for the . - /// - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - lock (this.innerDictionary) - { - return this.innerDictionary.ToArray().GetEnumerator(); - } - } - #endregion - } -} \ No newline at end of file diff --git a/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs b/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs index df1363b92..7097047a5 100644 --- a/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs +++ b/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs @@ -1,9 +1,12 @@ -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) James South. -// Licensed under the Apache License, Version 2.0. +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. // -// ----------------------------------------------------------------------- +// +// Processes any image requests within the web application. +// +// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Web.HttpModules { diff --git a/src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj b/src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj index 7274ce547..d9a2832fd 100644 --- a/src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj +++ b/src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj @@ -40,6 +40,7 @@ + @@ -49,14 +50,15 @@ + + - + - diff --git a/src/ImageProcessor.Web/NET45/Settings.StyleCop b/src/ImageProcessor.Web/NET45/Settings.StyleCop new file mode 100644 index 000000000..253825567 --- /dev/null +++ b/src/ImageProcessor.Web/NET45/Settings.StyleCop @@ -0,0 +1,11 @@ + + + + + James South + Copyright (c) James South. +Licensed under the Apache License, Version 2.0. + + + + \ No newline at end of file diff --git a/src/ImageProcessor/Imaging/ImageUtils.cs b/src/ImageProcessor/Imaging/ImageUtils.cs index 58a9568ab..f7e6de6b4 100644 --- a/src/ImageProcessor/Imaging/ImageUtils.cs +++ b/src/ImageProcessor/Imaging/ImageUtils.cs @@ -210,7 +210,6 @@ namespace ImageProcessor.Imaging /// True the value contains a valid image extension, otherwise false. public static bool IsValidImageExtension(string fileName) { - return FormatRegex.IsMatch(fileName); } diff --git a/src/ImageProcessor/Processors/Crop.cs b/src/ImageProcessor/Processors/Crop.cs index 9023916b6..53ee2041a 100644 --- a/src/ImageProcessor/Processors/Crop.cs +++ b/src/ImageProcessor/Processors/Crop.cs @@ -1,9 +1,12 @@ -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) James South. -// Licensed under the Apache License, Version 2.0. +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. // -// ----------------------------------------------------------------------- +// +// Crops an image to the given directions. +// +// -------------------------------------------------------------------------------------------------------------------- namespace ImageProcessor.Processors {