none
Inheriting Filestream RRS feed

  • Question

  • Hello,

    I'd like to inherit the FileStream class (so it can be used everywhere FileStream is used), but in my constructors I need to create the files myself, not through the inherited methods. The FileStream doesn't support a constructor with zero arguments.

    My goal is to implement a class that supports atomic writes and reads. In particular, the programmer would provide one path, I'd append this with "lock" and another file ending with "tmp" which is where the data is actually written (the trick is in the access and read/write mode). On close, the tmp file is renamed to the original file. Reading would check if both files are active. There's a bit more to it than that, but I don't see a simple solution by just inheriting the class and creating the filestreams myself.

    Even if I override every member of FileStream, how do I prevent the base from then actually doing anything?

    Taking a Stream class and using that doesn't imply it can be used where FileStream is an argument. Essentially, everything should behave the same except initially opening the file and initially closing it.

    Thanks,
    Jason

    Monday, January 16, 2012 8:37 PM

Answers

  • Hi Jason,

    I've looked around and, as far as I can tell, there isn't a straight C# way to obtain what you are after, but the the Windows API have a few promising APIs. The main concern is that you will eventually need to replace the original file with the modified one and - using the methods found in System.IO.File - would mean deleting the original and moving the new file. Each of those operations are atomic, but that's obviously not good enough.

    MoveFileEx allows you to rename a file to an existing one, by passing the MOVEFILE_REPLACE_EXISTING flag. The documentation is not very explicit on the subject, but I would expect this to be an atomic operation as long as the two files are on the same volume.

    If it's ok to leave out XP, once you move into PInvoke realm you may also want to look at File transactions. I believe these would help a lot in developing your application. I would start looking at CreateFileTransacted.

    HTH
    --mc

    • Marked as answer by Jason Curl Monday, January 23, 2012 5:51 PM
    Monday, January 23, 2012 12:44 PM

All replies

  • Yes, the parameterless constructor of FileStream is declared "internal", so you cannot access it. Theoretically you could just use the inherited FileStream to map to one of your work files, for instance:

    public class MyFileStream : FileStream {
      public MyFileStream (string path) : base (path + "TMP", ...) {
        ...
      }
    }
    

    If for some reason you don't want to do that, a crude alternative would be to just let the inherited FileStream map to some dummy file. A temp file with FileOptions.DeleteOnClose is probably the best approximation of NUL: (which cannot be accessed, unfortunately).

    HTH
    --mc

     

    Monday, January 16, 2012 9:01 PM
  • Hi Mario.

    I'm going back on my design, perhaps this is possible, I'll need to go through the scenarios. I might be able to use the original file as the lock file (to arbitrate access) instead of a separate file

    I know this is off topic, but do you know if a rename of a second file to the original file with overwrite is atomic?

    Tuesday, January 17, 2012 8:07 PM
  • Hi Jason,

    I'm not sure I understood your question, especially since you cannot overwrite a file using File.Move. Can you be a little more specific about what is it that you are trying to achieve?

    Thanks
    --mc

    Wednesday, January 18, 2012 2:10 AM
  • Hi Jason,

    When you write a class inherit FileStream class, if you don't want to implement methods of base class, you need to rewrite the method with the same name and arguments. Then you can implement any actions in rewrite methods. If you do not rewrite methods, it will call the methods in base class.

    For example,

    ClassA
    {
       public void Method1(){//...}
       public void Method2(){//...}
    }
    ClassB:ClassA
    {
       public void Method1(){//...}
    }
    
    When you create instance of ClassB: 
    ClassB clB=new ClassB();
    

    Then call clB.Method1 will implements Method1 in ClassB, call clB.Method2 will implements Method2 in ClassA.

    Thanks.


    Paul Zhou [MSFT]
    MSDN Community Support | Feedback to us
    Thursday, January 19, 2012 8:00 AM
  • Hi Paul,

    Thanks for the reply. If you try to inherit from the FileStream class as a base class, you must provide the base in the constructor as the default constructor is "internal". Thus, I must provide

    ClassA: FileStream {

      public ClassA(): this(xxx) { }

    }

     

    Just doing the following won't compile, as I don't belong in the mscorlib assembly:

    ClassA: FileStrem {

      public Class() { }

    }

     

    Which for the FileStream class actually opens a file. I'm actually trying to implement the functionality from the MSDN blog:

    http://blogs.msdn.com/b/adioltean/archive/2005/12/28/507866.aspx

    But to prevent multiple access and a race condition when multiple files write/read simultaneously, I need to create a lock file, then work on the original file and a temporary file. Before I even open a file, I need to check the filesystem, so it's not appropriate to use the example:
    public MyFileStream (string path) : base (path + "TMP", ...) {

     

    To answer Mario Cossi, I was looking for a way of avoiding a backup file as in the blog above. I think if I could rename the TMP file over the original file, and the removal and the rename was atomic, then this would save a step and make implementation a little simpler.

    So I haven't found a solution yet - I'm currently thinking about the design inheriting from "Stream". If the interface expects a FileStream, then I'll have to assume that it's a bad design.

    Thanks,
    jason.

    Thursday, January 19, 2012 5:02 PM
  • Hi Jason,

    I've looked around and, as far as I can tell, there isn't a straight C# way to obtain what you are after, but the the Windows API have a few promising APIs. The main concern is that you will eventually need to replace the original file with the modified one and - using the methods found in System.IO.File - would mean deleting the original and moving the new file. Each of those operations are atomic, but that's obviously not good enough.

    MoveFileEx allows you to rename a file to an existing one, by passing the MOVEFILE_REPLACE_EXISTING flag. The documentation is not very explicit on the subject, but I would expect this to be an atomic operation as long as the two files are on the same volume.

    If it's ok to leave out XP, once you move into PInvoke realm you may also want to look at File transactions. I believe these would help a lot in developing your application. I would start looking at CreateFileTransacted.

    HTH
    --mc

    • Marked as answer by Jason Curl Monday, January 23, 2012 5:51 PM
    Monday, January 23, 2012 12:44 PM
  • Hello Mario,

    Thanks for the pointer. I've tested the function, it does indeed overwrite the file, and if the file is in use, GetLastError returns 5 (E_ACCESSDENIED), so I can also see when an exception occurred.

    Unfortunately TxF is out of the question, as it isn't supported on Network file stores.

    Thanks,
    Jason.

    Monday, January 23, 2012 5:51 PM