mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: 1ba1d6f6fdba545f9659d634cc9bb1e6e0833758 Former-commit-id: 192eccd79658cb84e31c22e23c0f4c9690dbe314af/merge-core
21 changed files with 775 additions and 804 deletions
@ -1,168 +0,0 @@ |
|||||
namespace ImageProcessor.Web.Caching |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.IO; |
|
||||
using System.Linq; |
|
||||
using System.Threading.Tasks; |
|
||||
using System.Web; |
|
||||
using System.Web.Hosting; |
|
||||
|
|
||||
using ImageProcessor.Web.Configuration; |
|
||||
using ImageProcessor.Web.Extensions; |
|
||||
|
|
||||
public class DiskCache2 : ImageCacheBase |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The maximum number of files allowed in the directory.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// NTFS directories can handle up to 10,000 files in the directory before slowing down.
|
|
||||
/// This will help us to ensure that don't go over that limit.
|
|
||||
/// <see href="http://stackoverflow.com/questions/197162/ntfs-performance-and-large-volumes-of-files-and-directories"/>
|
|
||||
/// <see href="http://stackoverflow.com/questions/115882/how-do-you-deal-with-lots-of-small-files"/>
|
|
||||
/// <see href="http://stackoverflow.com/questions/1638219/millions-of-small-graphics-files-and-how-to-overcome-slow-file-system-access-on"/>
|
|
||||
/// </remarks>
|
|
||||
private const int MaxFilesCount = 100; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The max age.
|
|
||||
/// </summary>
|
|
||||
private readonly int maxAge; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The virtual cache path.
|
|
||||
/// </summary>
|
|
||||
private readonly string virtualCachePath; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The absolute path to virtual cache path on the server.
|
|
||||
/// </summary>
|
|
||||
private readonly string absoluteCachePath; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The virtual cached path to the cached file.
|
|
||||
/// </summary>
|
|
||||
private string virtualCachedFilePath; |
|
||||
|
|
||||
public DiskCache2(string requestPath, string fullPath, string querystring) |
|
||||
: base(requestPath, fullPath, querystring) |
|
||||
{ |
|
||||
// TODO: Get from configuration.
|
|
||||
this.Settings = new Dictionary<string, string>(); |
|
||||
this.maxAge = Convert.ToInt32(this.Settings["MaxAge"]); |
|
||||
this.virtualCachePath = this.Settings["VirtualCachePath"]; |
|
||||
this.absoluteCachePath = HostingEnvironment.MapPath(this.virtualCachePath); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The maximum number of days to cache files on the system for.
|
|
||||
/// TODO: Shift the getter source to proper config.
|
|
||||
/// </summary>
|
|
||||
public override int MaxAge |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return this.maxAge; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public override async Task<bool> IsNewOrUpdatedAsync() |
|
||||
{ |
|
||||
string cachedFileName = await this.CreateCachedFileName(); |
|
||||
|
|
||||
// Collision rate of about 1 in 10000 for the folder structure.
|
|
||||
// That gives us massive scope to store millions of files.
|
|
||||
string pathFromKey = string.Join("\\", cachedFileName.ToCharArray().Take(6)); |
|
||||
string virtualPathFromKey = pathFromKey.Replace(@"\", "/"); |
|
||||
this.CachedPath = Path.Combine(this.absoluteCachePath, pathFromKey, cachedFileName); |
|
||||
this.virtualCachedFilePath = Path.Combine(this.virtualCachePath, virtualPathFromKey, cachedFileName).Replace(@"\", "/"); |
|
||||
|
|
||||
bool isUpdated = false; |
|
||||
CachedImage cachedImage = CacheIndexer.GetValue(this.CachedPath); |
|
||||
|
|
||||
if (cachedImage == null) |
|
||||
{ |
|
||||
// Nothing in the cache so we should return true.
|
|
||||
isUpdated = true; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// Check to see if the cached image is set to expire.
|
|
||||
if (this.IsExpired(cachedImage.CreationTimeUtc)) |
|
||||
{ |
|
||||
CacheIndexer.Remove(this.CachedPath); |
|
||||
isUpdated = true; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return isUpdated; |
|
||||
} |
|
||||
|
|
||||
public override async Task AddImageToCacheAsync(Stream stream) |
|
||||
{ |
|
||||
// ReSharper disable once AssignNullToNotNullAttribute
|
|
||||
DirectoryInfo directoryInfo = new DirectoryInfo(Path.GetDirectoryName(this.CachedPath)); |
|
||||
if (!directoryInfo.Exists) |
|
||||
{ |
|
||||
directoryInfo.Create(); |
|
||||
} |
|
||||
|
|
||||
using (FileStream fileStream = File.Create(this.CachedPath)) |
|
||||
{ |
|
||||
await stream.CopyToAsync(fileStream); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public override async Task TrimCacheAsync() |
|
||||
{ |
|
||||
string directory = Path.GetDirectoryName(this.CachedPath); |
|
||||
|
|
||||
if (directory != null) |
|
||||
{ |
|
||||
DirectoryInfo directoryInfo = new DirectoryInfo(directory); |
|
||||
DirectoryInfo parentDirectoryInfo = directoryInfo.Parent; |
|
||||
|
|
||||
if (parentDirectoryInfo != null) |
|
||||
{ |
|
||||
// UNC folders can throw exceptions if the file doesn't exist.
|
|
||||
foreach (DirectoryInfo enumerateDirectory in await parentDirectoryInfo.SafeEnumerateDirectoriesAsync()) |
|
||||
{ |
|
||||
IEnumerable<FileInfo> files = enumerateDirectory.EnumerateFiles().OrderBy(f => f.CreationTimeUtc); |
|
||||
int count = files.Count(); |
|
||||
|
|
||||
foreach (FileInfo fileInfo in files) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
// If the group count is equal to the max count minus 1 then we know we
|
|
||||
// have reduced the number of items below the maximum allowed.
|
|
||||
// We'll cleanup any orphaned expired files though.
|
|
||||
if (!this.IsExpired(fileInfo.CreationTimeUtc) && count <= MaxFilesCount - 1) |
|
||||
{ |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
// Remove from the cache and delete each CachedImage.
|
|
||||
CacheIndexer.Remove(fileInfo.Name); |
|
||||
fileInfo.Delete(); |
|
||||
count -= 1; |
|
||||
} |
|
||||
// ReSharper disable once EmptyGeneralCatchClause
|
|
||||
catch |
|
||||
{ |
|
||||
// Do nothing; skip to the next file.
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public override void RewritePath(HttpContext context) |
|
||||
{ |
|
||||
// The cached file is valid so just rewrite the path.
|
|
||||
context.RewritePath(this.virtualCachedFilePath, false); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1 +1,10 @@ |
|||||
<cache virtualPath="~/app_data/cache" maxDays="365"/> |
<caching currentCache="DiskCache"> |
||||
|
<caches> |
||||
|
<cache name="DiskCache" type="ImageProcessor.Web.Caching.DiskCache, ImageProcessor.Web"> |
||||
|
<settings> |
||||
|
<setting key="MaxAge" value="365"/> |
||||
|
<setting key="VirtualCachePath" value="~/app_data/cache"/> |
||||
|
</settings> |
||||
|
</cache> |
||||
|
</caches> |
||||
|
</caching> |
||||
|
|||||
@ -0,0 +1,56 @@ |
|||||
|
// --------------------------------------------------------------------------------------------------------------------
|
||||
|
// <copyright file="SettingElement.cs" company="James South">
|
||||
|
// Copyright (c) James South.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
// <summary>
|
||||
|
// Represents a SettingElement configuration element within the configuration.
|
||||
|
// </summary>
|
||||
|
// --------------------------------------------------------------------------------------------------------------------
|
||||
|
|
||||
|
namespace ImageProcessor.Web.Configuration |
||||
|
{ |
||||
|
using System.Configuration; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents a SettingElement configuration element within the configuration.
|
||||
|
/// </summary>
|
||||
|
public class SettingElement : ConfigurationElement |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets or sets the key of the plugin setting.
|
||||
|
/// </summary>
|
||||
|
/// <value>The key of the plugin setting.</value>
|
||||
|
[ConfigurationProperty("key", IsRequired = true, IsKey = true)] |
||||
|
public string Key |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return this["key"] as string; |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
this["key"] = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the value of the plugin setting.
|
||||
|
/// </summary>
|
||||
|
/// <value>The value of the plugin setting.</value>
|
||||
|
[ConfigurationProperty("value", IsRequired = true)] |
||||
|
public string Value |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return (string)this["value"]; |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
this["value"] = value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,117 @@ |
|||||
|
// --------------------------------------------------------------------------------------------------------------------
|
||||
|
// <copyright file="SettingElementCollection.cs" company="James South">
|
||||
|
// Copyright (c) James South.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
// <summary>
|
||||
|
// Represents a SettingElementCollection collection configuration element within the configuration.
|
||||
|
// </summary>
|
||||
|
// --------------------------------------------------------------------------------------------------------------------
|
||||
|
|
||||
|
namespace ImageProcessor.Web.Configuration |
||||
|
{ |
||||
|
using System.Configuration; |
||||
|
using System.Linq; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents a SettingElementCollection collection configuration element within the configuration.
|
||||
|
/// </summary>
|
||||
|
public class SettingElementCollection : ConfigurationElementCollection |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets the type of the <see cref="ConfigurationElementCollection"/>.
|
||||
|
/// </summary>
|
||||
|
/// <value>
|
||||
|
/// The <see cref="ConfigurationElementCollectionType"/> of this collection.
|
||||
|
/// </value>
|
||||
|
public override ConfigurationElementCollectionType CollectionType |
||||
|
{ |
||||
|
get { return ConfigurationElementCollectionType.BasicMap; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the name used to identify this collection of elements in the configuration file when overridden in a derived class.
|
||||
|
/// </summary>
|
||||
|
/// <value>
|
||||
|
/// The name of the collection; otherwise, an empty string. The default is an empty string.
|
||||
|
/// </value>
|
||||
|
protected override string ElementName |
||||
|
{ |
||||
|
get { return "setting"; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the <see cref="T:ImageProcessor.Web.Config.ImageSecuritySection.SettingElement"/>
|
||||
|
/// at the specified index within the collection.
|
||||
|
/// </summary>
|
||||
|
/// <param name="index">The index at which to get the specified object.</param>
|
||||
|
/// <returns>
|
||||
|
/// The <see cref="T:ImageProcessor.Web.Config.ImageSecuritySection.SettingElement"/>
|
||||
|
/// at the specified index within the collection.
|
||||
|
/// </returns>
|
||||
|
public SettingElement this[int index] |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return (SettingElement)BaseGet(index); |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
if (this.BaseGet(index) != null) |
||||
|
{ |
||||
|
this.BaseRemoveAt(index); |
||||
|
} |
||||
|
|
||||
|
this.BaseAdd(index, value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns the setting element with the specified key.
|
||||
|
/// </summary>
|
||||
|
/// <param name="key">the key representing the element</param>
|
||||
|
/// <returns>the setting element</returns>
|
||||
|
public new SettingElement this[string key] |
||||
|
{ |
||||
|
get { return (SettingElement)BaseGet(key); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns a value indicating whether the settings collection contains the
|
||||
|
/// given object.
|
||||
|
/// </summary>
|
||||
|
/// <param name="key">The key to identify the setting.</param>
|
||||
|
/// <returns>True if the collection contains the key; otherwise false.</returns>
|
||||
|
public bool ContainsKey(string key) |
||||
|
{ |
||||
|
object[] keys = BaseGetAllKeys(); |
||||
|
|
||||
|
return keys.Any(obj => (string)obj == key); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the element key for a specified PluginElement configuration element.
|
||||
|
/// </summary>
|
||||
|
/// <param name="element">
|
||||
|
/// The <see cref="ConfigurationElement">ConfigurationElement</see>
|
||||
|
/// to return the key for.
|
||||
|
/// </param>
|
||||
|
/// <returns>The element key for a specified PluginElement configuration element.</returns>
|
||||
|
protected override object GetElementKey(ConfigurationElement element) |
||||
|
{ |
||||
|
return ((SettingElement)element).Key; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a new SettingElement configuration element.
|
||||
|
/// </summary>
|
||||
|
/// <returns>
|
||||
|
/// A new SettingElement configuration element.
|
||||
|
/// </returns>
|
||||
|
protected override ConfigurationElement CreateNewElement() |
||||
|
{ |
||||
|
return new SettingElement(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,180 @@ |
|||||
|
// --------------------------------------------------------------------------------------------------------------------
|
||||
|
// <copyright file="TypeInitializationExtensions.cs" company="James South">
|
||||
|
// Copyright (c) James South.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
// <summary>
|
||||
|
// Extensions methods for <see cref="T:System.Type" /> for creating instances of types faster than
|
||||
|
// using reflection. Modified from the original class at.
|
||||
|
// <see href="http://geekswithblogs.net/mrsteve/archive/2012/02/19/a-fast-c-sharp-extension-method-using-expression-trees-create-instance-from-type-again.aspx" />
|
||||
|
// </summary>
|
||||
|
// --------------------------------------------------------------------------------------------------------------------
|
||||
|
|
||||
|
namespace ImageProcessor.Web.Extensions |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Concurrent; |
||||
|
using System.Linq; |
||||
|
using System.Linq.Expressions; |
||||
|
using System.Reflection; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Extensions methods for <see cref="T:System.Type"/> for creating instances of types faster than
|
||||
|
/// using reflection. Modified from the original class at.
|
||||
|
/// <see href="http://geekswithblogs.net/mrsteve/archive/2012/02/19/a-fast-c-sharp-extension-method-using-expression-trees-create-instance-from-type-again.aspx"/>
|
||||
|
/// </summary>
|
||||
|
internal static class TypeInitializationExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Returns an instance of the <paramref name="type"/> on which the method is invoked.
|
||||
|
/// </summary>
|
||||
|
/// <param name="type">The type on which the method was invoked.</param>
|
||||
|
/// <returns>An instance of the <paramref name="type"/>.</returns>
|
||||
|
public static object GetInstance(this Type type) |
||||
|
{ |
||||
|
// This is about as quick as it gets.
|
||||
|
return Activator.CreateInstance(type); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns an instance of the <paramref name="type"/> on which the method is invoked.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TArg">The type of the argument to pass to the constructor.</typeparam>
|
||||
|
/// <param name="type">The type on which the method was invoked.</param>
|
||||
|
/// <param name="argument">The argument to pass to the constructor.</param>
|
||||
|
/// <returns>An instance of the given <paramref name="type"/>.</returns>
|
||||
|
public static object GetInstance<TArg>(this Type type, TArg argument) |
||||
|
{ |
||||
|
return GetInstance<TArg, TypeToIgnore>(type, argument, null); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns an instance of the <paramref name="type"/> on which the method is invoked.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TArg1">The type of the first argument to pass to the constructor.</typeparam>
|
||||
|
/// <typeparam name="TArg2">The type of the second argument to pass to the constructor.</typeparam>
|
||||
|
/// <param name="type">The type on which the method was invoked.</param>
|
||||
|
/// <param name="argument1">The first argument to pass to the constructor.</param>
|
||||
|
/// <param name="argument2">The second argument to pass to the constructor.</param>
|
||||
|
/// <returns>An instance of the given <paramref name="type"/>.</returns>
|
||||
|
public static object GetInstance<TArg1, TArg2>(this Type type, TArg1 argument1, TArg2 argument2) |
||||
|
{ |
||||
|
return GetInstance<TArg1, TArg2, TypeToIgnore>(type, argument1, argument2, null); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns an instance of the <paramref name="type"/> on which the method is invoked.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TArg1">The type of the first argument to pass to the constructor.</typeparam>
|
||||
|
/// <typeparam name="TArg2">The type of the second argument to pass to the constructor.</typeparam>
|
||||
|
/// <typeparam name="TArg3">The type of the third argument to pass to the constructor.</typeparam>
|
||||
|
/// <param name="type">The type on which the method was invoked.</param>
|
||||
|
/// <param name="argument1">The first argument to pass to the constructor.</param>
|
||||
|
/// <param name="argument2">The second argument to pass to the constructor.</param>
|
||||
|
/// <param name="argument3">The third argument to pass to the constructor.</param>
|
||||
|
/// <returns>An instance of the given <paramref name="type"/>.</returns>
|
||||
|
public static object GetInstance<TArg1, TArg2, TArg3>( |
||||
|
this Type type, |
||||
|
TArg1 argument1, |
||||
|
TArg2 argument2, |
||||
|
TArg3 argument3) |
||||
|
{ |
||||
|
return InstanceCreationFactory<TArg1, TArg2, TArg3> |
||||
|
.CreateInstanceOf(type, argument1, argument2, argument3); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The instance creation factory for creating instances.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TArg1">The type of the first argument to pass to the constructor.</typeparam>
|
||||
|
/// <typeparam name="TArg2">The type of the second argument to pass to the constructor.</typeparam>
|
||||
|
/// <typeparam name="TArg3">The type of the third argument to pass to the constructor.</typeparam>
|
||||
|
private static class InstanceCreationFactory<TArg1, TArg2, TArg3> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// This dictionary will hold a cache of object-creation functions, keyed by the Type to create:
|
||||
|
/// </summary>
|
||||
|
private static readonly ConcurrentDictionary<Type, Func<TArg1, TArg2, TArg3, object>> InstanceCreationMethods = new ConcurrentDictionary<Type, Func<TArg1, TArg2, TArg3, object>>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The create instance of.
|
||||
|
/// </summary>
|
||||
|
/// <param name="type">
|
||||
|
/// The type.
|
||||
|
/// </param>
|
||||
|
/// <param name="arg1">The first argument to pass to the constructor.</param>
|
||||
|
/// <param name="arg2">The second argument to pass to the constructor.</param>
|
||||
|
/// <param name="arg3">The third argument to pass to the constructor.</param>
|
||||
|
/// <returns>
|
||||
|
/// The <see cref="object"/>.
|
||||
|
/// </returns>
|
||||
|
public static object CreateInstanceOf(Type type, TArg1 arg1, TArg2 arg2, TArg3 arg3) |
||||
|
{ |
||||
|
CacheInstanceCreationMethodIfRequired(type); |
||||
|
|
||||
|
return InstanceCreationMethods[type].Invoke(arg1, arg2, arg3); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Caches the instance creation method.
|
||||
|
/// </summary>
|
||||
|
/// <param name="type">
|
||||
|
/// The <see cref="Type"/> who's constructor to cache.
|
||||
|
/// </param>
|
||||
|
private static void CacheInstanceCreationMethodIfRequired(Type type) |
||||
|
{ |
||||
|
// Bail out if we've already cached the instance creation method:
|
||||
|
Func<TArg1, TArg2, TArg3, object> cached; |
||||
|
if (InstanceCreationMethods.TryGetValue(type, out cached)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
Type[] argumentTypes = { typeof(TArg1), typeof(TArg2), typeof(TArg3) }; |
||||
|
|
||||
|
// Get a collection of the constructor argument Types we've been given; ignore any
|
||||
|
// arguments which are of the 'ignore this' Type:
|
||||
|
Type[] constructorArgumentTypes = argumentTypes.Where(t => t != typeof(TypeToIgnore)).ToArray(); |
||||
|
|
||||
|
// Get the Constructor which matches the given argument Types:
|
||||
|
ConstructorInfo constructor = type.GetConstructor( |
||||
|
BindingFlags.Instance | BindingFlags.Public, |
||||
|
null, |
||||
|
CallingConventions.HasThis, |
||||
|
constructorArgumentTypes, |
||||
|
new ParameterModifier[0]); |
||||
|
|
||||
|
// Get a set of Expressions representing the parameters which will be passed to the Func:
|
||||
|
ParameterExpression[] lamdaParameterExpressions = |
||||
|
{ |
||||
|
Expression.Parameter(typeof(TArg1), "param1"), |
||||
|
Expression.Parameter(typeof(TArg2), "param2"), |
||||
|
Expression.Parameter(typeof(TArg3), "param3") |
||||
|
}; |
||||
|
|
||||
|
// Get a set of Expressions representing the parameters which will be passed to the constructor:
|
||||
|
ParameterExpression[] constructorParameterExpressions = |
||||
|
lamdaParameterExpressions.Take(constructorArgumentTypes.Length).ToArray(); |
||||
|
|
||||
|
// Get an Expression representing the constructor call, passing in the constructor parameters:
|
||||
|
NewExpression constructorCallExpression = Expression.New(constructor, constructorParameterExpressions.Cast<Expression>()); |
||||
|
|
||||
|
// Compile the Expression into a Func which takes three arguments and returns the constructed object:
|
||||
|
Func<TArg1, TArg2, TArg3, object> constructorCallingLambda = |
||||
|
Expression.Lambda<Func<TArg1, TArg2, TArg3, object>>( |
||||
|
constructorCallExpression, |
||||
|
lamdaParameterExpressions).Compile(); |
||||
|
|
||||
|
InstanceCreationMethods.TryAdd(type, constructorCallingLambda); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// To allow for overloads with differing numbers of arguments, we flag arguments which should be
|
||||
|
/// ignored by using this Type:
|
||||
|
/// </summary>
|
||||
|
private class TypeToIgnore |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
namespace ImageProcessor.Web.Extensions |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Linq.Expressions; |
||||
|
|
||||
|
internal static class TypePropertyHelpers |
||||
|
{ |
||||
|
public static string GetPropertyName<T>(Expression<Func<T>> expression) |
||||
|
{ |
||||
|
MemberExpression member = expression.Body as MemberExpression; |
||||
|
if (member != null) |
||||
|
{ |
||||
|
return member.Member.Name; |
||||
|
} |
||||
|
|
||||
|
throw new ArgumentException("expression"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,2 @@ |
|||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> |
||||
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=configuration_005Cshared/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> |
||||
@ -1,3 +1,11 @@ |
|||||
<?xml version="1.0" encoding="utf-8" ?> |
<caching currentCache="DiskCache"> |
||||
<cache virtualPath="~/app_data/cache" maxDays="56"/> |
<caches> |
||||
|
<cache name="DiskCache" type="ImageProcessor.Web.Caching.DiskCache, ImageProcessor.Web"> |
||||
|
<settings> |
||||
|
<setting key="MaxAge" value="56"/> |
||||
|
<setting key="VirtualCachePath" value="~/app_data/cache"/> |
||||
|
</settings> |
||||
|
</cache> |
||||
|
</caches> |
||||
|
</caching> |
||||
|
|
||||
|
|||||
Loading…
Reference in new issue