[Request-for-help] API for temp file.
От: Sinix  
Дата: 24.09.16 09:17
Оценка:
Long story short: у нас в CodeJam среди кучи других полезняшек есть микро-хелпер для работы с временными файлами: завёл-поработал-оноавтоматомгрохнулось.

Выглядит как-то так:
            using (var tempFile = TempData.CreateFile())
            {
                DoSomethingWith(tempFile.Path);
            }
            // file is deleted for now


И всё вроде было отлично, пока не пришёл уважаемый Vasiliy2
Автор: Vasiliy2
Дата: 22.09.16
и не задал нехороший вопрос: а что нужно делать, если временный файл удалить не получается? С контрольным аргументом: куча последовательных вызовов выкушает всё место и наступит полная печаль

Текущее поведение вроде как соответствует FDG (ага, нет явных доводов за любую позицию — обращайся к FDG) и проглатывает исключения, связанные с ошибками удаления файла. Молча проглатывает. Почему так — см вот эту
Автор: Sinix
Дата: 22.09.16
кучу цитат. Если коротко — сигналить "всё пропало, шеф" при диспозе не очень хорошо, т.к. это может привести к ещё худшим последствиям.

И теперь собственно вопрос: а как быть-то? Основные варианты собраны вот тут, кому лень открывать —
  скопировал
    public class TempDataUseCases
    {
        public void CaseA_NoHelpers()
        {
            // weird one.

            var fileName = "1.txt";
            Exception deletedEx = null;
            try
            {
                File.Create(fileName).Close();

                Process(fileName);
            }
            finally
            {
                try
                {
                    File.Delete(fileName); // we do expect this will throw.
                }
                catch (Exception ex)
                {
                    deletedEx = ex;
                }
            }

            if (deletedEx != null)
            {
                HandleDeleteFailure(fileName, deletedEx);
            }
        }

        public void CaseB_LetItCrash()
        {
            // ok if failure is handled by caller
            using (var tempFile = TempData.CreateFile(throwOnDisposeFailure: true))
            {
                Process(tempFile.FileName);
            } // dispose will throw
        }

        public void CaseC_LetItCrashHandle()
        {
            // BAD BAD BAD: we need to store fileName somewhere.
            string fileName = null;
            try
            {
                using (var tempFile = TempData.CreateFile(throwOnDisposeFailure: true))
                {
                    fileName = tempFile.FileName;
                    Process(tempFile.FileName);
                }
            }
            catch (Exception ex) when (fileName != null) // HACK. Will not work on C#5 or below.
            {
                HandleDeleteFailure(fileName, ex);
            }
        }

        public void CaseD_TryDelete()
        {
            using (var tempFile = TempData.CreateFile())
            {
                Process(tempFile.FileName);

                if (!tempFile.TryClose())
                {
                    HandleDeleteFailure(tempFile.FileName, null); // no exception info available.
                }
            }
        }

        public void CaseE_EnsureDeleted()
        {
            // kinda ok
            using (var tempFile = TempData.CreateFile())
            {
                Process(tempFile.FileName);

                try
                {
                    tempFile.EnsureDelete();
                }
                catch (Exception ex)
                {
                    HandleDeleteFailure(tempFile.FileName, ex);
                }
            }
        }

        public void CaseF_ManualDelete()
        {
            // kinda ok
            using (var tempFile = TempData.CreateFile())
            {
                Process(tempFile.FileName);

                try
                {
                    File.Delete(tempFile.FileName);
                }
                catch (Exception ex)
                {
                    HandleDeleteFailure(tempFile.FileName, ex);
                }
            }
        }


        public void CaseG_Fallback()
        {
            // The best?
            using (var tempFile = TempData.CreateFile(
                (f, ex) => HandleDeleteFailure(f.FileName, ex)))
            {
                Process(tempFile.FileName);
            }
        }

        private void Process(string tempFile) { }

        private void HandleDeleteFailure(string fileName, Exception exception) { }
    }

    // Stub implementation. Unusable by design.
    public class TempData : IDisposable
    {
        public static TempData CreateFile() => new TempData();
        public static TempData CreateFile(bool throwOnDisposeFailure) => new TempData();
        public static TempData CreateFile(Action<TempData, Exception> deleteFallback) => new TempData();

        public string FileName => null;

        public void EnsureDelete()
        {
            throw new IOException("Cannot delete the file.");
        }

        public bool TryClose() => false;

        public void Dispose()
        {
            // ok or throw
        }
    }


Варианты и предложения приветствуются

UPD. И да, FileOptions.DeleteOnClose подходит не всегда. Чтоб не спорить — у нас точно такой же хелпер есть для директорий. Что с ними делать будем? ; )
Отредактировано 25.09.2016 18:42 Sinix . Предыдущая версия .
Re: [Request-for-help] API for temp file.
От: vorona  
Дата: 24.09.16 15:36
Оценка: +1
Здравствуйте, Sinix, Вы писали

DeleteOnClose
Отредактировано 24.09.2016 15:41 vorona . Предыдущая версия .
Re[2]: [Request-for-help] API for temp file.
От: Sinix  
Дата: 24.09.16 15:49
Оценка:
Здравствуйте, vorona, Вы писали:

V>DeleteOnClose

Дык давно уже.

В нагрузку к нему идут аналогичные для файла / директории, вопрос про них.
Re: [Request-for-help] API for temp file.
От: VladCore  
Дата: 24.09.16 19:17
Оценка: +1
Здравствуйте, Sinix, Вы писали:


S>Варианты и предложения приветствуются


Если проблема архитектурная, то должно быть где-то некое GC, удаляющее старые временные файлы. Так делается в хороших персистент кьюзах.

Если проблема техническая, то очень хорошо помогает AppDomain.CurrentDomain.DomainUnload, правда доступно без приседаний только в ASP.NET под IIS и в unit-тестах. В core его к сожалению нет.

На старых windows всё ещё хорошо работает удаление при перезагрузке.
Re[2]: [Request-for-help] API for temp file.
От: Sinix  
Дата: 24.09.16 19:35
Оценка:
Здравствуйте, VladCore, Вы писали:

VC>Если проблема архитектурная, то должно быть где-то некое GC, удаляющее старые временные файлы. Так делается в хороших персистент кьюзах.

VC>Если проблема техническая, то очень хорошо помогает AppDomain.CurrentDomain.DomainUnload, правда доступно без приседаний только в ASP.NET под IIS и в unit-тестах. В core его к сожалению нет.

Ну вот у меня примерно такое же мнение: тут надо писать свой код, опционально — в виде сервиса или простой задачи в шедулере. Т.е. уборка мусора в случае it happens — не ответственность TempFile. И текущее поведение: если не удаляется, то ничего не делаем — в принципе правильное.


Не факт, что моё мнение — единственно верное, поэтому я с радостью заслушаю все предложения.
Re[3]: [Request-for-help] API for temp file.
От: #John Европа https://github.com/ichensky
Дата: 24.09.16 22:19
Оценка:
Здравствуйте, Sinix, Вы писали:

И текущее поведение: если не удаляется, то ничего не делаем — в принципе правильное.

Если временный файл не удаляется, закрываем на него все наши хендлеры и пусть ОС решает когда его удалять.
Підтримати Україну у боротьбі з країною-терористом.

https://prytulafoundation.org/
https://u24.gov.ua/

Слава Збройним Силам України!!! Героям слава!!!
Re[4]: [Request-for-help] API for temp file.
От: Sinix  
Дата: 25.09.16 08:40
Оценка:
Здравствуйте, #John, Вы писали:

J>Если временный файл не удаляется, закрываем на него все наши хендлеры и пусть ОС решает когда его удалять.

Это далеко не всегда возможно. К примеру, если файл требуется скормить чужому софту, который не умеет в FileShare.
Если такого не требуется, то есть TempData.CreateFileStream(), который открывает поток с FileOptions.DeleteOnClose.

Ок, раз второй человек настаивает на DeleteOnClose — забыли про файлы, берём такой же хелпер, но с перламутровыми пуговицами для директорий. С ним что делать?
Re: [Request-for-help] API for temp file.
От: rm822 Россия  
Дата: 25.09.16 19:23
Оценка: 48 (1) +4
S>а что нужно делать, если временный файл удалить не получается?
Побуду немного К.О.
а) а что может сделать пользователь библиотеки, если она вдруг выкинет эксепш?
б) рассчитывает ли кто-нибудь в реальности что такая фигня может случиться?

Лично я встречался с такой фигней на практике. Админы что-то перемудрили с настройками домена и roaming profiles, и файлы тупо не удалялись первые секунд 5.
Код во первых не был рассчитан на то что там вылетит какой-то эксепшн, а во вторых никакого варианта кроме как проигнорить это небыло.
Чего я точно не хочу и как пользователь софта и как разработчик, так это чтобы из-за каких-то ****-админов софт падал или говорил вскую непонятную хрень пользователям.

Поэтому текущее поведение — почти правильное. Я бы дополнительно вывел в трейс эксепшн и добавил файр эвента аля UnexpectedException, кому надо запишут в лог
Re[2]: [Request-for-help] API for temp file.
От: Sinix  
Дата: 25.09.16 20:23
Оценка:
Здравствуйте, rm822, Вы писали:

R>Поэтому текущее поведение — почти правильное. Я бы дополнительно вывел в трейс эксепшн и добавил файр эвента аля UnexpectedException, кому надо запишут в лог


Принято!

Как это дело оформлять будем? Через static-событие, делегатом в конструкторе TempFile, простым дампом в Trace или ещё как?

Как вариант, можно завести доп. источник для всей библиотеки (по аналогии с PresentationTraceSources в WPF) и писать туда.
Re[5]: [Request-for-help] API for temp file.
От: aloch Россия  
Дата: 26.09.16 11:13
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Ок, раз второй человек настаивает на DeleteOnClose — забыли про файлы, берём такой же хелпер, но с перламутровыми пуговицами для директорий. С ним что делать?


А какой глубокий смысл во временных директориях? Никогда их не создавал, всегда просто файлы в TEMP, если нужно много файлов — то пусть будет много. Директории, по моему, нужны только установщикам, и то постаравшись можно без них обойтись.


Re[3]: [Request-for-help] API for temp file.
От: Vasiliy2  
Дата: 26.09.16 11:21
Оценка:
Здравствуйте, Sinix, Вы писали:


S>Как это дело оформлять будем? Через static-событие, делегатом в конструкторе TempFile, простым дампом в Trace или ещё как?


S>Как вариант, можно завести доп. источник для всей библиотеки (по аналогии с PresentationTraceSources в WPF) и писать туда.


У меня, кстати, тоже была мысль про событие, так что поддерживаю.
Еще, по-моему, неплох вариант, который уже частично реализован в DisposableExtensions — передача хендлера, который реализует нужную обработку.
Перегрузить конструктор. Кому нужна обработка, использует его. Кому не нужна — обойдется прежним конструктором. Так можно основные варианты использования будет закрыть, поскольку в обсуждении высказывались мысли как за обработку, так и без нее.
Re[6]: [Request-for-help] API for temp file.
От: Sinix  
Дата: 26.09.16 11:25
Оценка:
Здравствуйте, aloch, Вы писали:

A>А какой глубокий смысл во временных директориях?


Ну вот пара реальных сценариев:
1. Создать папку, заполнить, скормить стороннему софту, грохнуть.
2. Создать временную папку при запуске приложения (необязательно с рандомным именем), грохнуть по завершению.
Re[4]: [Request-for-help] API for temp file.
От: Sinix  
Дата: 26.09.16 11:32
Оценка: +1
Здравствуйте, Vasiliy2, Вы писали:

V>У меня, кстати, тоже была мысль про событие, так что поддерживаю.


Ну вот да, пока склоняюсь к варианту от rm822: заводим TraceSource для всей библиотеки CodeJam и логируем проглоченное исключение туда, кого не устраивает — стройным шагом идут писать свой хелпер. Иначе логика по разруливанию исключений получается заметно сложнее самого хелпера.
Re: [Request-for-help] API for temp file.
От: TK Лес кывт.рф
Дата: 26.09.16 11:45
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Варианты и предложения приветствуются


Сделайте по аналогии с DispatcherUnhandledExceptionFilterEventArgs.RequestCatch
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re: [Request-for-help] API for temp file.
От: Pavel Dvorkin Россия  
Дата: 26.09.16 14:16
Оценка:
Здравствуйте, Sinix, Вы писали:



S>И всё вроде было отлично, пока не пришёл уважаемый Vasiliy2
Автор: Vasiliy2
Дата: 22.09.16
и не задал нехороший вопрос: а что нужно делать, если временный файл удалить не получается? С контрольным аргументом: куча последовательных вызовов выкушает всё место и наступит полная печаль


Если имеет место куча последовательных вызовов при том, что прежний файл уже не нужен, то можно создать этот файл один раз и переиспользовать.
With best regards
Pavel Dvorkin
Re[2]: [Request-for-help] API for temp file.
От: Sinix  
Дата: 26.09.16 14:30
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Если имеет место куча последовательных вызовов при том, что прежний файл уже не нужен, то можно создать этот файл один раз и переиспользовать.

Угу. Но тут необязательно последовательные вызовы нужны. Достаточно криво написанного антивируса или заиграться с ACL и запретить delete и рано или поздно получится некрасиво.
Re[3]: [Request-for-help] API for temp file.
От: Pavel Dvorkin Россия  
Дата: 26.09.16 15:23
Оценка:
Здравствуйте, Sinix, Вы писали:

PD>>Если имеет место куча последовательных вызовов при том, что прежний файл уже не нужен, то можно создать этот файл один раз и переиспользовать.

S>Угу. Но тут необязательно последовательные вызовы нужны. Достаточно криво написанного антивируса или заиграться с ACL и запретить delete и рано или поздно получится некрасиво.

Если начать играть с ACL со своим собственным временным файлом, то хорошего будет мало, но зачем это делать ?
Антивирус — возможно, но не очень уж вероятно, можно пренебречь.
With best regards
Pavel Dvorkin
Re[7]: [Request-for-help] API for temp file.
От: #John Европа https://github.com/ichensky
Дата: 26.09.16 20:22
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Ну вот пара реальных сценариев:

S>1. Создать папку, заполнить, скормить стороннему софту, грохнуть.
Создали в директорию с временными файлами, в директории для временных файлов, скормили софту.
Либо сами пишем код, ждем пока сторонний софт сделает все что ему надо, хз. сколько тут будем ждать(зависит от стороннего софта, и от нашей программы), может секунду,может минуту. Потом убираем все локи с файла и удаляем или просто забиваем(если файл не сильно большой или их не много или у нас не получилось снять все локи), пусть ОС сама чистит.
S>2. Создать временную папку при запуске приложения (необязательно с рандомным именем), грохнуть по завершению.
Тут. При запуске смотреть если директория с таким именем уже существует — дропать ее.
Підтримати Україну у боротьбі з країною-терористом.

https://prytulafoundation.org/
https://u24.gov.ua/

Слава Збройним Силам України!!! Героям слава!!!
Re: [Request-for-help] API for temp file.
От: Слава  
Дата: 27.09.16 06:50
Оценка:
Здравствуйте, Sinix, Вы писали:

S>UPD. И да, FileOptions.DeleteOnClose подходит не всегда. Чтоб не спорить — у нас точно такой же хелпер есть для директорий. Что с ними делать будем? ; )


Нет абсолютно никакого способа закрыть доступ к некоему каталогу. Поэтому, и гарантировать его удаление нельзя.
Re[2]: [Request-for-help] API for temp file.
От: Sinix  
Дата: 27.09.16 06:52
Оценка:
Здравствуйте, Слава, Вы писали:

S>>UPD. И да, FileOptions.DeleteOnClose подходит не всегда. Чтоб не спорить — у нас точно такой же хелпер есть для директорий. Что с ними делать будем? ; )


С>Нет абсолютно никакого способа закрыть доступ к некоему каталогу. Поэтому, и гарантировать его удаление нельзя.


Кэп
Так делать-то что?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.