mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Building out caching
Passing along signal of VPP file monitoring Expiring results as appropriate in AvailableExtensions --HG-- branch : dev
This commit is contained in:
47
src/Orchard.Tests/Caching/CacheScopeTests.cs
Normal file
47
src/Orchard.Tests/Caching/CacheScopeTests.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Caching;
|
||||
using Orchard.Environment;
|
||||
using Autofac;
|
||||
using Orchard.Environment.FileSystems;
|
||||
using Orchard.Services;
|
||||
|
||||
namespace Orchard.Tests.Caching {
|
||||
[TestFixture]
|
||||
public class CacheScopeTests {
|
||||
private IContainer _hostContainer;
|
||||
|
||||
[SetUp]
|
||||
public void Init() {
|
||||
_hostContainer = OrchardStarter.CreateHostContainer(builder => {
|
||||
builder.RegisterType<Alpha>().InstancePerDependency();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public class Alpha {
|
||||
public ICacheManager CacheManager { get; set; }
|
||||
|
||||
public Alpha(ICacheManager cacheManager) {
|
||||
CacheManager = cacheManager;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComponentsAtHostLevelHaveAccessToCache() {
|
||||
var alpha = _hostContainer.Resolve<Alpha>();
|
||||
Assert.That(alpha.CacheManager, Is.Not.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HostLevelHasAccessToGlobalVolatileProviders() {
|
||||
Assert.That(_hostContainer.Resolve<IVirtualPathProvider>(), Is.Not.Null);
|
||||
Assert.That(_hostContainer.Resolve<IAppDataFolder>(), Is.Not.Null);
|
||||
Assert.That(_hostContainer.Resolve<IClock>(), Is.Not.Null);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
using Autofac;
|
||||
using System;
|
||||
using Autofac;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Caching;
|
||||
|
||||
@@ -11,9 +12,12 @@ namespace Orchard.Tests.Caching {
|
||||
[SetUp]
|
||||
public void Init() {
|
||||
var builder = new ContainerBuilder();
|
||||
builder.RegisterModule(new CacheModule());
|
||||
builder.RegisterType<DefaultCacheManager>().As<ICacheManager>();
|
||||
builder.RegisterType<DefaultCacheHolder>().As<ICacheHolder>().SingleInstance();
|
||||
builder.RegisterType<DefaultCacheManager>().As<ICacheManager>();
|
||||
_container = builder.Build();
|
||||
_cacheManager = _container.Resolve<ICacheManager>();
|
||||
_cacheManager = _container.Resolve<ICacheManager>(new TypedParameter(typeof(Type), GetType()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -28,5 +32,66 @@ namespace Orchard.Tests.Caching {
|
||||
var result = _cacheManager.Get("testItem", ctx => "");
|
||||
Assert.That(result, Is.EqualTo("testResult"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CacheModuleProvidesTypeSpecificManager() {
|
||||
var scope = _container.BeginLifetimeScope(builder => {
|
||||
builder.RegisterModule(new CacheModule());
|
||||
builder.RegisterType<ComponentOne>();
|
||||
builder.RegisterType<ComponentTwo>();
|
||||
});
|
||||
|
||||
var c1 = scope.Resolve<ComponentOne>();
|
||||
var c2 = scope.Resolve<ComponentTwo>();
|
||||
var w1a = c1.CacheManager.Get("hello", ctx => "world1");
|
||||
var w1b = c1.CacheManager.Get("hello", ctx => "world2");
|
||||
var w2a = c2.CacheManager.Get("hello", ctx => "world3");
|
||||
var w2b = c2.CacheManager.Get("hello", ctx => "world4");
|
||||
|
||||
Assert.That(w1a, Is.EqualTo("world1"));
|
||||
Assert.That(w1b, Is.EqualTo("world1"));
|
||||
Assert.That(w2a, Is.EqualTo("world3"));
|
||||
Assert.That(w2b, Is.EqualTo("world3"));
|
||||
|
||||
var c3 = scope.Resolve<ComponentOne>();
|
||||
var c4 = scope.Resolve<ComponentTwo>();
|
||||
var w3a = c3.CacheManager.Get("hello", ctx => "world5");
|
||||
var w3b = c3.CacheManager.Get("hello", ctx => "world6");
|
||||
var w4a = c4.CacheManager.Get("hello", ctx => "world7");
|
||||
var w4b = c4.CacheManager.Get("hello", ctx => "world8");
|
||||
|
||||
Assert.That(w3a, Is.EqualTo("world1"));
|
||||
Assert.That(w3b, Is.EqualTo("world1"));
|
||||
Assert.That(w4a, Is.EqualTo("world3"));
|
||||
Assert.That(w4b, Is.EqualTo("world3"));
|
||||
|
||||
Assert.That(c1.CacheManager,
|
||||
Is.Not.SameAs(c3.CacheManager));
|
||||
|
||||
Assert.That(c1.CacheManager.GetCache<string, string>(),
|
||||
Is.SameAs(c3.CacheManager.GetCache<string, string>()));
|
||||
|
||||
Assert.That(c1.CacheManager,
|
||||
Is.Not.SameAs(c2.CacheManager));
|
||||
|
||||
Assert.That(c1.CacheManager.GetCache<string, string>(),
|
||||
Is.Not.SameAs(c2.CacheManager.GetCache<string, string>()));
|
||||
}
|
||||
|
||||
class ComponentOne {
|
||||
public ICacheManager CacheManager { get; set; }
|
||||
|
||||
public ComponentOne(ICacheManager cacheManager) {
|
||||
CacheManager = cacheManager;
|
||||
}
|
||||
}
|
||||
|
||||
class ComponentTwo {
|
||||
public ICacheManager CacheManager { get; set; }
|
||||
|
||||
public ComponentTwo(ICacheManager cacheManager) {
|
||||
CacheManager = cacheManager;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,7 +1,6 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Environment.FileSystems;
|
||||
|
||||
namespace Orchard.Tests.Environment.Configuration {
|
||||
|
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Environment.FileSystems;
|
||||
|
||||
namespace Orchard.Tests.Environment.Configuration {
|
||||
[TestFixture]
|
||||
|
@@ -3,7 +3,6 @@ using System.Runtime.Serialization;
|
||||
using System.Xml;
|
||||
using Autofac;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Environment.FileSystems;
|
||||
using Orchard.Environment.Topology;
|
||||
using Orchard.Environment.Topology.Models;
|
||||
|
@@ -125,6 +125,7 @@
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Caching\CacheScopeTests.cs" />
|
||||
<Compile Include="Caching\CacheTests.cs" />
|
||||
<Compile Include="Commands\CommandHandlerDescriptorBuilderTests.cs" />
|
||||
<Compile Include="Commands\CommandHandlerTests.cs" />
|
||||
|
@@ -16,5 +16,6 @@ namespace Orchard.Tests.Stubs {
|
||||
public DateTime FutureMoment(TimeSpan span) {
|
||||
return UtcNow.Add(span);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -393,7 +393,7 @@
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>False</AutoAssignPort>
|
||||
<DevelopmentServerPort>80</DevelopmentServerPort>
|
||||
<DevelopmentServerPort>30320</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>
|
||||
</IISUrl>
|
||||
|
@@ -1,14 +1,13 @@
|
||||
using System;
|
||||
using Orchard.Caching.Providers;
|
||||
|
||||
namespace Orchard.Caching {
|
||||
public class AcquireContext<TKey> {
|
||||
public AcquireContext(TKey key, Action<IVolatileSignal> monitor) {
|
||||
public AcquireContext(TKey key, Action<IVolatileToken> monitor) {
|
||||
Key = key;
|
||||
IsInvalid = monitor;
|
||||
Monitor = monitor;
|
||||
}
|
||||
|
||||
public TKey Key { get; private set; }
|
||||
public Action<IVolatileSignal> IsInvalid { get; private set; }
|
||||
public Action<IVolatileToken> Monitor { get; private set; }
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Caching.Providers;
|
||||
using System.Linq;
|
||||
|
||||
namespace Orchard.Caching {
|
||||
public class Cache<TKey, TResult> : ICache<TKey, TResult> {
|
||||
@@ -12,19 +12,19 @@ namespace Orchard.Caching {
|
||||
|
||||
public TResult Get(TKey key, Func<AcquireContext<TKey>, TResult> acquire) {
|
||||
CacheEntry entry;
|
||||
if (!_entries.TryGetValue(key, out entry)) {
|
||||
entry = new CacheEntry { VolatileItems = new List<IVolatileSignal>() };
|
||||
if (!_entries.TryGetValue(key, out entry) || entry.Tokens.Any(t => !t.IsCurrent)) {
|
||||
entry = new CacheEntry { Tokens = new List<IVolatileToken>() };
|
||||
|
||||
var context = new AcquireContext<TKey>(key, volatileItem => entry.VolatileItems.Add(volatileItem));
|
||||
var context = new AcquireContext<TKey>(key, volatileItem => entry.Tokens.Add(volatileItem));
|
||||
entry.Result = acquire(context);
|
||||
_entries.Add(key, entry);
|
||||
_entries[key] = entry;
|
||||
}
|
||||
return entry.Result;
|
||||
}
|
||||
|
||||
private class CacheEntry {
|
||||
public TResult Result { get; set; }
|
||||
public IList<IVolatileSignal> VolatileItems { get; set; }
|
||||
public IList<IVolatileToken> Tokens { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
29
src/Orchard/Caching/CacheModule.cs
Normal file
29
src/Orchard/Caching/CacheModule.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Autofac;
|
||||
|
||||
namespace Orchard.Caching {
|
||||
public class CacheModule : Module {
|
||||
protected override void Load(ContainerBuilder builder) {
|
||||
builder.RegisterType<DefaultCacheManager>()
|
||||
.As<ICacheManager>()
|
||||
.InstancePerDependency();
|
||||
}
|
||||
|
||||
protected override void AttachToComponentRegistration(Autofac.Core.IComponentRegistry componentRegistry, Autofac.Core.IComponentRegistration registration) {
|
||||
var needsCacheManager = registration.Activator.LimitType
|
||||
.GetConstructors()
|
||||
.Any(x => x.GetParameters()
|
||||
.Any(xx => xx.ParameterType == typeof(ICacheManager)));
|
||||
|
||||
if (needsCacheManager) {
|
||||
registration.Preparing += (sender, e) => {
|
||||
var parameter = new TypedParameter(
|
||||
typeof(ICacheManager),
|
||||
e.Context.Resolve<ICacheManager>(new TypedParameter(typeof(Type), registration.Activator.LimitType)));
|
||||
e.Parameters = e.Parameters.Concat(new[] { parameter });
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
src/Orchard/Caching/DefaultCacheHolder.cs
Normal file
27
src/Orchard/Caching/DefaultCacheHolder.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Castle.Core;
|
||||
|
||||
namespace Orchard.Caching {
|
||||
public class DefaultCacheHolder : ICacheHolder {
|
||||
private readonly IDictionary<CacheKey, object> _caches = new Dictionary<CacheKey, object>();
|
||||
|
||||
class CacheKey : Pair<Type, Pair<Type, Type>> {
|
||||
public CacheKey(Type component, Type key, Type result)
|
||||
: base(component, new Pair<Type, Type>(key, result)) {
|
||||
}
|
||||
}
|
||||
|
||||
public ICache<TKey, TResult> GetCache<TKey, TResult>(Type component) {
|
||||
var cacheKey = new CacheKey(component, typeof(TKey), typeof(TResult));
|
||||
lock (_caches) {
|
||||
object value;
|
||||
if (!_caches.TryGetValue(cacheKey, out value)) {
|
||||
value = new Cache<TKey, TResult>();
|
||||
_caches[cacheKey] = value;
|
||||
}
|
||||
return (ICache<TKey, TResult>)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,32 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Castle.Core;
|
||||
|
||||
namespace Orchard.Caching {
|
||||
public class DefaultCacheManager : ICacheManager {
|
||||
private readonly Dictionary<Pair<Type, Type>, object> _caches;
|
||||
private readonly Type _component;
|
||||
private readonly ICacheHolder _cacheHolder;
|
||||
|
||||
public DefaultCacheManager() {
|
||||
_caches = new Dictionary<Pair<Type, Type>, object>();
|
||||
public DefaultCacheManager(Type component, ICacheHolder cacheHolder) {
|
||||
_component = component;
|
||||
_cacheHolder = cacheHolder;
|
||||
}
|
||||
|
||||
#region Implementation of ICacheManager
|
||||
public ICache<TKey, TResult> GetCache<TKey, TResult>() {
|
||||
return _cacheHolder.GetCache<TKey, TResult>(_component);
|
||||
}
|
||||
|
||||
public TResult Get<TKey, TResult>(TKey key, Func<AcquireContext<TKey>, TResult> acquire) {
|
||||
return GetCache<TKey, TResult>().Get(key, acquire);
|
||||
}
|
||||
|
||||
public ICache<TKey, TResult> GetCache<TKey, TResult>() {
|
||||
var cacheKey = new Pair<Type, Type>(typeof(TKey), typeof(TResult));
|
||||
object value;
|
||||
if (!_caches.TryGetValue(cacheKey, out value)) {
|
||||
value = new Cache<TKey, TResult>();
|
||||
_caches.Add(cacheKey, value);
|
||||
}
|
||||
|
||||
return (ICache<TKey, TResult>)value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
7
src/Orchard/Caching/ICacheHolder.cs
Normal file
7
src/Orchard/Caching/ICacheHolder.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using System;
|
||||
|
||||
namespace Orchard.Caching {
|
||||
public interface ICacheHolder : ISingletonDependency {
|
||||
ICache<TKey, TResult> GetCache<TKey, TResult>(Type component);
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
|
||||
namespace Orchard.Caching {
|
||||
public interface ICacheManager : ISingletonDependency {
|
||||
public interface ICacheManager {
|
||||
TResult Get<TKey, TResult>(TKey key, Func<AcquireContext<TKey>, TResult> acquire);
|
||||
ICache<TKey, TResult> GetCache<TKey, TResult>();
|
||||
}
|
||||
|
4
src/Orchard/Caching/IVolatileProvider.cs
Normal file
4
src/Orchard/Caching/IVolatileProvider.cs
Normal file
@@ -0,0 +1,4 @@
|
||||
namespace Orchard.Caching {
|
||||
public interface IVolatileProvider : ISingletonDependency {
|
||||
}
|
||||
}
|
5
src/Orchard/Caching/IVolatileToken.cs
Normal file
5
src/Orchard/Caching/IVolatileToken.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace Orchard.Caching {
|
||||
public interface IVolatileToken {
|
||||
bool IsCurrent { get; }
|
||||
}
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
namespace Orchard.Caching.Providers {
|
||||
public interface IVolatileProvider : IDependency {
|
||||
void Enlist(IVolatileSink sink);
|
||||
}
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
namespace Orchard.Caching.Providers {
|
||||
public interface IVolatileSignal {
|
||||
IVolatileProvider Provider { get; set; }
|
||||
}
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
namespace Orchard.Caching.Providers {
|
||||
public interface IVolatileSink {
|
||||
|
||||
}
|
||||
}
|
23
src/Orchard/Caching/Weak.cs
Normal file
23
src/Orchard/Caching/Weak.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Orchard.Caching {
|
||||
public class Weak<T> : WeakReference {
|
||||
public Weak(T target)
|
||||
: base(target) {
|
||||
}
|
||||
|
||||
public Weak(T target, bool trackResurrection)
|
||||
: base(target, trackResurrection) {
|
||||
}
|
||||
|
||||
protected Weak(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context) {
|
||||
}
|
||||
|
||||
public new T Target {
|
||||
get { return (T)base.Target; }
|
||||
set { base.Target = value; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -24,7 +24,7 @@ namespace Orchard.Environment.Extensions.Folders {
|
||||
private readonly ICacheManager _cacheManager;
|
||||
private readonly IVirtualPathProvider _virtualPathProvider;
|
||||
|
||||
public ExtensionFolders(
|
||||
protected ExtensionFolders(
|
||||
IEnumerable<string> paths,
|
||||
string manifestName,
|
||||
bool manifestIsOptional,
|
||||
@@ -39,28 +39,39 @@ namespace Orchard.Environment.Extensions.Folders {
|
||||
}
|
||||
|
||||
public IEnumerable<ExtensionDescriptor> AvailableExtensions() {
|
||||
var virtualPaths = _cacheManager.Get("", ctx => {
|
||||
var x = 5;
|
||||
return new[] { "hello" };
|
||||
});
|
||||
|
||||
|
||||
var list = new List<ExtensionDescriptor>();
|
||||
foreach (var virtualPath in virtualPaths) {
|
||||
list.Add(GetExtensionDescriptor(virtualPath));
|
||||
foreach (var locationPath in _paths) {
|
||||
var subfolderPaths = _cacheManager.Get(locationPath, ctx => {
|
||||
ctx.Monitor(_virtualPathProvider.WhenPathChanges(ctx.Key));
|
||||
return _virtualPathProvider.GetSubfolderPaths(ctx.Key);
|
||||
});
|
||||
foreach (var subfolderPath in subfolderPaths) {
|
||||
var extensionName = Path.GetFileName(subfolderPath.TrimEnd('/', '\\'));
|
||||
var manifestPath = Path.Combine(subfolderPath, _manifestName);
|
||||
var descriptor = GetExtensionDescriptor(locationPath, extensionName, manifestPath);
|
||||
if (descriptor != null)
|
||||
list.Add(descriptor);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
ExtensionDescriptor GetExtensionDescriptor(string virtualPath) {
|
||||
return _cacheManager.Get(virtualPath, context => {
|
||||
ExtensionDescriptor GetExtensionDescriptor(string locationPath, string extensionName, string manifestPath) {
|
||||
return _cacheManager.Get(manifestPath, context => {
|
||||
|
||||
context.IsInvalid(_virtualPathProvider.WhenPathChanges(virtualPath));
|
||||
context.Monitor(_virtualPathProvider.WhenPathChanges(manifestPath));
|
||||
|
||||
var text = _virtualPathProvider.ReadAllText(virtualPath);
|
||||
var manifestText = _virtualPathProvider.ReadAllText(manifestPath);
|
||||
if (manifestText == null) {
|
||||
if (_manifestIsOptional) {
|
||||
manifestText = string.Format("name: {0}", extensionName);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
var parseResult = ParseManifest(text);
|
||||
return GetDescriptorForExtension(parseResult.Name, parseResult);
|
||||
return GetDescriptorForExtension(locationPath, extensionName, ParseManifest(manifestText));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -78,52 +89,29 @@ namespace Orchard.Environment.Extensions.Folders {
|
||||
}
|
||||
|
||||
public ParseResult ParseManifest(string name) {
|
||||
foreach (var path in _paths) {
|
||||
if (!Directory.Exists(PathHelpers.GetPhysicalPath(path)))
|
||||
continue;
|
||||
|
||||
var extensionDirectoryPath = Path.Combine(PathHelpers.GetPhysicalPath(path), name);
|
||||
if (!Directory.Exists(PathHelpers.GetPhysicalPath(extensionDirectoryPath)))
|
||||
continue;
|
||||
|
||||
var extensionManifestPath = Path.Combine(extensionDirectoryPath, _manifestName);
|
||||
|
||||
if (File.Exists(extensionManifestPath)) {
|
||||
var yamlStream = YamlParser.Load(extensionManifestPath);
|
||||
return new ParseResult {
|
||||
Location = path,
|
||||
Name = name,
|
||||
YamlDocument = yamlStream.Documents.Single()
|
||||
};
|
||||
}
|
||||
|
||||
if (_manifestIsOptional) {
|
||||
var yamlInput = new TextInput(string.Format("name: {0}", name));
|
||||
var parser = new YamlParser();
|
||||
bool success;
|
||||
var yamlStream = parser.ParseYamlStream(yamlInput, out success);
|
||||
return new ParseResult {
|
||||
Location = path,
|
||||
Name = name,
|
||||
YamlDocument = yamlStream.Documents.Single()
|
||||
};
|
||||
}
|
||||
bool success;
|
||||
var yamlStream = new YamlParser().ParseYamlStream(new TextInput(name), out success);
|
||||
if (yamlStream == null || !success) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
return new ParseResult {
|
||||
Name = name,
|
||||
YamlDocument = yamlStream.Documents.Single()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private ExtensionDescriptor GetDescriptorForExtension(string name, ParseResult parseResult) {
|
||||
private ExtensionDescriptor GetDescriptorForExtension(string locationPath, string extensionName, ParseResult parseResult) {
|
||||
var mapping = (Mapping)parseResult.YamlDocument.Root;
|
||||
var fields = mapping.Entities
|
||||
.Where(x => x.Key is Scalar)
|
||||
.ToDictionary(x => ((Scalar)x.Key).Text, x => x.Value);
|
||||
|
||||
var extensionDescriptor = new ExtensionDescriptor {
|
||||
Location = parseResult.Location,
|
||||
Name = name,
|
||||
Location = locationPath,
|
||||
Name = extensionName,
|
||||
ExtensionType = _extensionType,
|
||||
DisplayName = GetValue(fields, "name") ?? name,
|
||||
DisplayName = GetValue(fields, "name") ?? extensionName,
|
||||
Description = GetValue(fields, "description"),
|
||||
Version = GetValue(fields, "version"),
|
||||
OrchardVersion = GetValue(fields, "orchardversion"),
|
||||
|
@@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web.Hosting;
|
||||
using Orchard.Environment.FileSystems;
|
||||
|
||||
namespace Orchard.Environment.Configuration {
|
||||
namespace Orchard.Environment.FileSystems {
|
||||
public class AppDataFolder : IAppDataFolder {
|
||||
protected string _basePath;
|
||||
|
||||
@@ -42,9 +40,9 @@ namespace Orchard.Environment.Configuration {
|
||||
var files = Directory.GetFiles(directoryPath);
|
||||
|
||||
return files.Select(file => {
|
||||
var fileName = Path.GetFileName(file);
|
||||
return Path.Combine(path, fileName);
|
||||
});
|
||||
var fileName = Path.GetFileName(file);
|
||||
return Path.Combine(path, fileName);
|
||||
});
|
||||
}
|
||||
|
||||
public IEnumerable<string> ListDirectories(string path) {
|
||||
@@ -55,9 +53,9 @@ namespace Orchard.Environment.Configuration {
|
||||
var files = Directory.GetDirectories(directoryPath);
|
||||
|
||||
return files.Select(file => {
|
||||
var fileName = Path.GetFileName(file);
|
||||
return Path.Combine(path, fileName);
|
||||
});
|
||||
var fileName = Path.GetFileName(file);
|
||||
return Path.Combine(path, fileName);
|
||||
});
|
||||
}
|
||||
|
||||
public string CreateDirectory(string path) {
|
||||
@@ -74,5 +72,6 @@ namespace Orchard.Environment.Configuration {
|
||||
public string MapPath(string path) {
|
||||
return Path.Combine(_basePath, path);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,54 +1,127 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web.Caching;
|
||||
using System.Web.Hosting;
|
||||
using Orchard.Caching.Providers;
|
||||
using Orchard.Caching;
|
||||
using Orchard.Services;
|
||||
|
||||
namespace Orchard.Environment.FileSystems {
|
||||
public class DefaultVirtualPathProvider : IVirtualPathProvider {
|
||||
private readonly IClock _clock;
|
||||
private readonly IList<IVolatileSink> _sinks = new List<IVolatileSink>();
|
||||
//private string _cachePrefix = Guid.NewGuid().ToString("n");
|
||||
private readonly Thunk _thunk;
|
||||
private readonly string _prefix = Guid.NewGuid().ToString("n");
|
||||
private readonly IDictionary<string, Weak<Token>> _tokens = new Dictionary<string, Weak<Token>>();
|
||||
|
||||
|
||||
public DefaultVirtualPathProvider(IClock clock) {
|
||||
_clock = clock;
|
||||
_thunk = new Thunk(this);
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetSubfolderPaths(string virtualPath) {
|
||||
if (!HostingEnvironment.VirtualPathProvider.DirectoryExists(virtualPath))
|
||||
return Enumerable.Empty<string>();
|
||||
|
||||
return HostingEnvironment.VirtualPathProvider
|
||||
.GetDirectory(virtualPath)
|
||||
.Directories.OfType<VirtualDirectory>()
|
||||
.Select(d => d.VirtualPath)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public string ReadAllText(string virtualPath) {
|
||||
if (!HostingEnvironment.VirtualPathProvider.FileExists(virtualPath))
|
||||
return null;
|
||||
|
||||
using (var stream = VirtualPathProvider.OpenFile(virtualPath)) {
|
||||
using (var reader = new StreamReader(stream)) {
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
//var cd = HostingEnvironment.VirtualPathProvider.. .GetCacheDependency(virtualPath, null, _clock.UtcNow);
|
||||
//HostingEnvironment.Cache.Add(
|
||||
// _cachePrefix + virtualPath,
|
||||
// virtualPath,
|
||||
// cd,
|
||||
// NoAbsoluteExpiration,
|
||||
// );
|
||||
}
|
||||
|
||||
public IVolatileSignal WhenPathChanges(string virtualPath) {
|
||||
|
||||
return new VirtualPathSignal(this, virtualPath);
|
||||
public IVolatileToken WhenPathChanges(string virtualPath) {
|
||||
var token = BindToken(virtualPath);
|
||||
BindSignal(virtualPath);
|
||||
return token;
|
||||
}
|
||||
|
||||
private Token BindToken(string virtualPath) {
|
||||
lock (_tokens) {
|
||||
Weak<Token> weak;
|
||||
if (!_tokens.TryGetValue(virtualPath, out weak)) {
|
||||
weak = new Weak<Token>(new Token(virtualPath));
|
||||
_tokens[virtualPath] = weak;
|
||||
}
|
||||
|
||||
class VirtualPathSignal : IVolatileSignal {
|
||||
private readonly string _virtualPath;
|
||||
var token = weak.Target;
|
||||
if (token == null) {
|
||||
token = new Token(virtualPath);
|
||||
weak.Target = token;
|
||||
}
|
||||
|
||||
public VirtualPathSignal(DefaultVirtualPathProvider provider, string virtualPath) {
|
||||
_virtualPath = virtualPath;
|
||||
Provider = provider;
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
private Token DetachToken(string virtualPath) {
|
||||
lock (_tokens) {
|
||||
Weak<Token> weak;
|
||||
if (!_tokens.TryGetValue(virtualPath, out weak)) {
|
||||
return null;
|
||||
}
|
||||
var token = weak.Target;
|
||||
weak.Target = null;
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
private void BindSignal(string virtualPath) {
|
||||
var cacheDependency = HostingEnvironment.VirtualPathProvider.GetCacheDependency(
|
||||
virtualPath,
|
||||
new[] { virtualPath },
|
||||
_clock.UtcNow);
|
||||
|
||||
HostingEnvironment.Cache.Add(
|
||||
_prefix + virtualPath,
|
||||
virtualPath,
|
||||
cacheDependency,
|
||||
Cache.NoAbsoluteExpiration,
|
||||
Cache.NoSlidingExpiration,
|
||||
CacheItemPriority.NotRemovable,
|
||||
_thunk.Signal);
|
||||
}
|
||||
|
||||
public void Signal(string key, object value, CacheItemRemovedReason reason) {
|
||||
var virtualPath = Convert.ToString(value);
|
||||
var token = DetachToken(virtualPath);
|
||||
if (token != null)
|
||||
token.IsCurrent = false;
|
||||
}
|
||||
|
||||
public class Token : IVolatileToken {
|
||||
public Token(string virtualPath) {
|
||||
IsCurrent = true;
|
||||
VirtualPath = virtualPath;
|
||||
}
|
||||
public bool IsCurrent { get; set; }
|
||||
public string VirtualPath { get; private set; }
|
||||
}
|
||||
|
||||
class Thunk {
|
||||
private readonly Weak<DefaultVirtualPathProvider> _weak;
|
||||
|
||||
public Thunk(DefaultVirtualPathProvider provider) {
|
||||
_weak = new Weak<DefaultVirtualPathProvider>(provider);
|
||||
}
|
||||
|
||||
public IVolatileProvider Provider { get; set; }
|
||||
}
|
||||
|
||||
public void Enlist(IVolatileSink sink) {
|
||||
_sinks.Add(sink);
|
||||
public void Signal(string key, object value, CacheItemRemovedReason reason) {
|
||||
var provider = _weak.Target;
|
||||
if (provider != null)
|
||||
provider.Signal(key, value, reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Caching;
|
||||
|
||||
namespace Orchard.Environment.FileSystems {
|
||||
/// <summary>
|
||||
@@ -6,7 +7,7 @@ namespace Orchard.Environment.FileSystems {
|
||||
/// Expected to work on physical filesystem, but decouples core
|
||||
/// system from web hosting apis
|
||||
/// </summary>
|
||||
public interface IAppDataFolder {
|
||||
public interface IAppDataFolder : IVolatileProvider {
|
||||
IEnumerable<string> ListFiles(string path);
|
||||
IEnumerable<string> ListDirectories(string path);
|
||||
|
||||
|
@@ -1,9 +1,11 @@
|
||||
using Orchard.Caching.Providers;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Caching;
|
||||
|
||||
namespace Orchard.Environment.FileSystems {
|
||||
public interface IVirtualPathProvider : IVolatileProvider {
|
||||
IVolatileSignal WhenPathChanges(string path);
|
||||
|
||||
IEnumerable<string> GetSubfolderPaths(string virtualPath);
|
||||
string ReadAllText(string virtualPath);
|
||||
|
||||
IVolatileToken WhenPathChanges(string path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ using System.Web.Hosting;
|
||||
using Autofac;
|
||||
using Autofac.Configuration;
|
||||
using Autofac.Integration.Web;
|
||||
using Orchard.Caching;
|
||||
using Orchard.Environment.AutofacUtil;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Environment.Extensions;
|
||||
@@ -15,6 +16,7 @@ using Orchard.Environment.ShellBuilders;
|
||||
using Orchard.Environment.Topology;
|
||||
using Orchard.Events;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Services;
|
||||
|
||||
namespace Orchard.Environment {
|
||||
public static class OrchardStarter {
|
||||
@@ -22,10 +24,15 @@ namespace Orchard.Environment {
|
||||
var builder = new ContainerBuilder();
|
||||
builder.RegisterModule(new LoggingModule());
|
||||
builder.RegisterModule(new EventsModule());
|
||||
builder.RegisterModule(new CacheModule());
|
||||
|
||||
// a single default host implementation is needed for bootstrapping a web app domain
|
||||
builder.RegisterType<DefaultOrchardEventBus>().As<IEventBus>().SingleInstance();
|
||||
builder.RegisterType<AppDataFolder>().As<IAppDataFolder>().SingleInstance();
|
||||
builder.RegisterType<DefaultCacheHolder>().As<ICacheHolder>().SingleInstance();
|
||||
|
||||
RegisterVolatileProvider<DefaultVirtualPathProvider, IVirtualPathProvider>(builder);
|
||||
RegisterVolatileProvider<AppDataFolder, IAppDataFolder>(builder);
|
||||
RegisterVolatileProvider<Clock, IClock>(builder);
|
||||
|
||||
builder.RegisterType<DefaultOrchardHost>().As<IOrchardHost>().As<IEventHandler>().SingleInstance();
|
||||
{
|
||||
@@ -92,6 +99,13 @@ namespace Orchard.Environment {
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
private static void RegisterVolatileProvider<TRegister, TService>(ContainerBuilder builder) where TService : IVolatileProvider {
|
||||
builder.RegisterType<TRegister>()
|
||||
.As<TService>()
|
||||
.As<IVolatileProvider>()
|
||||
.SingleInstance();
|
||||
}
|
||||
|
||||
public static IOrchardHost CreateHost(Action<ContainerBuilder> registrations) {
|
||||
var container = CreateHostContainer(registrations);
|
||||
return container.Resolve<IOrchardHost>();
|
||||
|
@@ -5,6 +5,7 @@
|
||||
public interface ISingletonDependency : IDependency {
|
||||
|
||||
}
|
||||
|
||||
public interface ITransientDependency : IDependency {
|
||||
|
||||
}
|
||||
|
@@ -145,15 +145,18 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="Caching\AcquireContext.cs" />
|
||||
<Compile Include="Caching\Cache.cs" />
|
||||
<Compile Include="Caching\CacheModule.cs" />
|
||||
<Compile Include="Caching\DefaultCacheHolder.cs" />
|
||||
<Compile Include="Caching\DefaultCacheManager.cs" />
|
||||
<Compile Include="Caching\Providers\IVolatileSink.cs" />
|
||||
<Compile Include="Caching\ICacheHolder.cs" />
|
||||
<Compile Include="Caching\Weak.cs" />
|
||||
<Compile Include="Environment\FileSystems\DefaultVirtualPathProvider.cs" />
|
||||
<Compile Include="Environment\FileSystems\IAppDataFolder.cs" />
|
||||
<Compile Include="Environment\FileSystems\IVirtualPathProvider.cs" />
|
||||
<Compile Include="Caching\ICache.cs" />
|
||||
<Compile Include="Caching\ICacheManager.cs" />
|
||||
<Compile Include="Caching\Providers\IVolatileSignal.cs" />
|
||||
<Compile Include="Caching\Providers\IVolatileProvider.cs" />
|
||||
<Compile Include="Caching\IVolatileToken.cs" />
|
||||
<Compile Include="Caching\IVolatileProvider.cs" />
|
||||
<Compile Include="Commands\CommandParameters.cs" />
|
||||
<Compile Include="Commands\CommandDescriptor.cs" />
|
||||
<Compile Include="Commands\CommandHandlerDescriptor.cs" />
|
||||
|
@@ -1,13 +1,15 @@
|
||||
using System;
|
||||
using Orchard.Caching;
|
||||
|
||||
namespace Orchard.Services {
|
||||
public interface IClock : IDependency {
|
||||
public interface IClock : IVolatileProvider {
|
||||
DateTime UtcNow { get; }
|
||||
}
|
||||
|
||||
public class Clock : IClock {
|
||||
public DateTime UtcNow {
|
||||
get { return DateTime.Now; }
|
||||
get { return DateTime.UtcNow; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user