mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-02-09 09:16:41 +08:00
PERF: Cache result of parsing .csproj files
--HG-- branch : 1.x extra : transplant_source : %02%96%9A%B7%91%05%E1%1EF%03%5D%5C%EB%A0%95Q%13%F6B%9C
This commit is contained in:
@@ -53,10 +53,10 @@ namespace Orchard.Tests.Environment.Loaders {
|
|||||||
|
|
||||||
DynamicExtensionLoaderAccessor extensionLoader = _container.Resolve<DynamicExtensionLoaderAccessor>();
|
DynamicExtensionLoaderAccessor extensionLoader = _container.Resolve<DynamicExtensionLoaderAccessor>();
|
||||||
StubFileSystem stubFileSystem = _container.Resolve<StubFileSystem>();
|
StubFileSystem stubFileSystem = _container.Resolve<StubFileSystem>();
|
||||||
StubFileSystem.FileEntry fileEntry = stubFileSystem.CreateFileEntry("orchard.a.csjproj");
|
StubFileSystem.FileEntry fileEntry = stubFileSystem.CreateFileEntry("orchard.a.csproj");
|
||||||
|
|
||||||
// Create duplicate source files (invalid situation in reality but easy enough to test)
|
// Create duplicate source files (invalid situation in reality but easy enough to test)
|
||||||
_mockedStubProjectFileParser.Setup(stubProjectFileParser => stubProjectFileParser.Parse(It.IsAny<Stream>())).Returns(
|
_mockedStubProjectFileParser.Setup(stubProjectFileParser => stubProjectFileParser.Parse(It.IsAny<string>())).Returns(
|
||||||
new ProjectFileDescriptor { SourceFilenames = new[] { fileName1, fileName2, fileName1 } }); // duplicate file
|
new ProjectFileDescriptor { SourceFilenames = new[] { fileName1, fileName2, fileName1 } }); // duplicate file
|
||||||
|
|
||||||
IEnumerable<string> dependencies = extensionLoader.GetDependenciesAccessor(fileEntry.Name);
|
IEnumerable<string> dependencies = extensionLoader.GetDependenciesAccessor(fileEntry.Name);
|
||||||
@@ -76,14 +76,15 @@ namespace Orchard.Tests.Environment.Loaders {
|
|||||||
|
|
||||||
DynamicExtensionLoaderAccessor extensionLoader = _container.Resolve<DynamicExtensionLoaderAccessor>();
|
DynamicExtensionLoaderAccessor extensionLoader = _container.Resolve<DynamicExtensionLoaderAccessor>();
|
||||||
StubFileSystem stubFileSystem = _container.Resolve<StubFileSystem>();
|
StubFileSystem stubFileSystem = _container.Resolve<StubFileSystem>();
|
||||||
StubFileSystem.FileEntry fileEntry = stubFileSystem.CreateFileEntry("orchard.a.csjproj");
|
StubFileSystem.FileEntry fileEntry = stubFileSystem.CreateFileEntry("orchard.a.csproj");
|
||||||
StubFileSystem.FileEntry fileEntry2 = stubFileSystem.CreateFileEntry("orchard.b.csjproj");
|
StubFileSystem.FileEntry fileEntry2 = stubFileSystem.CreateFileEntry("orchard.b.csproj");
|
||||||
StubFileSystem.FileEntry fileEntry3 = stubFileSystem.CreateFileEntry("orchard.c.csjproj");
|
StubFileSystem.FileEntry fileEntry3 = stubFileSystem.CreateFileEntry("orchard.c.csproj");
|
||||||
|
|
||||||
// Project a reference b and c which share a file in common
|
// Project a reference b and c which share a file in common
|
||||||
|
|
||||||
// Result for project a
|
// Result for project a
|
||||||
_mockedStubProjectFileParser.Setup(stubProjectFileParser => stubProjectFileParser.Parse(It.Is<Stream>(stream => ((StubFileSystem.FileEntryReadStream)stream).FileEntry == fileEntry)))
|
_mockedStubProjectFileParser
|
||||||
|
.Setup(stubProjectFileParser => stubProjectFileParser.Parse(It.Is<string>(virtualPath => virtualPath == "orchard.a.csproj")))
|
||||||
.Returns(
|
.Returns(
|
||||||
new ProjectFileDescriptor {
|
new ProjectFileDescriptor {
|
||||||
SourceFilenames = new[] { fileName1, fileName2 },
|
SourceFilenames = new[] { fileName1, fileName2 },
|
||||||
@@ -96,16 +97,16 @@ namespace Orchard.Tests.Environment.Loaders {
|
|||||||
},
|
},
|
||||||
new ReferenceDescriptor {
|
new ReferenceDescriptor {
|
||||||
ReferenceType = ReferenceType.Project,
|
ReferenceType = ReferenceType.Project,
|
||||||
SimpleName = Path.GetFileNameWithoutExtension(fileEntry2.Name),
|
SimpleName = Path.GetFileNameWithoutExtension(fileEntry3.Name),
|
||||||
FullName = Path.GetFileNameWithoutExtension(fileEntry2.Name),
|
FullName = Path.GetFileNameWithoutExtension(fileEntry3.Name),
|
||||||
Path = fileEntry3.Name
|
Path = fileEntry3.Name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Result for project b and c
|
// Result for project b and c
|
||||||
_mockedStubProjectFileParser.Setup(stubProjectFileParser => stubProjectFileParser.Parse(It.Is<Stream>(stream =>
|
_mockedStubProjectFileParser
|
||||||
((StubFileSystem.FileEntryReadStream)stream).FileEntry == fileEntry2 || ((StubFileSystem.FileEntryReadStream)stream).FileEntry == fileEntry3)))
|
.Setup(stubProjectFileParser => stubProjectFileParser.Parse(It.Is<string>(virtualPath => (virtualPath == "~/orchard.b.csproj" || virtualPath == "~/orchard.c.csproj"))))
|
||||||
.Returns(
|
.Returns(
|
||||||
new ProjectFileDescriptor {
|
new ProjectFileDescriptor {
|
||||||
SourceFilenames = new[] { commonFileName }
|
SourceFilenames = new[] { commonFileName }
|
||||||
@@ -116,14 +117,14 @@ namespace Orchard.Tests.Environment.Loaders {
|
|||||||
Assert.That(dependencies.Count(), Is.EqualTo(6), "6 results should mean no duplicates");
|
Assert.That(dependencies.Count(), Is.EqualTo(6), "6 results should mean no duplicates");
|
||||||
|
|
||||||
// Project files
|
// Project files
|
||||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(fileEntry.Name)), Is.Not.Null);
|
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(fileEntry.Name)), Is.Not.Null);
|
||||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(fileEntry2.Name)), Is.Not.Null);
|
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(fileEntry2.Name)), Is.Not.Null);
|
||||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(fileEntry3.Name)), Is.Not.Null);
|
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(fileEntry3.Name)), Is.Not.Null);
|
||||||
|
|
||||||
// Individual source files
|
// Individual source files
|
||||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(fileName1)), Is.Not.Null);
|
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(fileName1)), Is.Not.Null);
|
||||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(fileName2)), Is.Not.Null);
|
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(fileName2)), Is.Not.Null);
|
||||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(commonFileName)), Is.Not.Null);
|
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(commonFileName)), Is.Not.Null);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@@ -135,8 +136,8 @@ namespace Orchard.Tests.Environment.Loaders {
|
|||||||
|
|
||||||
DynamicExtensionLoaderAccessor extensionLoader = _container.Resolve<DynamicExtensionLoaderAccessor>();
|
DynamicExtensionLoaderAccessor extensionLoader = _container.Resolve<DynamicExtensionLoaderAccessor>();
|
||||||
StubFileSystem stubFileSystem = _container.Resolve<StubFileSystem>();
|
StubFileSystem stubFileSystem = _container.Resolve<StubFileSystem>();
|
||||||
StubFileSystem.FileEntry fileEntry = stubFileSystem.CreateFileEntry("orchard.a.csjproj");
|
StubFileSystem.FileEntry fileEntry = stubFileSystem.CreateFileEntry("orchard.a.csproj");
|
||||||
StubFileSystem.FileEntry fileEntry2 = stubFileSystem.CreateFileEntry("orchard.b.csjproj");
|
StubFileSystem.FileEntry fileEntry2 = stubFileSystem.CreateFileEntry("orchard.b.csproj");
|
||||||
|
|
||||||
StubFileSystem.DirectoryEntry directoryEntry = stubFileSystem.CreateDirectoryEntry("bin");
|
StubFileSystem.DirectoryEntry directoryEntry = stubFileSystem.CreateDirectoryEntry("bin");
|
||||||
StubFileSystem.FileEntry fileEntry3 = directoryEntry.CreateFile("orchard.b.dll");
|
StubFileSystem.FileEntry fileEntry3 = directoryEntry.CreateFile("orchard.b.dll");
|
||||||
@@ -144,7 +145,8 @@ namespace Orchard.Tests.Environment.Loaders {
|
|||||||
// Project a reference b and c which share a file in common
|
// Project a reference b and c which share a file in common
|
||||||
|
|
||||||
// Result for project a
|
// Result for project a
|
||||||
_mockedStubProjectFileParser.Setup(stubProjectFileParser => stubProjectFileParser.Parse(It.Is<Stream>(stream => ((StubFileSystem.FileEntryReadStream)stream).FileEntry == fileEntry)))
|
_mockedStubProjectFileParser
|
||||||
|
.Setup(stubProjectFileParser => stubProjectFileParser.Parse(It.Is<string>(virtualPath => virtualPath == "orchard.a.csproj")))
|
||||||
.Returns(
|
.Returns(
|
||||||
new ProjectFileDescriptor {
|
new ProjectFileDescriptor {
|
||||||
SourceFilenames = new[] { fileName1, fileName2 },
|
SourceFilenames = new[] { fileName1, fileName2 },
|
||||||
@@ -159,14 +161,15 @@ namespace Orchard.Tests.Environment.Loaders {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Result for project b and c
|
// Result for project b and c
|
||||||
_mockedStubProjectFileParser.Setup(stubProjectFileParser => stubProjectFileParser.Parse(It.Is<Stream>(stream =>
|
_mockedStubProjectFileParser
|
||||||
((StubFileSystem.FileEntryReadStream)stream).FileEntry == fileEntry2)))
|
.Setup(stubProjectFileParser => stubProjectFileParser.Parse(It.Is<string>(virtualPath => virtualPath == "~/orchard.b.csproj")))
|
||||||
.Returns(
|
.Returns(
|
||||||
new ProjectFileDescriptor {
|
new ProjectFileDescriptor {
|
||||||
SourceFilenames = new[] { commonFileName }
|
SourceFilenames = new[] { commonFileName }
|
||||||
});
|
});
|
||||||
|
|
||||||
_mockedDependenciesFolder.Setup(dependenciesFolder => dependenciesFolder.GetDescriptor(It.Is<string>(moduleName => moduleName == Path.GetDirectoryName(fileEntry2.Name))))
|
_mockedDependenciesFolder
|
||||||
|
.Setup(dependenciesFolder => dependenciesFolder.GetDescriptor(It.Is<string>(moduleName => moduleName == Path.GetDirectoryName(fileEntry2.Name))))
|
||||||
.Returns(
|
.Returns(
|
||||||
new DependencyDescriptor {
|
new DependencyDescriptor {
|
||||||
VirtualPath = Path.Combine(directoryEntry.Name, fileEntry3.Name)
|
VirtualPath = Path.Combine(directoryEntry.Name, fileEntry3.Name)
|
||||||
@@ -177,14 +180,14 @@ namespace Orchard.Tests.Environment.Loaders {
|
|||||||
Assert.That(dependencies.Count(), Is.EqualTo(6), "6 results should mean no duplicates");
|
Assert.That(dependencies.Count(), Is.EqualTo(6), "6 results should mean no duplicates");
|
||||||
|
|
||||||
// Project files
|
// Project files
|
||||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(fileEntry.Name)), Is.Not.Null);
|
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(fileEntry.Name)), Is.Not.Null);
|
||||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(fileEntry2.Name)), Is.Not.Null);
|
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(fileEntry2.Name)), Is.Not.Null);
|
||||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(Path.Combine(directoryEntry.Name, fileEntry3.Name))), Is.Not.Null);
|
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(Path.Combine(directoryEntry.Name, fileEntry3.Name))), Is.Not.Null);
|
||||||
|
|
||||||
// Individual source files
|
// Individual source files
|
||||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(fileName1)), Is.Not.Null);
|
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(fileName1)), Is.Not.Null);
|
||||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(fileName2)), Is.Not.Null);
|
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(fileName2)), Is.Not.Null);
|
||||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(commonFileName)), Is.Not.Null);
|
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(commonFileName)), Is.Not.Null);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DynamicExtensionLoaderAccessor : DynamicExtensionLoader {
|
internal class DynamicExtensionLoaderAccessor : DynamicExtensionLoader {
|
||||||
|
|||||||
@@ -48,8 +48,7 @@ namespace Orchard.Environment.Extensions.Compilers {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
using (var stream = _virtualPathProvider.OpenFile(context.VirtualPath)) {
|
var projectFileDescriptor = _projectFileParser.Parse(context.VirtualPath);
|
||||||
var projectFileDescriptor = _projectFileParser.Parse(stream);
|
|
||||||
|
|
||||||
// Add source files
|
// Add source files
|
||||||
var directory = _virtualPathProvider.GetDirectoryName(context.VirtualPath);
|
var directory = _virtualPathProvider.GetDirectoryName(context.VirtualPath);
|
||||||
@@ -100,7 +99,6 @@ namespace Orchard.Environment.Extensions.Compilers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
//Note: we need to embed the "e.Message" in the exception text because
|
//Note: we need to embed the "e.Message" in the exception text because
|
||||||
// ASP.NET build manager "swallows" inner exceptions from this method.
|
// ASP.NET build manager "swallows" inner exceptions from this method.
|
||||||
|
|||||||
@@ -3,12 +3,38 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
using Orchard.Caching;
|
||||||
|
using Orchard.FileSystems.WebSite;
|
||||||
|
|
||||||
namespace Orchard.Environment.Extensions.Compilers {
|
namespace Orchard.Environment.Extensions.Compilers {
|
||||||
public class DefaultProjectFileParser : IProjectFileParser {
|
public class DefaultProjectFileParser : IProjectFileParser {
|
||||||
|
private readonly IWebSiteFolder _webSiteFolder;
|
||||||
|
private readonly ICacheManager _cacheManager;
|
||||||
|
|
||||||
|
public DefaultProjectFileParser(IWebSiteFolder webSiteFolder, ICacheManager cacheManager) {
|
||||||
|
_webSiteFolder = webSiteFolder;
|
||||||
|
_cacheManager = cacheManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProjectFileDescriptor Parse(string virtualPath) {
|
||||||
|
return _cacheManager.Get(virtualPath,
|
||||||
|
ctx => {
|
||||||
|
ctx.Monitor(_webSiteFolder.WhenPathChanges(virtualPath));
|
||||||
|
string content = _webSiteFolder.ReadFile(virtualPath);
|
||||||
|
using (var reader = new StringReader(content)) {
|
||||||
|
return Parse(reader);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public ProjectFileDescriptor Parse(Stream stream) {
|
public ProjectFileDescriptor Parse(Stream stream) {
|
||||||
var document = XDocument.Load(XmlReader.Create(stream));
|
using (var reader = new StreamReader(stream)) {
|
||||||
|
return Parse(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProjectFileDescriptor Parse(TextReader reader) {
|
||||||
|
var document = XDocument.Load(XmlReader.Create(reader));
|
||||||
return new ProjectFileDescriptor {
|
return new ProjectFileDescriptor {
|
||||||
AssemblyName = GetAssemblyName(document),
|
AssemblyName = GetAssemblyName(document),
|
||||||
SourceFilenames = GetSourceFilenames(document).ToArray(),
|
SourceFilenames = GetSourceFilenames(document).ToArray(),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace Orchard.Environment.Extensions.Compilers {
|
namespace Orchard.Environment.Extensions.Compilers {
|
||||||
public interface IProjectFileParser {
|
public interface IProjectFileParser {
|
||||||
|
ProjectFileDescriptor Parse(string virtualPath);
|
||||||
ProjectFileDescriptor Parse(Stream stream);
|
ProjectFileDescriptor Parse(Stream stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,8 +101,7 @@ namespace Orchard.Environment.Extensions.Loaders {
|
|||||||
if (projectPath == null)
|
if (projectPath == null)
|
||||||
return Enumerable.Empty<ExtensionReferenceProbeEntry>();
|
return Enumerable.Empty<ExtensionReferenceProbeEntry>();
|
||||||
|
|
||||||
using (var stream = _virtualPathProvider.OpenFile(projectPath)) {
|
var projectFile = _projectFileParser.Parse(projectPath);
|
||||||
var projectFile = _projectFileParser.Parse(stream);
|
|
||||||
|
|
||||||
return projectFile.References.Select(r => new ExtensionReferenceProbeEntry {
|
return projectFile.References.Select(r => new ExtensionReferenceProbeEntry {
|
||||||
Descriptor = descriptor,
|
Descriptor = descriptor,
|
||||||
@@ -111,7 +110,6 @@ namespace Orchard.Environment.Extensions.Loaders {
|
|||||||
VirtualPath = _virtualPathProvider.GetProjectReferenceVirtualPath(projectPath, r.SimpleName, r.Path)
|
VirtualPath = _virtualPathProvider.GetProjectReferenceVirtualPath(projectPath, r.SimpleName, r.Path)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public override void ReferenceActivated(ExtensionLoadingContext context, ExtensionReferenceProbeEntry referenceEntry) {
|
public override void ReferenceActivated(ExtensionLoadingContext context, ExtensionReferenceProbeEntry referenceEntry) {
|
||||||
//Note: This is the same implementation as "PrecompiledExtensionLoader"
|
//Note: This is the same implementation as "PrecompiledExtensionLoader"
|
||||||
@@ -193,8 +191,7 @@ namespace Orchard.Environment.Extensions.Loaders {
|
|||||||
private void AddDependencies(string projectPath, HashSet<string> currentSet) {
|
private void AddDependencies(string projectPath, HashSet<string> currentSet) {
|
||||||
string basePath = _virtualPathProvider.GetDirectoryName(projectPath);
|
string basePath = _virtualPathProvider.GetDirectoryName(projectPath);
|
||||||
|
|
||||||
using (var stream = _virtualPathProvider.OpenFile(projectPath)) {
|
ProjectFileDescriptor projectFile = _projectFileParser.Parse(projectPath);
|
||||||
ProjectFileDescriptor projectFile = _projectFileParser.Parse(stream);
|
|
||||||
|
|
||||||
// Add source files
|
// Add source files
|
||||||
currentSet.UnionWith(projectFile.SourceFilenames.Select(f => _virtualPathProvider.Combine(basePath, f)));
|
currentSet.UnionWith(projectFile.SourceFilenames.Select(f => _virtualPathProvider.Combine(basePath, f)));
|
||||||
@@ -236,7 +233,6 @@ namespace Orchard.Environment.Extensions.Loaders {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private string GetProjectPath(ExtensionDescriptor descriptor) {
|
private string GetProjectPath(ExtensionDescriptor descriptor) {
|
||||||
string projectPath = _virtualPathProvider.Combine(descriptor.Location, descriptor.Id,
|
string projectPath = _virtualPathProvider.Combine(descriptor.Location, descriptor.Id,
|
||||||
|
|||||||
Reference in New Issue
Block a user