I have a simple service class here which import csv or xml file into database using .NET Standard library C#.
Do you have any comments? Are there any recommended techniques to use instead of the switch statement in the method ProcessPaymentFile?
public class AbcPaymentService : IAbcPaymentService
{
private readonly IAbcPaymentContext _abcPaymentContext;
private readonly IConfiguration _configuration;
public AbcPaymentService(IAbcPaymentContext abcPaymentContext, IConfiguration configuration)
{
_abcPaymentContext = abcPaymentContext;
_configuration = configuration;
}
public List<PaymentTransactionDetailResponse> GetTransactionsByCurrency(string currency)
{
var paymentTransactions = _abcPaymentContext.PaymentTransactions.Where(p => p.CurrencyCode == currency).ToList();
return MapPaymentTransactions(paymentTransactions);
}
public List<PaymentTransactionDetailResponse> GetTransactionsByDateRange(DateTime dateFrom, DateTime dateTo)
{
var paymentTransactions = _abcPaymentContext.PaymentTransactions
.Where(p => p.TransactionDate >= dateFrom && p.TransactionDate <= dateTo).ToList();
return MapPaymentTransactions(paymentTransactions);
}
public List<PaymentTransactionDetailResponse> GetTransactionsByStatus(string status)
{
// add more validation. ie. check length.
var paymentTransactions = _abcPaymentContext.PaymentTransactions.Where(p => p.Status == status).ToList();
return MapPaymentTransactions(paymentTransactions);
}
public void ProcessPaymentFile(IFormFile file)
{
#region Validation
var fileExtension = Path.GetExtension(file.FileName);
var validFileTypes = new[] { ".csv",".xml"}; // move to appsetting for easier configuration.
bool isValidType = validFileTypes.Any(t => t.Trim() == fileExtension.ToLower());
if (isValidType == false)
throw new ArgumentException($"Unknown format.");
if(file.Length > 1000) // move to appsetting for easier configuration.
throw new ArgumentException($"Invalid file size. Only less than 1 MB is allowed.");
#endregion
// Upload file to server
var target = _configuration["UploadPath"];
var filePath = Path.Combine(target, file.FileName);
try
{
using (var stream = new FileStream(filePath, FileMode.Create))
{
file.CopyTo(stream);
}
switch (fileExtension.ToLower())
{
case ".csv":
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false,
};
using (var reader = new StreamReader(filePath)) {
using (var csv = new CsvReader(reader, config))
{
csv.Context.RegisterClassMap<CsvMap>();
var paymentTransactionCsv = csv.GetRecords<Models.Xml.Transaction>().ToList();
SaveToDb(paymentTransactionCsv);
}
}
break;
case ".xml":
var serializer = new XmlSerializer(typeof(Models.Xml.Transactions));
using (TextReader reader = new StreamReader(new FileStream(filePath, FileMode.Open)))
{
var paymentTransactionXml = (Models.Xml.Transactions)serializer.Deserialize(reader);
SaveToDb(paymentTransactionXml.Transaction);
}
break;
default:
throw new ArgumentException($"Invalid file type. Only {string.Join(",", validFileTypes)} allowed.");
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
#region PrivateFunctions
private void SaveToDb(List<Models.Xml.Transaction> paymentTransactions)
{
if (PaymentTransactionXmlIsValid(paymentTransactions) == false)
throw new Exception("Invalid transaction."); // todo: write into log file or db
// if all validation passed, map objects and
var paymentTransactionsEntity = paymentTransactions.Select(p => new PaymentTransaction()
{
TransactionId = p.Id,
TransactionDate = p.TransactionDate,
Amount = Convert.ToDecimal(p.PaymentDetails.Amount),
CurrencyCode = p.PaymentDetails.CurrencyCode,
Status = Mapper.MapStatus(p.Status)
})
.ToList();
// save into db.
_abcPaymentContext.PaymentTransactions.AddRange(paymentTransactionsEntity);
_abcPaymentContext.SaveChanges();
// todo: don't insert duplicate transaction
}
private bool PaymentTransactionXmlIsValid(List<Models.Xml.Transaction> paymentTransactions)
{
foreach (var trans in paymentTransactions)
{
if (string.IsNullOrEmpty(trans.Id)) return false;
if (trans.TransactionDate == null) return false;
if(trans.PaymentDetails.Amount == 0) return false;
if (string.IsNullOrEmpty(trans.PaymentDetails.CurrencyCode)) return false;
if (string.IsNullOrEmpty(trans.Status)) return false;
}
return true;
}
private List<PaymentTransactionDetailResponse> MapPaymentTransactions(List<PaymentTransaction> paymentTransactions) {
// Construct Dto responses model.
var paymentTransactionDetailResponses = paymentTransactions.Select(p => new PaymentTransactionDetailResponse()
{
Id = p.TransactionId.ToString(),
Payment = $"{p.Amount} {p.CurrencyCode}",
Status = p.Status
}).ToList();
return paymentTransactionDetailResponses;
}
#endregion
}
file.Length > 1000A megabyte is 1024 kilobytes \$\endgroup\$