mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +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>();
|
||||
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)
|
||||
_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
|
||||
|
||||
IEnumerable<string> dependencies = extensionLoader.GetDependenciesAccessor(fileEntry.Name);
|
||||
@@ -76,14 +76,15 @@ namespace Orchard.Tests.Environment.Loaders {
|
||||
|
||||
DynamicExtensionLoaderAccessor extensionLoader = _container.Resolve<DynamicExtensionLoaderAccessor>();
|
||||
StubFileSystem stubFileSystem = _container.Resolve<StubFileSystem>();
|
||||
StubFileSystem.FileEntry fileEntry = stubFileSystem.CreateFileEntry("orchard.a.csjproj");
|
||||
StubFileSystem.FileEntry fileEntry2 = stubFileSystem.CreateFileEntry("orchard.b.csjproj");
|
||||
StubFileSystem.FileEntry fileEntry3 = stubFileSystem.CreateFileEntry("orchard.c.csjproj");
|
||||
StubFileSystem.FileEntry fileEntry = stubFileSystem.CreateFileEntry("orchard.a.csproj");
|
||||
StubFileSystem.FileEntry fileEntry2 = stubFileSystem.CreateFileEntry("orchard.b.csproj");
|
||||
StubFileSystem.FileEntry fileEntry3 = stubFileSystem.CreateFileEntry("orchard.c.csproj");
|
||||
|
||||
// Project a reference b and c which share a file in common
|
||||
|
||||
// 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(
|
||||
new ProjectFileDescriptor {
|
||||
SourceFilenames = new[] { fileName1, fileName2 },
|
||||
@@ -96,16 +97,16 @@ namespace Orchard.Tests.Environment.Loaders {
|
||||
},
|
||||
new ReferenceDescriptor {
|
||||
ReferenceType = ReferenceType.Project,
|
||||
SimpleName = Path.GetFileNameWithoutExtension(fileEntry2.Name),
|
||||
FullName = Path.GetFileNameWithoutExtension(fileEntry2.Name),
|
||||
SimpleName = Path.GetFileNameWithoutExtension(fileEntry3.Name),
|
||||
FullName = Path.GetFileNameWithoutExtension(fileEntry3.Name),
|
||||
Path = fileEntry3.Name
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Result for project b and c
|
||||
_mockedStubProjectFileParser.Setup(stubProjectFileParser => stubProjectFileParser.Parse(It.Is<Stream>(stream =>
|
||||
((StubFileSystem.FileEntryReadStream)stream).FileEntry == fileEntry2 || ((StubFileSystem.FileEntryReadStream)stream).FileEntry == fileEntry3)))
|
||||
_mockedStubProjectFileParser
|
||||
.Setup(stubProjectFileParser => stubProjectFileParser.Parse(It.Is<string>(virtualPath => (virtualPath == "~/orchard.b.csproj" || virtualPath == "~/orchard.c.csproj"))))
|
||||
.Returns(
|
||||
new ProjectFileDescriptor {
|
||||
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");
|
||||
|
||||
// Project files
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(fileEntry.Name)), Is.Not.Null);
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(fileEntry2.Name)), Is.Not.Null);
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(fileEntry3.Name)), Is.Not.Null);
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(fileEntry.Name)), Is.Not.Null);
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(fileEntry2.Name)), Is.Not.Null);
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(fileEntry3.Name)), Is.Not.Null);
|
||||
|
||||
// Individual source files
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(fileName1)), Is.Not.Null);
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(fileName2)), Is.Not.Null);
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(commonFileName)), Is.Not.Null);
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(fileName1)), Is.Not.Null);
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(fileName2)), Is.Not.Null);
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(commonFileName)), Is.Not.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -135,8 +136,8 @@ namespace Orchard.Tests.Environment.Loaders {
|
||||
|
||||
DynamicExtensionLoaderAccessor extensionLoader = _container.Resolve<DynamicExtensionLoaderAccessor>();
|
||||
StubFileSystem stubFileSystem = _container.Resolve<StubFileSystem>();
|
||||
StubFileSystem.FileEntry fileEntry = stubFileSystem.CreateFileEntry("orchard.a.csjproj");
|
||||
StubFileSystem.FileEntry fileEntry2 = stubFileSystem.CreateFileEntry("orchard.b.csjproj");
|
||||
StubFileSystem.FileEntry fileEntry = stubFileSystem.CreateFileEntry("orchard.a.csproj");
|
||||
StubFileSystem.FileEntry fileEntry2 = stubFileSystem.CreateFileEntry("orchard.b.csproj");
|
||||
|
||||
StubFileSystem.DirectoryEntry directoryEntry = stubFileSystem.CreateDirectoryEntry("bin");
|
||||
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
|
||||
|
||||
// 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(
|
||||
new ProjectFileDescriptor {
|
||||
SourceFilenames = new[] { fileName1, fileName2 },
|
||||
@@ -159,14 +161,15 @@ namespace Orchard.Tests.Environment.Loaders {
|
||||
});
|
||||
|
||||
// Result for project b and c
|
||||
_mockedStubProjectFileParser.Setup(stubProjectFileParser => stubProjectFileParser.Parse(It.Is<Stream>(stream =>
|
||||
((StubFileSystem.FileEntryReadStream)stream).FileEntry == fileEntry2)))
|
||||
_mockedStubProjectFileParser
|
||||
.Setup(stubProjectFileParser => stubProjectFileParser.Parse(It.Is<string>(virtualPath => virtualPath == "~/orchard.b.csproj")))
|
||||
.Returns(
|
||||
new ProjectFileDescriptor {
|
||||
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(
|
||||
new DependencyDescriptor {
|
||||
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");
|
||||
|
||||
// Project files
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(fileEntry.Name)), Is.Not.Null);
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(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(fileEntry.Name)), Is.Not.Null);
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(fileEntry2.Name)), Is.Not.Null);
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(Path.Combine(directoryEntry.Name, fileEntry3.Name))), Is.Not.Null);
|
||||
|
||||
// Individual source files
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(fileName1)), Is.Not.Null);
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(fileName2)), Is.Not.Null);
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Equals(commonFileName)), Is.Not.Null);
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(fileName1)), Is.Not.Null);
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(fileName2)), Is.Not.Null);
|
||||
Assert.That(dependencies.FirstOrDefault(dep => dep.Contains(commonFileName)), Is.Not.Null);
|
||||
}
|
||||
|
||||
internal class DynamicExtensionLoaderAccessor : DynamicExtensionLoader {
|
||||
|
@@ -48,8 +48,7 @@ namespace Orchard.Environment.Extensions.Compilers {
|
||||
return;
|
||||
|
||||
try {
|
||||
using (var stream = _virtualPathProvider.OpenFile(context.VirtualPath)) {
|
||||
var projectFileDescriptor = _projectFileParser.Parse(stream);
|
||||
var projectFileDescriptor = _projectFileParser.Parse(context.VirtualPath);
|
||||
|
||||
// Add source files
|
||||
var directory = _virtualPathProvider.GetDirectoryName(context.VirtualPath);
|
||||
@@ -100,7 +99,6 @@ namespace Orchard.Environment.Extensions.Compilers {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
//Note: we need to embed the "e.Message" in the exception text because
|
||||
// ASP.NET build manager "swallows" inner exceptions from this method.
|
||||
|
@@ -3,12 +3,38 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Orchard.Caching;
|
||||
using Orchard.FileSystems.WebSite;
|
||||
|
||||
namespace Orchard.Environment.Extensions.Compilers {
|
||||
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) {
|
||||
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 {
|
||||
AssemblyName = GetAssemblyName(document),
|
||||
SourceFilenames = GetSourceFilenames(document).ToArray(),
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Orchard.Environment.Extensions.Compilers {
|
||||
public interface IProjectFileParser {
|
||||
ProjectFileDescriptor Parse(string virtualPath);
|
||||
ProjectFileDescriptor Parse(Stream stream);
|
||||
}
|
||||
}
|
@@ -101,8 +101,7 @@ namespace Orchard.Environment.Extensions.Loaders {
|
||||
if (projectPath == null)
|
||||
return Enumerable.Empty<ExtensionReferenceProbeEntry>();
|
||||
|
||||
using (var stream = _virtualPathProvider.OpenFile(projectPath)) {
|
||||
var projectFile = _projectFileParser.Parse(stream);
|
||||
var projectFile = _projectFileParser.Parse(projectPath);
|
||||
|
||||
return projectFile.References.Select(r => new ExtensionReferenceProbeEntry {
|
||||
Descriptor = descriptor,
|
||||
@@ -111,7 +110,6 @@ namespace Orchard.Environment.Extensions.Loaders {
|
||||
VirtualPath = _virtualPathProvider.GetProjectReferenceVirtualPath(projectPath, r.SimpleName, r.Path)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReferenceActivated(ExtensionLoadingContext context, ExtensionReferenceProbeEntry referenceEntry) {
|
||||
//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) {
|
||||
string basePath = _virtualPathProvider.GetDirectoryName(projectPath);
|
||||
|
||||
using (var stream = _virtualPathProvider.OpenFile(projectPath)) {
|
||||
ProjectFileDescriptor projectFile = _projectFileParser.Parse(stream);
|
||||
ProjectFileDescriptor projectFile = _projectFileParser.Parse(projectPath);
|
||||
|
||||
// Add source files
|
||||
currentSet.UnionWith(projectFile.SourceFilenames.Select(f => _virtualPathProvider.Combine(basePath, f)));
|
||||
@@ -236,7 +233,6 @@ namespace Orchard.Environment.Extensions.Loaders {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetProjectPath(ExtensionDescriptor descriptor) {
|
||||
string projectPath = _virtualPathProvider.Combine(descriptor.Location, descriptor.Id,
|
||||
|
Reference in New Issue
Block a user