C# чтение и удаление файла, используемого для обмена сообщениями

Я пишу платежную систему, которая взаимодействует с очень старым POS-интерфейсом через файлы.

POS-система пытается открыть / создать файл (привязанный к одному имени). Если он открывает файл, он затем записывает запрос на платеж и закрывает файл, если ему не удается открыть файл, POS-система будет ждать и повторять попытку (извините, не знаю, сколько времени между попытками, но очень короткое).

Я должен следить за каталогом, и когда я вижу, что приходит новый файл, я должен попытаться открыть файл и заблокировать любой доступ к системе POS от записи любых новых записей. Пока у меня открыт файл, я просматриваю записи и отправляю их в платежную систему, как только я получаю подтверждение, я должен закрыть файл и удалить его.

Меня беспокоит / проблема - как мне заблокировать POS-систему от записи чего-либо между операторами close и delete?

Я могу установить FileShare на Delete, что позволяет моей программе удалить файл до того, как я его закрою, но это означает, что другие процессы также могут удалить файл независимо от того, успешно ли я обработал записи.

Опция 1

void Main()
{
    string filename = @"C:\Temp\CHARGES.TXT";

    using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.None))
    {
        var buffer = new byte[1024];
        while (true)
        {
            var bytesRead = fs.Read(buffer, 0, buffer.Length);

            if (bytesRead == 0)
                break;

            var text = ASCIIEncoding.ASCII.GetString(buffer, 0, bytesRead);
            Console.Write(text);
        }
    }
    File.Delete(filename);
}

Вариант 2

void Main()
{
    string filename = @"C:\Temp\CHARGES.TXT";

    using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Delete))
    {
        var buffer = new byte[1024];
        while (true)
        {
            var bytesRead = fs.Read(buffer, 0, buffer.Length);

            if (bytesRead == 0)
                break;

            var text = ASCIIEncoding.ASCII.GetString(buffer, 0, bytesRead);
            Console.Write(text);
        }
        File.Delete(filename);
    }
}

Поскольку это платежная система, потеря записей недопустима. Кроме того, поскольку у меня нет контроля над POS-системой, я не могу изменить способ ее работы.

заранее спасибо

Вы думали о переименовании файла перед его открытием? Затем вы можете обработать файл в удобное для вас время, и ваша POS-система может создать новый независимо.

peeebeee 02.05.2018 08:23

Если нет конкретной причины, по которой вам нужно заблокировать систему POS на короткое время, вы просто хотите избежать потери каких-либо транзакций, сделайте то, что сказал @peeebeee, сначала переименуйте / переместите файл в сторону, а затем обработайте его, POS После этого система воссоздаст свой собственный файл.

Lasse V. Karlsen 02.05.2018 08:29

Да, я это сделал, но это означает, что статус учетной записи может рассинхронизироваться, если записи в первом файле не могут быть обработаны по какой-либо причине.

ShaneG 02.05.2018 08:30

Вы не упомянули ни о каких подобных проблемах и не описали, как хочу с этим справиться. Пожалуйста, не превращайте этот вопрос в подвижную цель, обозначьте все ограничения, критерии и т. д., Которые вам необходимо учесть перед тем, как найти хорошее решение, только тогда люди смогут вам помочь.

Lasse V. Karlsen 02.05.2018 08:31

Чтобы заблокировать систему POS, вы можете открыть файл для чтения и записи, прочитать его, что блокирует систему POS, а затем обрезать файл до 0 байтов перед его закрытием. Если вы это сделаете, вам нужно будет удалить и его?

Lasse V. Karlsen 02.05.2018 08:32

Спасибо, Лассе, это сработает, так как мне не нужно удалять файл - мне просто нужно убедиться, что я не обрабатываю одни и те же записи дважды.

ShaneG 02.05.2018 08:38

Будет ли в POS-файле более одной записи? Может ли, например, первая запись обработать правильно, в то время как запись 2 не удалась, а запись 3 завершилась успешно? Вы обрабатываете эти записи как пакет, чтобы в случае неудачи откатился весь пакет?

Sam Axe 02.05.2018 08:41

К сожалению, @Sam нет, он не пакетируется, поскольку каждая запись отправляется на удаленную точку API. Поэтому мне действительно нужно будет обрабатывать как очередь и удалять успешные сообщения из очереди. Что приводит к следующему: если все прошло успешно, файл пуст; в противном случае останутся только неудачные записи, которые могут быть добавлены.

ShaneG 02.05.2018 08:52

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

Nyerguds 02.05.2018 09:44

@Nyerguds, хотя то, что вы утверждаете, является правдой в отношении того, чтобы не терять никаких транзакций, но это действительно приводит к проблемам с синхронизацией. Если POS-система каким-то образом получает баланс от удаленной системы и «знает», что нужно вычесть какие-либо записи в этом файле, то, если мы изменим имя, POS не будет знать об этих записях. POS-система очень старая и представляет собой «черный ящик», поэтому я не знаю, что она делает внутри, и поэтому не хочу включать ее с переименованием файлов.

ShaneG 02.05.2018 10:19

Подожди, что? Это также подразумевает POS-систему читает из файла? Зачем это делать исходящему интерфейсу?

Nyerguds 02.05.2018 10:40

Нет, я говорю - я не знаю, что это значит. Однако я знаю, что он использует кредитные лимиты, чтобы сказать, можете ли вы иметь больше «чего-то» (технический термин), которые «могли бы» использовать неполные записи как часть расчета. Поэтому я говорю, что лучше ничего не менять, поскольку я не знаю, какое влияние они окажут на остальную систему.

ShaneG 02.05.2018 10:47

В качестве дополнительного примечания - у меня не будет доступа к системе POS во время разработки и, вероятно, не во время тестирования до UAT, поэтому я не могу проверить, что произойдет, если я переименую файл.

ShaneG 02.05.2018 10:49
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
13
126
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Спасибо @Lasse и @Sam за ваши полезные комментарии. Я внес в код следующие изменения:

void Main()
{
    string filename = @"C:\Temp\CHARGES.TXT";
    int start = 0;
    int end = 0;

    using (var fs = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.Read))
    {
        // get a big enough buffer to hold the whole file and read it in
        long fileSize = new FileInfo(filename).Length;
        var buffer = new byte[fileSize];
        var bytesRead = fs.Read(buffer, 0, buffer.Length);

        try
        {
            // convert each line to ASCII to process through to API
            while (end < bytesRead)
            {
                while (end < bytesRead && buffer[end] != 0x0A && buffer[end] != 0x0D)
                {
                    end++;
                }
                var text = ASCIIEncoding.ASCII.GetString(buffer, start, end - start);

                // Send to API
                Console.WriteLine("Start [{0}] End [{1}] Text [{2}]", start, end, text);

                if (end < bytesRead)
                {
                    end += 2;
                    start = end;
                }
                if (end > bytesRead / 2)
                {
                    throw new NotImplementedException();
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine("just to prove the partial");
        }       
        finally
        {
            // convert any remaining records back to bytes and write back to file
            fs.SetLength(0);
            fs.Write(buffer, end, bytesRead - end);
        }
    }
}

Теперь процесс будет проходить через файл и записывать все неудачные / необработанные сообщения в файл в конце - это покрывает мои проблемы с блокировкой, а также покрывает отдельные сбои записи.

Спасибо еще раз

Почему вы не используете StreamReader для обработки текста? Только одна загвоздка: удаление StreamReader приведет к удалению базового Stream, поэтому вам нужно обрезать файл, прежде чем это делать.

Lasse V. Karlsen 02.05.2018 09:43

@Lasse спасибо за ваш комментарий, но я не уверен, что это дает мне, кроме преобразования байта в ASCII. Мне все равно пришлось бы читать весь файл, а затем разбивать на строки для обработки через API. Любые неудачные или необработанные записи должны быть записаны обратно в файл перед закрытием (@Sams указывает на то, что каждая запись обрабатывается полностью автономно) и снятием блокировки, а StreamReader не поддерживает запись, так что это будет означать введение StreamWriter (?) Как хорошо. Рад, что вы дадите мне дальнейшие советы, так как я ими не пользовался.

ShaneG 02.05.2018 10:26

Другие вопросы по теме