I have a console application that basically retrieves XML content, writes to the XML file and sends the file to an SFTP site:
public Main(string[] args)
{
try
{
//code to parse arguments to load into DTO to extract stored proc and report name
List<ReportInfo> reports = Parse(args);
foreach(var rpt in reports)
{
//retrieve xml content from database
XmlDocument doc = new XmlDocument();
using (SqlConnection con = new SqlConnection(AppConfig.ConnectionString))
{
using (SqlCommand cmd = con.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = rpt.StoredProcedure;
con.Open();
using (var reader = cmd.ExecuteXmlReader())
{
doc.Load(reader);
reader.Close();
}
con.Close();
}
}
//save xml file in a folder
string filePath = Path.Combine(AppConfig.ReportsFolder, string.Format(rpt.FileName, DateTime.Today.ToString("MMddyyyy")));
using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (var xmlWriter = XmlWriter.Create(fileStream,
new XmlWriterSettings
{
OmitXmlDeclaration = false,
ConformanceLevel = ConformanceLevel.Document,
Encoding = Encoding.UTF8
}))
{
xmldoc.Save(xmlWriter);
}
}
// third party tool is called to transmit the file
SFtpClient client = new SFtpClient("host","user","pwd");
client.Send(filPpath);
}
}
catch(Exception ex)
{
_iLogger.Error(ex);
}
}
Main()
consists of considerable amount of lines. So, I have decided to split functionality into smaller classes similar to SOLID principles like this:
public Main(string[] args)
{
try
{
List<ReportInfo> reports = Parse(args);
foreach(var rpt in reports)
{
XmlDocument xmldoc = DBHelper.GetReport(rpt.sproc);
var filePath = ReportProcessor.SaveFileAsXml(xmldoc);
ReportProcessor.SendFileviaSFtp(filePath);
}
}
catch(Exception ex)
{
_iLogger.Error(ex);
}
}
public static class ParametersParser
{
public static List<ReportsInfo> Parse(string[] args)
{
//parse the args
}
}
public static class DBHelper
{
public static XmlDocument GetReport(string storedprocedure)
{
XmlDocument doc = new XmlDocument();
using (SqlConnection con = new SqlConnection(AppConfig.ConnectionString))
{
using (SqlCommand cmd = con.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = storedprocedure;
con.Open();
using (var reader = cmd.ExecuteXmlReader())
{
doc.Load(reader);
reader.Close();
}
con.Close();
}
}
return doc;
}
}
public static class ReportProcessor
{
public static string SaveFileAsXml(string fileName, XmlDocument xmldoc)
{
string filePath = Path.Combine(AppConfig.ReportsFolder, string.Format(fileName, DateTime.Today.ToString("MMddyyyy")));
using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (var xmlWriter = XmlWriter.Create(fileStream,
new XmlWriterSettings
{
OmitXmlDeclaration = false,
ConformanceLevel = ConformanceLevel.Document,
Encoding = Encoding.UTF8
}))
{
xmldoc.Save(xmlWriter);
}
}
return filePath;
}
public static void SendFileviaSFtp(string filePath)
{
//here third party tool is called to transmit the file
SFtpClient client = new SFtpClient("host","user","pwd");
client.Send(filepath);
}
}
Here are my doubts with these modifications:
- As observed, the classes are static. I felt they are more of utility classes and placed them all in
Helpers
folder. So, is it advisable to use static classes this way or shall I shift to normal classes? - By having static classes and methods, do I run into threading issues as there might be chances multiple instances of this exe might execute concurrently?
Even after splitting into different classes, I have the exception handling and logging inside
Main()
. Is it ok to have it handle at single location or shall I also include in individual methods and throw specific exception?public static class ParametersParser { public static List<ReportsInfo> Parse(string[] args) { try { //parse the args } catch(Exception ex) { throw new Exception("ParametersParser.Parse", ex); } } }