This abstract file system organizes files in the form of directories . We can use it to read the contents of a file , monitor the directory or files, and get notifications of changes in a timely manner . Since the IFileProvider object provides monitoring functions for file system transformations, similar functions in .NET Core are mostly implemented using an IChangeToken object, so it is necessary to first understand IChangeToken before going into the IFileProvider.

I. IChangeToken

The literally understood IChangeToken object is a “Token” associated with a set of monitoring data that can be sent out in a timely manner when a data change is detected. If the data associated with IChangeToken changes, its HasChanged property will become True. We can call its RegisterChangeCallback method to register a callback that can be executed automatically when the data changes. This method will return an IDisposable object, and we will unregister the callback by using its Dispose method. As for the other property of the IChangeToken interface, ActiveChangeCallbacks , which indicates whether the registered callback operation needs to be actively performed when the data changes.

Public  interface IChangeToken
    BOOL HasChanged { GET ;}
     BOOL ActiveChangeCallbacks { GET ;}
    IDisposable RegisterChangeCallback(Action < object > callback, object state);

.NET Core provides several native IChangeToken implementation types, and we most often use an implementation called CancellationChangeToken . The implementation principle of CancellationChangeToken is very simple. It basically sends notifications with the familiar CancellationToken object in the following form.



Public  class CancellationChangeToken : IChangeToken
    Private  readonly CancellationToken _token;
     public CancellationChangeToken(CancellationToken token) => _token = token;
     public  bool HasChanged => _token.IsCancellationRequested; 
     public  bool ActiveChangeCallbacks => true ;    
     public IDisposable RegisterChangeCallback(Action< object > callback, object state) => _token. Register(callback, state);



In addition to the CancellationChangeToken, we sometimes use an implementation called CompositeChangeToken . As the name implies, CompositeChangeToken represents a composite IChangeToken object composed of multiple IChangeTokens. As shown in the following code snippet, we need to provide these IChangeToken objects when calling the constructor to create a CompositeChangeToken object. For a CompositeChangeToken object, as long as any of the IChangeTokens that make up it changes, its HasChanged property will become True, and the registered callback will naturally be executed. As for the ActiveChangeCallbacks property, as long as any IChangeToken property of the same name returns True, the property will return True.



Public  class CompositeChangeToken : IChangeToken
    Public  bool   ActiveChangeCallbacks { get ; }
     public IReadOnlyList<IChangeToken> ChangeTokens { get ; }
     public  bool   HasChanged { get ; }
    Public CompositeChangeToken(IReadOnlyList<IChangeToken> changeTokens);   
     public IDisposable RegisterChangeCallback(Action< object > callback, object state);   



We can directly call the RegisterChangeCallback method provided by IChangeToken to register the callback operation after receiving the data change notification, but the more common way is to directly call the following two OnChange method overloads provided by the static type ChangeToken to perform callback registration. The first argument of the method needs to be specified as a Func<IChangeToken> delegate that provides the IChangeToken object.

Public  static  class ChangeToken
    Public  static IDisposable OnChange(Func<IChangeToken> changeTokenProducer, Action changeTokenConsumer) ;
     public  static IDisposable OnChange<TState>(Func<IChangeToken> changeTokenProducer, Action<TState> changeTokenConsumer, TState state) ;

Second, IFileProvider

After understanding what an IChangeToken is, we focus on moving to the file system’s core interface, IFileProvider, which is defined in the NuGet package “Microsoft.Extensions.FileProviders.Abstractions”. We did a few simple example demonstrations in ”
Abstract “File System”

, which actually embody the three basic functions hosted by the file system, and these three basic functions are respectively reflected in the IFileProvider interface as shown below. In the method.

Public  interface IFileProvider
    IFileInfo GetFileInfo( string subpath);
    IDirectoryContents GetDirectoryContents( string subpath);
    IChangeToken Watch( string filter);

Third, IFileInfo

Although the file system uses directories to organize files, both the directory and the file are represented by an IFileInfo object. The specific directory or file is determined by the IsDirectory property of IFileInfo . For an IFileInfo object, we can use the read-only property Exists to determine whether the specified directory or file exists. As for the other two properties, Name and PhysicalPath , they represent the name and physical path of the file or directory, respectively. The property LastModified returns a timestamp indicating when the directory or file was last modified. For an IFileInfo object that represents a specific file, we can use the attribute Length to get the byte length of the file content. If we want to read the contents of the file, we can do so by means of the Stream object returned by the CreateReadStream method.



Public  interface IFileInfo
    BOOL the Exists { GET ;}
     BOOL   IsDirectory { GET ;}
     String the Name { GET ;}
     String PhysicalPath { GET ;}
    DateTimeOffset LastModified { get ; }
     long Length { get ; }

    Stream CreateReadStream();



The GetFileInfo method of the IFileProvider interface will get the IFileInfo object representing the file according to the specified path . In other words, although an IFileInfo object can be used to describe directories and files, the purpose of the GetFileInfo method is to get the file returned by the specified path instead of the directory (I personally disagree with this ambiguous API design). In general, the method always returns a specific IFileInfo object regardless of whether the specified file exists, because the existence or non-existence of the target file is determined by the Exists attribute of the object.

Fourth, IDirectoryContents

If you want to get the contents of a directory, such as how many files you need to view or if the subdirectories are included in this directory, we can call the GetDirectoryContents method of the IFileProvider object and take the path of the directory as a parameter. The contents of the directory are represented by the IDirectoryContents object returned by this method. As shown in the following code snippet, an IDirectoryContents object is actually a collection of IFileInfo objects. All the IFileInfos that make up this collection are naturally a description of all the files and subdirectories contained in this directory. Like the GetFileInfo method, the GetDirectoryContents method always returns a specific IDirectoryContents object, regardless of whether the specified directory exists. Its Exists property will help us determine if the specified directory exists.

Public  interface IDirectoryContents : IEnumerable<IFileInfo>
    Bool Exists { get ; }

Fifth, monitoring the directory or file update

If we want to monitor the changes in the directory or file where IFileProvider is located, we can call its Watch method, provided that the corresponding IFileProvider object provides such monitoring. This method accepts a string type parameter filter, which we can use to specify a “File Globing Pattern” expression (hereafter referred to as the Globing Pattern expression) to filter the target directory or file to be monitored.

Globing Pattern expressions are much simpler than regular expressions. They only contain a “wild” of ” * “. If you say that it contains two wildcards, then the other wildcard is ” ** “. The Globing Pattern expression is represented as a file path, where “*” represents all characters that do not include a path separator (“/” or “\”), and “**” represents all characters including the path separator. . The table below shows several typical Globing Pattern expressions and the file matching semantics of their code.

Globing Pattern expression


Matching file






All files named ” settings ” under the subdirectory ” src/foobar/foo/ ” (without its subdirectories) , such as settings.json , settings.xml, and settings.ini .






All .cs files in the subdirectory ” src/foobar/foo/ ” (without its subdirectories) .




All files in the subdirectory ” src/foobar/foo/ ” (without its subdirectories).




All .cs files under the subdirectory ” src ” (with its subdirectories) .


In general, whether you call the path to the target file or directory specified by the GetFileInfo or GetDirectoryContents methods of the IFileProvider object, or the filter expression specified by the call to the Watch method, is a relative path to the root directory of the current IFileProvider object. The specified path can be prefixed with the “/” character, but this prefix is ​​unnecessary. In other words, the two sets of programs shown below are completely equivalent.

Path does not contain the prefix “/”

Var dirContents = fileProvider.GetDirectoryContents( " foobar " );
 var fileInfo = fileProvider.GetFileInfo( " foobar/foobar.txt " );
 var changeToken = fileProvider.Watch( " foobar/*.txt " );

The path contains the prefix “/”

Var dirContents = fileProvider.GetDirectoryContents( " /foobar " );
 var fileInfo = fileProvider.GetFileInfo( " /foobar/foobar.txt " );
 var changeToken = fileProvider.Watch( " /foobar/*.txt " );

In general, the file system with the IFileProvider object as the core is very simple in design. In addition to the IFileProvider interface, the file system involves other objects, such as IDirectoryContents, IFileInfo, and IChangeToken. The UML shown in the following figure shows these interfaces and the relationships between them.


ASP.Net Core3 File system[1] Abstract File System

ASP.Net Core3 File system[2] Overall design

ASP.Net Core 3 File System [3] Physical File System

ASP.Net Core3 File system[4] Embedded File systeme System

Orignal link: