RazorLight icon indicating copy to clipboard operation
RazorLight copied to clipboard

2.0 beta file cache not clearing properly

Open jjxtra opened this issue 5 years ago • 3 comments

In the 2.0 beta branch, using a file system project, the cache is not being cleared. You can write a cshtml file, attempt to retrieve from cache, not found, so you compile it and it is added to cache.

Later you can append new text to the file, then attempt to read from cache. It will still be in the cache even though file contents have changed.

One problem I believe is not using .Refresh on the file info before accessing the last write time. Another problem is that HasChanged should not change from true back to false until the file contents are actually read.

The following code fixes the problem (from MIT license MailDemon project at https://github.com/DigitalRuby/MailDemon):

    public class MailDemonFileChangeToken : IChangeToken
    {
        private readonly FileInfo fileInfo;
        private DateTime lastWriteTime;

        public MailDemonFileChangeToken(string viewPath)
        {
            fileInfo = new FileInfo(viewPath);
            lastWriteTime = fileInfo.LastWriteTimeUtc;
        }

        public bool ActiveChangeCallbacks => false;

        public bool HasChanged
        {
            get
            {
                fileInfo.Refresh();
                if (fileInfo.LastWriteTimeUtc != lastWriteTime)
                {
                    hasChanged = true;
                    lastWriteTime = fileInfo.LastWriteTimeUtc;
                }
                return hasChanged;
            }
        }

        public IDisposable RegisterChangeCallback(Action<object> callback, object state) => EmptyDisposable.Instance;
        internal bool hasChanged;

        internal class EmptyDisposable : IDisposable
        {
            public static EmptyDisposable Instance { get; } = new EmptyDisposable();
            private EmptyDisposable() { }
            public void Dispose() { }
        }
    }

    public class MailDemonFileProjectItem : RazorLight.Razor.RazorLightProjectItem
    {
        private readonly string templateKey;
        private readonly string fullPath;

        public MailDemonFileProjectItem(string rootDirectory, string templateKey)
        {
            this.templateKey = templateKey;
            if (Path.IsPathFullyQualified(templateKey))
            {
                fullPath = templateKey;
            }
            else
            {
                fullPath = Path.Combine(rootDirectory, templateKey);
            }
            ExpirationToken = new MailDemonFileChangeToken(fullPath);
        }

        public override string Key => templateKey;
        public override bool Exists => File.Exists(fullPath);
        public override Stream Read()
        {
            byte[] bytes = File.ReadAllBytes(fullPath);
            (ExpirationToken as MailDemonFileChangeToken).hasChanged = false;
            return new MemoryStream(bytes);
        }
    }

jjxtra avatar Feb 26 '19 14:02 jjxtra

@jjxtra Interesting - I imagine this still exists in 2.0.0-beta3. However, what OS are you using for file change detection? NTFS LastWriteTime is not reliable, and behaves screwy when you re-name the file and create a new file with the same name as the old one.

jzabroski avatar Dec 18 '19 01:12 jzabroski

Windows 10 and Ubuntu

jjxtra avatar Dec 18 '19 02:12 jjxtra

Would you be willing to submit a patch? Thanks

jzabroski avatar Dec 18 '19 02:12 jzabroski