Я пишу платежную систему, которая взаимодействует с очень старым 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, сначала переименуйте / переместите файл в сторону, а затем обработайте его, POS После этого система воссоздаст свой собственный файл.
Да, я это сделал, но это означает, что статус учетной записи может рассинхронизироваться, если записи в первом файле не могут быть обработаны по какой-либо причине.
Вы не упомянули ни о каких подобных проблемах и не описали, как хочу с этим справиться. Пожалуйста, не превращайте этот вопрос в подвижную цель, обозначьте все ограничения, критерии и т. д., Которые вам необходимо учесть перед тем, как найти хорошее решение, только тогда люди смогут вам помочь.
Чтобы заблокировать систему POS, вы можете открыть файл для чтения и записи, прочитать его, что блокирует систему POS, а затем обрезать файл до 0 байтов перед его закрытием. Если вы это сделаете, вам нужно будет удалить и его?
Спасибо, Лассе, это сработает, так как мне не нужно удалять файл - мне просто нужно убедиться, что я не обрабатываю одни и те же записи дважды.
Будет ли в POS-файле более одной записи? Может ли, например, первая запись обработать правильно, в то время как запись 2 не удалась, а запись 3 завершилась успешно? Вы обрабатываете эти записи как пакет, чтобы в случае неудачи откатился весь пакет?
К сожалению, @Sam нет, он не пакетируется, поскольку каждая запись отправляется на удаленную точку API. Поэтому мне действительно нужно будет обрабатывать как очередь и удалять успешные сообщения из очереди. Что приводит к следующему: если все прошло успешно, файл пуст; в противном случае останутся только неудачные записи, которые могут быть добавлены.
На вашем месте я бы создал один поток, предназначенный только для немедленного перемещения любых обнаруженных входящих файлов на новое имя, и настроил бы вашу фактическую обработку полностью отдельно от этого. Тогда вы никогда не должны терять транзакции.
@Nyerguds, хотя то, что вы утверждаете, является правдой в отношении того, чтобы не терять никаких транзакций, но это действительно приводит к проблемам с синхронизацией. Если POS-система каким-то образом получает баланс от удаленной системы и «знает», что нужно вычесть какие-либо записи в этом файле, то, если мы изменим имя, POS не будет знать об этих записях. POS-система очень старая и представляет собой «черный ящик», поэтому я не знаю, что она делает внутри, и поэтому не хочу включать ее с переименованием файлов.
Подожди, что? Это также подразумевает POS-систему читает из файла? Зачем это делать исходящему интерфейсу?
Нет, я говорю - я не знаю, что это значит. Однако я знаю, что он использует кредитные лимиты, чтобы сказать, можете ли вы иметь больше «чего-то» (технический термин), которые «могли бы» использовать неполные записи как часть расчета. Поэтому я говорю, что лучше ничего не менять, поскольку я не знаю, какое влияние они окажут на остальную систему.
В качестве дополнительного примечания - у меня не будет доступа к системе POS во время разработки и, вероятно, не во время тестирования до UAT, поэтому я не могу проверить, что произойдет, если я переименую файл.
Спасибо @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 спасибо за ваш комментарий, но я не уверен, что это дает мне, кроме преобразования байта в ASCII. Мне все равно пришлось бы читать весь файл, а затем разбивать на строки для обработки через API. Любые неудачные или необработанные записи должны быть записаны обратно в файл перед закрытием (@Sams указывает на то, что каждая запись обрабатывается полностью автономно) и снятием блокировки, а StreamReader не поддерживает запись, так что это будет означать введение StreamWriter (?) Как хорошо. Рад, что вы дадите мне дальнейшие советы, так как я ими не пользовался.
Вы думали о переименовании файла перед его открытием? Затем вы можете обработать файл в удобное для вас время, и ваша POS-система может создать новый независимо.