diff --git a/FileSystem.sln b/FileSystem.sln
index 5c8aa746..288daa0c 100644
--- a/FileSystem.sln
+++ b/FileSystem.sln
@@ -1,7 +1,6 @@
-
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
-VisualStudioVersion = 14.0.22823.1
+VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A1477614-E825-4204-A684-385004B63AEB}"
EndProject
@@ -28,6 +27,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.FileProvid
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.FileProviders.Sources", "src\Microsoft.AspNet.FileProviders.Sources\Microsoft.AspNet.FileProviders.Sources.xproj", "{92C2C85C-D1A5-44BD-BE23-238E08471B4D}"
EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.FileProviders.Composite", "src\Microsoft.AspNet.FileProviders.Composite\Microsoft.AspNet.FileProviders.Composite.xproj", "{CAAF52EF-F91B-474D-AC56-FE9D96FF8254}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.FileProviders.Composite.Tests", "test\Microsoft.AspNet.FileProviders.Composite.Tests\Microsoft.AspNet.FileProviders.Composite.Tests.xproj", "{C2EA9BD0-C986-4B60-9E45-5EA51E1EA494}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -132,6 +135,30 @@ Global
{92C2C85C-D1A5-44BD-BE23-238E08471B4D}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{92C2C85C-D1A5-44BD-BE23-238E08471B4D}.Release|x86.ActiveCfg = Release|Any CPU
{92C2C85C-D1A5-44BD-BE23-238E08471B4D}.Release|x86.Build.0 = Release|Any CPU
+ {CAAF52EF-F91B-474D-AC56-FE9D96FF8254}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CAAF52EF-F91B-474D-AC56-FE9D96FF8254}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CAAF52EF-F91B-474D-AC56-FE9D96FF8254}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {CAAF52EF-F91B-474D-AC56-FE9D96FF8254}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {CAAF52EF-F91B-474D-AC56-FE9D96FF8254}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CAAF52EF-F91B-474D-AC56-FE9D96FF8254}.Debug|x86.Build.0 = Debug|Any CPU
+ {CAAF52EF-F91B-474D-AC56-FE9D96FF8254}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CAAF52EF-F91B-474D-AC56-FE9D96FF8254}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CAAF52EF-F91B-474D-AC56-FE9D96FF8254}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {CAAF52EF-F91B-474D-AC56-FE9D96FF8254}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {CAAF52EF-F91B-474D-AC56-FE9D96FF8254}.Release|x86.ActiveCfg = Release|Any CPU
+ {CAAF52EF-F91B-474D-AC56-FE9D96FF8254}.Release|x86.Build.0 = Release|Any CPU
+ {C2EA9BD0-C986-4B60-9E45-5EA51E1EA494}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C2EA9BD0-C986-4B60-9E45-5EA51E1EA494}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C2EA9BD0-C986-4B60-9E45-5EA51E1EA494}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {C2EA9BD0-C986-4B60-9E45-5EA51E1EA494}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {C2EA9BD0-C986-4B60-9E45-5EA51E1EA494}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C2EA9BD0-C986-4B60-9E45-5EA51E1EA494}.Debug|x86.Build.0 = Debug|Any CPU
+ {C2EA9BD0-C986-4B60-9E45-5EA51E1EA494}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C2EA9BD0-C986-4B60-9E45-5EA51E1EA494}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C2EA9BD0-C986-4B60-9E45-5EA51E1EA494}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {C2EA9BD0-C986-4B60-9E45-5EA51E1EA494}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {C2EA9BD0-C986-4B60-9E45-5EA51E1EA494}.Release|x86.ActiveCfg = Release|Any CPU
+ {C2EA9BD0-C986-4B60-9E45-5EA51E1EA494}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -145,5 +172,7 @@ Global
{6B6BA57A-B32D-430A-AF39-09CAA85308C2} = {E399495E-82B8-4C06-8779-C1D02BEF4495}
{66FE5FDF-BBF9-4573-A7B7-53551731C0F9} = {E399495E-82B8-4C06-8779-C1D02BEF4495}
{92C2C85C-D1A5-44BD-BE23-238E08471B4D} = {A1477614-E825-4204-A684-385004B63AEB}
+ {CAAF52EF-F91B-474D-AC56-FE9D96FF8254} = {A1477614-E825-4204-A684-385004B63AEB}
+ {C2EA9BD0-C986-4B60-9E45-5EA51E1EA494} = {E399495E-82B8-4C06-8779-C1D02BEF4495}
EndGlobalSection
EndGlobal
diff --git a/src/Microsoft.AspNet.FileProviders.Composite/CompositeDirectoryContents.cs b/src/Microsoft.AspNet.FileProviders.Composite/CompositeDirectoryContents.cs
new file mode 100644
index 00000000..31a1dd10
--- /dev/null
+++ b/src/Microsoft.AspNet.FileProviders.Composite/CompositeDirectoryContents.cs
@@ -0,0 +1,95 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Microsoft.AspNet.FileProviders
+{
+ ///
+ /// Represents the result of a call composition of for a list of and a path.
+ ///
+ public class CompositeDirectoryContents : IDirectoryContents
+ {
+ private readonly IList _fileProviders;
+ private readonly string _subPath;
+ private List _files;
+ private bool _exists;
+ private List _directories;
+
+ ///
+ /// Creates a new instance of to represents the result of a call composition of .
+ ///
+ /// The list of for which the results have to be composed.
+ /// The path.
+ public CompositeDirectoryContents(IList fileProviders, string subpath)
+ {
+ if(fileProviders == null)
+ {
+ throw new ArgumentNullException(nameof(fileProviders));
+ }
+ _fileProviders = fileProviders;
+ _subPath = subpath;
+ }
+
+ private void EnsureDirectoriesAreInitialized()
+ {
+ if (_directories == null)
+ {
+ _directories = new List();
+ foreach (var fileProvider in _fileProviders)
+ {
+ var directoryContents = fileProvider.GetDirectoryContents(_subPath);
+ if (directoryContents != null && directoryContents.Exists)
+ {
+ _exists = true;
+ _directories.Add(directoryContents);
+ }
+ }
+ }
+ }
+
+ private void EnsureFilesAreInitialized()
+ {
+ EnsureDirectoriesAreInitialized();
+ if (_files == null)
+ {
+ _files = new List();
+ var names = new HashSet();
+ for (var i = 0; i < _directories.Count; i++)
+ {
+ var directoryContents = _directories[i];
+ foreach (var file in directoryContents)
+ {
+ if (names.Add(file.Name))
+ {
+ _files.Add(file);
+ }
+ }
+ }
+ }
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ EnsureFilesAreInitialized();
+ return _files.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ EnsureFilesAreInitialized();
+ return _files.GetEnumerator();
+ }
+
+ public bool Exists
+ {
+ get
+ {
+ EnsureDirectoriesAreInitialized();
+ return _exists;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.FileProviders.Composite/CompositeDisposable.cs b/src/Microsoft.AspNet.FileProviders.Composite/CompositeDisposable.cs
new file mode 100644
index 00000000..aefc4ab0
--- /dev/null
+++ b/src/Microsoft.AspNet.FileProviders.Composite/CompositeDisposable.cs
@@ -0,0 +1,36 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.AspNet.FileProviders
+{
+ ///
+ /// Represents a composition of .
+ ///
+ public class CompositeDisposable : IDisposable
+ {
+ private readonly IList _disposables;
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The list of to compose.
+ public CompositeDisposable(IList disposables)
+ {
+ if(disposables == null)
+ {
+ throw new ArgumentNullException(nameof(disposables));
+ }
+ _disposables = disposables;
+ }
+
+ public void Dispose()
+ {
+ for (var i = 0; i < _disposables.Count; i++)
+ {
+ _disposables[i].Dispose();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.FileProviders.Composite/CompositeFileChangeToken.cs b/src/Microsoft.AspNet.FileProviders.Composite/CompositeFileChangeToken.cs
new file mode 100644
index 00000000..25f3a9d9
--- /dev/null
+++ b/src/Microsoft.AspNet.FileProviders.Composite/CompositeFileChangeToken.cs
@@ -0,0 +1,56 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Extensions.Primitives;
+
+namespace Microsoft.AspNet.FileProviders
+{
+ ///
+ /// Represents a composition of .
+ ///
+ public class CompositeFileChangeToken : IChangeToken
+ {
+ private readonly IList _changeTokens;
+
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The list of to compose.
+ public CompositeFileChangeToken(IList changeTokens)
+ {
+ if(changeTokens == null)
+ {
+ throw new ArgumentNullException(nameof(changeTokens));
+ }
+ _changeTokens = changeTokens;
+ }
+
+ public IDisposable RegisterChangeCallback(Action