0

I'm using WCF and trying to pass an object of class System.IO.FileInfo from server to client. However, the member contents such as CreationTime gets reset to year 1600 in this case. My guess is that there are some kind of serialization issues. What I'm able to get around is to create a wrapper class such as this and pass that through WCF, then everything works.

[DataContract]
public class dcFileInfo
{
    [DataMember]
    public string Name { get; set; }

    [DataMember] 
    public DateTime CreationTime { get; set; }

    public dcFileInfo(FileInfo fi)
    {
        this.Name = fi.Name;
        this.CreationTime = fi.CreationTime;
    }
}

However, this seems to be redundant (if not looks stupid). Is there an easier way (e.g., using some keywords such as "Serializable")? Please help!

3
  • Are you using .NET Framework or .NET Core? And which version? Commented Jun 4 at 23:24
  • .Net Framework 4.8.1. Commented Jun 5 at 22:33
  • Correction: Passing FileInfo through WCF DOES NOT work. I've changed the code above where I have to pass CreationTime separately. Commented Jun 5 at 22:47

1 Answer 1

1

The basic problem here is that FileInfo is not a data object, it is a view into the local file system that allows live querying and manipulation of a specific file. As such, after deserialization its properties return information about an identically named file on the server, not the original properties on the client - even though FileInfo is marked as [Serializable] in .NET Framework 4.8.1.[1] From the docs:

FileInfo Class

Provides properties and instance methods for the creation, copying, deletion, moving, and opening of files, and aids in the creation of FileStream objects. This class cannot be inherited.

The FileInfo class [also] provides properties that enable you to retrieve information about a file.

This explains why CreationTime resets to year 1600: when a file with the specified name does not exist on the receiving system, FileInfo returns this value to indicate an error. From the docs for the base class method FileSystemInfo.CreationTime:

FileSystemInfo.CreationTime Property

Gets or sets the creation time of the current file or directory.

If the file described in the FileSystemInfo object does not exist, this property returns 12:00 midnight, January 1, 1601 A.D. (C.E.) Coordinated Universal Time (UTC), adjusted to local time.

For confirmation, we can check the .NET Framework 4.8.1 reference source for streaming constructors for FileInfo and FileSystemInfo. Both populate only the name, full path and original path from the provided SerializationInfo. Other properties such as the creation time are not populated and so are retrieved from the local file system instead.

As a workaround, you will need to create a DTO for FileInfo for serialization purposes -- even though you stated in your question that "this seems to be redundant (if not looks stupid)" there doesn't seem to be any other option. To make its use a little easier, you could add an implicit operator from FileInfo to your DTO:

[DataContract(Name = "FileInfo")]
public class FileInfoDTO
{
    public static implicit operator FileInfoDTO (FileInfo fileInfo) { return fileInfo == null ? null : new FileInfoDTO(fileInfo); }

    public FileInfoDTO() { } // Parameterless constructor required for serialization
    public FileInfoDTO(FileInfo fileInfo)
    {
        if (fileInfo == null)
            throw new ArgumentNullException("fileInfo");
        this.Attributes = fileInfo.Exists ? fileInfo.Attributes : null;
        this.CreationTime = fileInfo.CreationTime;
        this.CreationTimeUtc = fileInfo.CreationTimeUtc;
        this.DirectoryName = fileInfo.DirectoryName;
        this.Exists = fileInfo.Exists;
        this.Extension = fileInfo.Extension;
        this.IsReadOnly = fileInfo.IsReadOnly;
        this.LastAccessTime = fileInfo.LastAccessTime;
        this.LastAccessTimeUtc = fileInfo.LastAccessTimeUtc;
        this.LastWriteTime = fileInfo.LastWriteTime;
        this.LastWriteTimeUtc = fileInfo.LastWriteTimeUtc;
        this.Length = fileInfo.Exists ? fileInfo.Length : null;
        this.Name = fileInfo.Name;
    }
    
    // TODO: remove any properties you don't need to serialize
    [DataMember] public string Name { get; set; }
    [DataMember] public DateTime CreationTime { get; set; }
    [DataMember] public DateTime CreationTimeUtc { get; set; }
    [DataMember] public string DirectoryName { get; set; }
    [DataMember] public bool Exists { get; set; }
    [DataMember] public string Extension { get; set; }
    [DataMember] public bool IsReadOnly { get; set; }
    [DataMember] public DateTime LastAccessTime { get; set; }
    [DataMember] public DateTime LastAccessTimeUtc { get; set; }
    [DataMember] public DateTime LastWriteTime { get; set; }
    [DataMember] public DateTime LastWriteTimeUtc { get; set; }
    // When the file does not exist, (FileAttributes)(-1). is returned.  This cannot be serialized, so make the property nullable instead.
    [DataMember] public FileAttributes? Attributes { get; set; }
    // When the file does not exist, FileInfo.Length throws an exception.  Make the property nullable instead.
    [DataMember] public long? Length { get; set; }
}

Then replace FileInfo with FileInfoDTO in your data models:

[DataContract]
public class Model
{
    [DataMember]
    public FileInfoDTO FileInfo { get; set; }
}

And you will be able to initialize the DTO values from your existing FileInfo values using the implicit operator:

var model = new Model() { FileInfo = new FileInfo(fileName) };

Demo fiddle here.


[1] While your question is specifically about .NET Framework, the situation is even worse in .NET Core. There FileInfo is not marked as serializable and so cannot be serialized by WCF at all. See ASP.NET Core serialized object with a FileInfo returns incomplete JSON for details, and this fiddle for a demo.
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.