I'm fairly new to OOP and have been working heavily with C# lately. I've come across an issue in which I had a lot of duplicate code with only a query and how to handle the query results changing.
I significantly cut down on the code by creating a static class that accepts generics and an action delegate but it has a ton of arguments required and I'm looking for a cleaner way to accomplish this.
Here's how it's currently working:
public static void RunReport<TIcketType, TReportType>(Guid reportId, DateTime startDate, DateTime endDate, string queryString, Action<TIcketType, TReportType> parser)
where TIcketType : Medcare.Core.models.APPOC.BaseTicket
where TReportType : new()
{
var reportProcessFilename = System.IO.Path.GetTempPath() + "Report_" + reportId + ".work";
var reportJsonFilename = System.IO.Path.GetTempPath() + "Report_" + reportId + ".JSON";
try
{
// As long as this file exists we are assumed to be working on the report
using (System.IO.File.Create(reportProcessFilename)){}
var finishedReport = new TReportType();
using (var docStore = new DocumentStore { Url = "http://MyRavenDB.com", DefaultDatabase = "MyRavenDB" })
{
docStore.Initialize();
using (var session = docStore.OpenSession())
{
var query = session.Advanced.LuceneQuery<TIcketType>("Ticket/Search")
.Where(string.Format(queryString, startDate, endDate));
QueryHeaderInformation qhi;
var stream = session.Advanced.Stream(query, out qhi);
while (stream.MoveNext())
{
var ticket = stream.Current.Document;
ticket.id = stream.Current.Key;
parser(ticket, finishedReport);
}
}
}
var json = JsonConvert.SerializeObject(finishedReport);
System.IO.File.WriteAllText(reportJsonFilename, json);
}
catch (Exception ex)
{
var json = "{success: false, message: " + ex.Message + "}";
System.IO.File.WriteAllText(reportJsonFilename, json);
}
finally
{
if (System.IO.File.Exists(reportProcessFilename))
System.IO.File.Delete(reportProcessFilename);
}
}
I have to pass a generic to the ravenDB function "LuceneQuery", it attempts to dynamically fill out the query based on the generic that was passed to it. It must inherit our TicketBase class so I can be sure the generic passed contains some base properties.
Here's the code that calls this function from an API route:
public static ReportRequestResponse RunReport<TRequest>(TRequest request, ReportTypes reportType)
where TRequest : ReportRequest
{
if (!string.IsNullOrEmpty(request.ReportId))
{
var targetReportWorkingFilename = System.IO.Path.GetTempPath() + "Report_" + request.ReportId + ".work";
var targetReportFilename = System.IO.Path.GetTempPath() + "Report_" + request.ReportId + ".JSON";
if (!System.IO.File.Exists(targetReportFilename))
return new ReportRequestResponse
{
ReportId = request.ReportId,
Report = "Report ID not found",
Status = "failure"
};
string reportResults;
string status;
if (System.IO.File.Exists(targetReportWorkingFilename))
{
status = "working";
reportResults = "Waiting on Report to Finish";
}
else
{
status = "done";
using (var sr = new System.IO.StreamReader(targetReportFilename))
reportResults = sr.ReadToEnd();
}
return new ReportRequestResponse
{
ReportId = request.ReportId,
Report = reportResults,
Status = status
};
}
if (!request.StartDate.HasValue || !request.EndDate.HasValue)
return new ReportRequestResponse
{
ReportId = string.Empty,
Status = "failure",
Report = "StartDate and EndDate are required."
};
// Starting a new Report Thread
var newReportId = Classes.Reports.ReportsHelper.GenerateNewReportID(); // Generate a valid GUID that doesn't exist yet
var reportFilename = System.IO.Path.GetTempPath() + "Report_" + newReportId + ".JSON";
if (System.IO.File.Exists(reportFilename)) System.IO.File.Delete(reportFilename);
using (System.IO.File.Create(reportFilename)) { }
switch (reportType)
{
case ReportTypes.ByteSuccess:
Classes.APPOC.ByteSuccessReport.Start(newReportId, request.StartDate.Value, request.EndDate.Value);
break;
case ReportTypes.DocRequest:
Classes.APPOC.DocumentRequestByType.Start(newReportId, request.StartDate.Value, request.EndDate.Value);
break;
case ReportTypes.TherapySales:
Classes.APPOC.TherapySales.Start(newReportId, request.StartDate.Value, request.EndDate.Value);
break;
}
return new ReportRequestResponse
{
ReportId = newReportId.ToString(),
StartDate = request.StartDate,
EndDate = request.EndDate,
Status = "running",
Report = reportFilename
};
}
}
The logic:
/* LOGIC:
* If date range is specified-
* Generate new report GUID and call desired RunReport depending on ReportType
* HTTP Response with report GUID
* If report ID is specified
* Look for report JSON file
* If found check for a .work file
* If found http response with status: working
* If not found http response with GUID.JSON file
* If not found http response with no report with that GUID exists
*/
And here's a specific Report class calling the function:
private static void RunReport(Guid reportId, DateTime startDate, DateTime endDate)
{
const string queryString = "type: 8?? AND dateCreated: [{0:yyyy-MM-dd} TO {1:yyyy-MM-dd}]";
ReportRunner.RunReport<DocRequestFullNotes, Dictionary<string, TicketSummary>>
(reportId, startDate, endDate, queryString,
(ticket, finishedReport) =>
{
var therapies = Medcare.Core.models.APPOC.Therapies.therapies;
var targetTherapy = therapies.FirstOrDefault(t => t.startingTicketType == ticket.type);
var myTicket = new Ticket
{
Status = ticket.status,
Id = ticket.id,
CreatedBy = ticket.createdBy,
DateCreated = ticket.dateCreated,
SourceId = ticket.source.id,
Details = ticket.details,
Path = ticket.CurrentPath,
Notes = ticket.notes
};
if (targetTherapy != null && !finishedReport.ContainsKey(targetTherapy.id))
{
var therapy = new TicketSummary {Name = targetTherapy.name};
finishedReport.Add(targetTherapy.id, therapy);
}
if (targetTherapy != null && !finishedReport.ContainsKey(targetTherapy.id))
{
var therapy = new TicketSummary {Name = targetTherapy.name};
finishedReport.Add(targetTherapy.id, therapy);
}
if (targetTherapy == null) return;
finishedReport[targetTherapy.id].AddTicket(myTicket);
});
}