-
Notifications
You must be signed in to change notification settings - Fork 82
Conversation
The CombinedFileProvider merges the content of the directories
Hi @mgrosperrin, I'm your friendly neighborhood .NET Foundation Pull Request Bot (You can call me DNFBOT). Thanks for your contribution! The agreement was validated by .NET Foundation and real humans are currently evaluating your PR. TTYL, DNFBOT; |
"projects": ["src"] | ||
"projects": [ "src" ], | ||
"sdk": { | ||
"version": "1.0.0-beta8" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this modification should be part of the PR. Especially not since the latest stable version is RC1, not beta8.
Remove the sdk version in global.json
Hi @MartinJohns, |
/// <returns>The file information. Caller must check Exists property. This will be the first existing <see cref="IFileInfo"/> returned by the provided <see cref="IFileProvider"/> or a not found <see cref="IFileInfo"/> if no existing files is found.</returns> | ||
public IFileInfo GetFileInfo(string subpath) | ||
{ | ||
foreach (var fileProvider in _fileProviders) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: you could change _fileProviders
's type to IFileProvider[]
to avoid allocating an enumerator here (you don't have to explicitly use a for
loop, since the compiler is supposed to optimize that for you).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand your point: IFileProvider[]
implements IEnumerable<IFileProvider>
so when an enumerator will be allocated (which will not be in your case) ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a variable is declared as an array, the compiler automatically replaces the foreach
loop by a for
loop and directly accesses the items via array[index]
without having to instantiate a new enumerator. The compiler can't apply the same optimization when the type is IEnumerable<>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I'm happy to learn something today!
Definitely a must have! 😄 |
|
||
public CombinedDirectoryContents(IEnumerable<IEnumerable<IFileInfo>> listOfFiles) | ||
{ | ||
foreach (var files in listOfFiles) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code path will immediately enumerate all the files exposed by the different providers, which may take some time. You should maybe consider deferring this operation to the last moment (in GetEnumerator
) and using an iterator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have made the assumption that the directory contents will be pre-calculated (based on the two other providers), but you're right. I can move it to a Lazy<IEnumerable<IFileInfo>>
(to avoiding dealing with the thread-safe and evaluating only once issues).
/// Initializes a new instance of the <see cref="CombinedFileProvider" /> class using a list of file provider. | ||
/// </summary> | ||
/// <param name="fileProviders"></param> | ||
public CombinedFileProvider(params IFileProvider[] fileProviders) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd avoid params. Just take an IEnumerable<IFileProvider>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A params
overload definitely makes sense, because that's how people will use this combined thingy:
options.FileProvider = new CombinedFileSystemProvider(
new EmbeddedFileProvider(...),
new EmbeddedFileProvider(...));
Since the file provider is a hot path, using IEnumerable<IFileProvider>
is not really appropriate. Not to mention that, semantically, it's not immediately clear that the order matters when using IEnumerable
. IMHO, an array or a list is much more appropriate in this case. That said, nothing prevents you from adding an IEnumerable<>
overload that calls ToArray()
or ToList()
to avoid allocating enumerators in GetFileInfo
/GetDirectoryContents
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A params overload definitely makes sense, because that's how people will use this combined thingy:
Not really. Just make an array if you need that:
new CombinedFileSystemProvider(new[] {
new EmbeddedFileProvider(...),
new EmbeddedFileProvider(...) });
I'd keep the params array overload for a static factory method instead of baking it into the ctor.
Converting it to an array or list is more appropriate than enforcing an array be passed in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @davidfowl, is it a general purpose to avoid params, or just in this situation ?
Just a question. Is this provider going to allow to have Views in Class Library? If yes, how can we use it? If not, please ignore my question :). Thanks. |
@VaclavElias You can do that already. services.Configure<RazorViewEngineOptions>(options =>
{
options.FileProvider = yourFileProvider;
}); By the default the used file provider is a The here proposed |
Thanks @MartinJohns making it clear for me! |
|
||
public CombinedFileChangeToken(List<IChangeToken> changeTokens) | ||
{ | ||
_changeTokens = changeTokens ?? new List<IChangeToken>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: looks like changeTokens
cannot be null when used by Watch
. I'd personally removed the null coalescing operator 😄
…f IFileProvider and files
} | ||
public void Dispose() | ||
{ | ||
for (int i = 0; i < _disposables.Count; i++) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple of other places with the same issue.
⌚ |
cc @Eilon for |
…tead of Lazy) Add tests for the combined IDsposable Fix formatting and typos
Yeah |
Hi, |
Sorry, GitHub doesn't send an email when you push an update. A comment saying you had it updated would be great (for future use). I'll have a look in a bit. |
Oh OK, didn't know that 😄 |
@pranavkm Could you merge once it's good? @mgrosperrin Thanks for the contribution! |
|
||
namespace Microsoft.AspNet.FileProviders | ||
{ | ||
internal class CompositeDirectoryContents : IDirectoryContents |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you make these types public? No reason to have them be internal.
Looks good. If you could get the last few changes. |
This might be a crazy idea given the state of so much code in the repositories.. But can't we start to comment the code? Add some documentation? |
Add a constructor to CompositeFileProvider with IEnumerable
Hi, |
I'll get this merged in soon. |
Merged in 15c9f1d |
Thanks for the PR! |
You're welcome! |
This PR is for the issue #49
The behavior for each method is:
GetFileInfo
: returns the firstIFileInfo
of all providedIFileProvider
where theExists
property istrue
,GetDirectoryContents
: merges the the contents of all providedIFileProvider
. On duplicate entries (based onName
property), only the first entry is kept,Watch
: returns aIChangeToken
that merges theIChangeToken
of all providedIFileProvider
. When one of the token raises the change notification, the returned token raises it.