. NET Core File System [3]: A physical file system built by PhysicalFileProvider,

Source: Internet
Author: User

. NET Core File System [3]: A physical file system built by PhysicalFileProvider,

ASP. NET Core applications use the most specific physical files, such as configuration files, View files, and static files on webpages. the abstraction of physical file systems is implemented through the FileProvider PhysicalFileProvider, this type is defined in the NuGet package "Microsoft. extensions. fileProviders. physical. We know that the System. IO namespace defines a set of APIs to operate physical directories and files. In fact, PhysicalFileProvider ultimately calls these APIs to complete related IO operations. [This Article has been synchronized to ASP. NET Core framework secrets]

 

Directory
1. PhysicalFileProvider
Ii. PhysicalFileInfo
Iii. PhysicalDirectoryInfo
4. Monitoring of physical files
V. Summary

1. PhysicalFileProvider

The following code snippet shows the definition of the PhysicalFileProvider type.

   1: public class PhysicalFileProvider : IFileProvider, IDisposable
   2: {   
   3:     public PhysicalFileProvider(string root);   
   4:      
   5:     public IFileInfo GetFileInfo(string subpath);  
   6:     public IDirectoryContents GetDirectoryContents(string subpath); 
   7:     public IChangeToken Watch(string filter);
   8:  
   9:     public void Dispose();   
  10: }

Ii. PhysicalFileInfo

A PhysicalFileProvider object is always mapped to a specific physical directory. The path of the mapped directory is provided by the root parameter of the constructor, which serves as the root directory of PhysicalFileProvider. The FileInfo object returned by the GetFileInfo method represents the file corresponding to the specified path. This is an object of the PhysicalFileInfo type. The following code snippet shows the complete definition of this type. A physical file can be represented by a System. IO. FileInfo object. A PhysicalFileInfo object is actually an encapsulation of this FileInfo object. All attributes defined in PhysicalFileInfo are derived from this FileInfo object. For the CreateReadStream method that creates a read file output stream, it returns a FileStream object created based on the absolute path of the physical file.

   1: public class PhysicalFileInfo : IFileInfo
   2: {
   3:     ...
   4:     public PhysicalFileInfo(FileInfo info);    
   5: }

For the GetFile method of PhysicalFileProvider, even if the specified path points to a specific physical file, it does not always return a PhysicalFileInfo object. Specifically, PhysicalFileProvider regards the following scenarios as "the target file does not exist" and asks GetFile to return a NotFoundFileInfo object. As the name suggests, NotFoundFileInfo indicates that a file "does not exist", that is, its Exists attribute always returns False, while other attributes become meaningless. When we call CreateReadStream to read a file that does not exist at all, a FileNotFoundException exception is thrown.

  • No physical file matches the specified path.
  • If an absolute Path (for example, "c: \ foobar") is specified, that is, Path. IsPathRooted, True is returned.
  • If the specified path points to a hidden file.
Iii. PhysicalDirectoryInfo

For PhysicalFileProvider, it uses the PhysicalFileInfo object to describe a specific physical file. For the directory description, it uses an object of the PhysicalDirectoryInfo type. Since PhysicalFileInfo encapsulates a System. IO. FileInfo object, we should think that PhysicalDirectoryInfo encapsulates the directory's DirectoryInfo object. As shown in the following code snippet, we need to provide this DirectoryInfo object when creating a PhysicalDirectoryInfo object. The return values of all attributes implemented by PhysicalDirectoryInfo are derived from this DirectoryInfo object. Because the CreateReadStream method is used to read the object content, when we call this method of a PhysicalDirectoryInfo object, an InvalidOperationException type exception will be thrown.

   1: public class PhysicalDirectoryInfo : IFileInfo
   2: {   
   3:     ...
   4:     public PhysicalDirectoryInfo(DirectoryInfo info);
   5: }

When we call the GetDirectoryContents method of PhysicalFileProvider, if the specified path points to a specific directory, this method returns an object of the EnumerableDirectoryContents type, however, EnumerableDirectoryContents is only an internal type that is invisible during programming. EnumerableDirectoryContents is a collection of FileInfo objects. This collection includes all PhysicalDirectoryInfo objects describing subdirectories and PhysicalFileInfo objects describing files. As for the Exists attribute of EnumerableDirectoryContents, it always returns True. If the specified path does not point to an existing directory or an absolute path is specified, this method returns a NotFoundDirectoryContents object with the Exsits attribute always returning False.

4. Monitoring of physical files

Let's talk about the Watch method of PhysicalFileProvider. When we call this method, PhysicalFileProvider will parse the filter expression we provide to determine the files we want to monitor, and then use the FileSystemWatcher object to monitor these files. Changes (creation, modification, rename, and deletion) to these files are reflected in the ChangeToken returned by the Watch method in real time. It is worth mentioning that the FileSystemWatcher type implements the IDisposable interface, and PhysicalFileProvider implements the same interface. The only mission of the Dispose method of PhysicalFileProvider is to release the FileSystemWatcher object.

The filter expression specified in the Watch method must be a relative path to the root directory of the current PhysicalFileProvider. You can use the "/" or "./" prefix, or use no prefix. Once we use an absolute path (for example, "c: \ test \*. txt ") or" .. /"prefix (for example," .. /test /*. txt), no matter whether the parsed files exist in the root directory of PhysicalFileProvider, these files will not be monitored. In addition, if we do not specify any filtering conditions, no files will be monitored.

The real purpose of monitoring file changes is to enable the application to detect changes to the data source in a timely manner, and then automatically perform some pre-registered rollback operations. The registration of callback can be completed directly by calling the RegisterChangeCallback method of ChangeToken. The registration callback is represented by a delegate object of the Action <object> type. For the file monitoring instance demonstrated in section 1, the corresponding program "rationale" can be rewritten to the following form.

   1: IFileProvider fileProvider = new PhysicalFileProvider(@"c:\test");
   2: fileProvider.Watch("data.txt").RegisterChangeCallback(_ = >LoadFileAsync(fileProvider), null);
   3: while (true)
   4: {
   5:     File.WriteAllText(@"c:\test\data.txt", DateTime.Now.ToString());
   6:     Task.Delay(5000).Wait();
   7: }
   8:  
   9: public static async void LoadFileAsync(IFileProvider fileProvider)
  10: {
  11:     Stream stream = fileProvider.GetFileInfo("data.txt").CreateReadStream();
  12:     {
  13:         byte[] buffer = new byte[stream.Length];
  14:         await stream.ReadAsync(buffer, 0, buffer.Length);
  15:         Console.WriteLine(Encoding.ASCII.GetString(buffer));
  16:     }
  17: }

If the above program is executed, we will find that only the first file update can be perceived, and subsequent file update operations will be automatically ignored. The root cause of this problem is that the mission of a single ChangeToken object is to send the corresponding signals when the bound data source changes for the first time, without the ability to continuously send data transformations. In fact, this can be seen from the definition of the IChangeToken interface. We know that it has a HasChanged attribute to indicate whether the data has changed, however, there is no way to "reset" This attribute. Therefore, when we need to continuously monitor a file, we need to re-call the Watch method of FileProvider in the registration callback and re-register the callback by generating ChangeToken. In addition, the RegisterChangeCallback method of ChangeToken returns the callback registration object in the form of an IDisposable object, we should call the Dispose method of the first returned callback registration object to release the callback registration object during the second registration. The following program can achieve the purpose of continuous monitoring of files.

   1: IFileProvider fileProvider = new PhysicalFileProvider(@"c:\test");
   2: Action<object> callback = null;
   3: IDisposable regiser = null;
   4: callback = _ =>
   5: {
   6:     regiser.Dispose();
   7:     LoadFileAsync(fileProvider);
   8:     fileProvider.Watch("data.txt").RegisterChangeCallback(callback, null);
   9: };
  10:  
  11: regiser = fileProvider.Watch("data.txt").RegisterChangeCallback(callback, null);

However, this programming method not only seems cumbersome, but many people who lack the knowledge of ChangeToken cannot even understand it. To solve this problem, we can use the OnChange method defined in the ChangeToken type to register the callback automatically executed when the data changes. The two methods have two parameters. The former is a Func <IChangeToken> object used to create the ChangeToken object, and the latter is the Action <object>/Action <TState> object representing the callback operation. In fact, in the instance demonstration in Section 1, we call this OnChange method.

   1: public static class ChangeToken
   2: {
   3:     public static IDisposable OnChange(Func<IChangeToken> changeTokenProducer, Action changeTokenConsumer)
   4:     {        
   5:         Action<object> callback = null;
   6:         callback = delegate (object s) {
   7:             changeTokenConsumer();
   8:             changeTokenProducer().RegisterChangeCallback(callback, null);
   9:         };
  10:         return changeTokenProducer().RegisterChangeCallback(callback, null);
  11:     }
  12:  
  13:     public static IDisposable OnChange<TState>(Func<IChangeToken> changeTokenProducer, Action<TState> changeTokenConsumer, TState state)
  14:     {
  15:         Action<object> callback = null;
  16:         callback = delegate (object s) {
  17:             changeTokenConsumer((TState) s);
  18:             changeTokenProducer().RegisterChangeCallback(callback, s);
  19:         };
  20:         return changeTokenProducer().RegisterChangeCallback(callback, state);
  21:     }
  22: }

If you use this OnChange method to replace the previously manually called ChangeToken's RegisterChangeCallback Method for callback registration, the previously cumbersome program can be replaced by the following two sentences. In fact, in "reading and monitoring file changes", we call this OnChange method.

   1: IFileProvider fileProvider = new PhysicalFileProvider(@"c:\test");
   2: ChangeToken.OnChange(() => fileProvider.Watch("data.txt"), () => LoadFileAsync(fileProvider));

V. Summary

The UML is used to summarize the overall design of the physical file system built by PhysicalFileProvider. First, a PhysicalDirectoryInfo and PhysicalFileInfo object are used to describe directories and files in the file System. They encapsulate a DirectoryInfo and FileInfo (System. IO. FileInfo) object respectively. The GetDirectoryContents method of PhysicalFileProvider returns an EnumerableDirectoryContents object (if the specified directory exists), which is the PhysicalDirectoryInfo and PhysicalFileInfo objects created based on all its subdirectories and files. When we call the GetFileInfo method of PhysicalFileProvider, if the specified file exists, the PhysicalFileInfo object describing the file is returned. As for the Watch method of PhysicalFileProvider, it finally uses FileSystemWatcher to monitor the changes of specified files.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.