Historically in my classes I've simply had a number of methods which would return DataSets based on data returned from a stored procedure. Recently however I've moved to giving my classes properties and returning data via lists as shown in the class below.
public class Contribution
{
public int MemberNumber { get; }
public DateTime DateDue { get; }
public DateTime DateReceived { get; }
public string TransactionType { get; }
public string ContributionType { get; }
public decimal ContributionAmount { get; }
public decimal ContributionExpenses { get; }
public decimal SocialTax { get; }
public decimal Insurance { get; }
public decimal AdminFee { get; }
public decimal BrokerFee { get; }
public decimal Investable { get; }
public Contribution(int memberNumber, DateTime dateDue, DateTime dateReceived, string transactionType, string contributionType, decimal contributionAmount, decimal contributionExpenses, decimal socialTax, decimal insurance, decimal adminFee, decimal brokerFee, decimal investable)
{
MemberNumber = memberNumber;
DateDue = dateDue;
DateReceived = dateReceived;
TransactionType = transactionType;
ContributionType = contributionType;
ContributionAmount = contributionAmount;
ContributionExpenses = contributionExpenses;
SocialTax = socialTax;
Insurance = insurance;
AdminFee = adminFee;
BrokerFee = brokerFee;
Investable = investable;
}
public static List<Contribution> GetContributions(Member member, DateTime startDate, DateTime endDate)
{
var contributions = new List<Contribution>();
string[] adminCodes = {"AADMIN", "EADMIN", "RADMIN"};
string[] brokerCodes = {"ABROK", "EBROK", "RBROK"};
var insuranceCodes = Classes.Member.Insurance.GetCodes(member);
var products = Product.GetProducts(member);
using (var connection = new SqlConnection(Common.GetConnectionString()))
{
var cmd = new SqlCommand("GetMemberContributions", connection) {CommandType = CommandType.StoredProcedure};
cmd.Parameters.Add("@MemberNumber", SqlDbType.Int).Value = member.MemberNumber;
cmd.Parameters.Add("@StartDate", SqlDbType.DateTime).Value = startDate;
cmd.Parameters.Add("@EndDate", SqlDbType.DateTime).Value = endDate;
connection.Open();
var ds = new DataSet();
var da = new SqlDataAdapter(cmd);
da.Fill(ds);
if (ds.Tables[0].Rows.Count == 0)
return contributions;
foreach (DataRow row in ds.Tables[0].Rows)
{
DateTime.TryParse(row["Due"].ToString().Trim(), out DateTime dateDue);
DateTime.TryParse(row["Received"].ToString().Trim(), out DateTime dateReceived);
decimal.TryParse(row["Amount"].ToString().Trim(), out decimal amount);
decimal.TryParse(row["Expenses"].ToString().Trim(), out decimal expenses);
var transactionType = row["TransactionType"].ToString().Trim();
var contributionType = row["ContributionType"].ToString().Trim();
decimal socialTax = 0;
decimal insurance = 0;
decimal adminFee = 0;
decimal brokerFee = 0;
decimal investable = 0;
if (transactionType == "TAX")
socialTax = amount;
if (transactionType == "FEE")
{
if (adminCodes.Contains(contributionType))
adminFee = amount;
if (brokerCodes.Contains(contributionType))
brokerFee = amount;
}
if (insuranceCodes.Find(i => i.Code == contributionType) != null)
insurance = amount;
var x = contributionType.Substring(1, contributionType.Length - 1); //remove the leading A, E, R to match contribution type to product code
if (products.Find(i => i.Code == x) != null)
investable = amount;
var contribution = new Contribution(
Convert.ToInt32(row["Member"]),
dateDue,
dateReceived,
transactionType,
contributionType,
amount,
expenses,
socialTax,
insurance,
adminFee,
brokerFee,
investable
);
contributions.Add(contribution);
}
}
return contributions;
}
}
An answer given to the previous version of my question advised that the GetContributions method should not live within the above class.
However my original question was how best to include another method that returned a total of contributions per type. I currently have this second method in the below class but I don't believe this is the best approach as I would end up with a new class for each method that returned a different data structure.
public class ContributionTotal
{
public string ContributionType { get; }
public string ContributionDescription { get; }
public decimal Total { get; }
public DateTime LastContributionDate { get; }
public decimal LastContributionAmount { get; }
public ContributionTotal(string contributionType, string contributionDescription, decimal total, DateTime lastContributionDate, decimal lastContributionAmount)
{
ContributionType = contributionType;
ContributionDescription = contributionDescription;
Total = total;
LastContributionDate = lastContributionDate;
LastContributionAmount = lastContributionAmount;
}
public static List<ContributionTotal> GetContributionTotals(Member member)
{
var contributionTotals = new List<ContributionTotal>();
using (var connection = new SqlConnection(Common.GetConnectionString()))
{
var cmd = new SqlCommand("GetMemberTotalContributions", connection) {CommandType = CommandType.StoredProcedure};
cmd.Parameters.AddWithValue("@MemberNumber", member.MemberNumber);
cmd.Parameters.AddWithValue("@SchemeCode", member.SchemeCode);
connection.Open();
var ds = new DataSet();
var da = new SqlDataAdapter(cmd);
da.Fill(ds);
if (ds.Tables[0].Rows.Count == 0)
return contributionTotals;
//calculate latest contribution date for each contribution type
var avcLastContributionDate = DateTime.MinValue;
var employeeLastContributionDate = DateTime.MinValue;
var employerLastContributionDate = DateTime.MinValue;
foreach (DataRow row in ds.Tables[0].Rows)
{
var contributionType = row["pmc_cont"].ToString().Substring(0, 1); //Should return either A, E or R for AVC, Employee or Employer
if (!DateTime.TryParse(row["LastDate"].ToString().Trim(), out DateTime lastDate)) continue;
if (lastDate > avcLastContributionDate && contributionType == "A")
avcLastContributionDate = lastDate;
if (lastDate > employeeLastContributionDate && contributionType == "E")
employeeLastContributionDate = lastDate;
if (lastDate > employerLastContributionDate && contributionType == "R")
employerLastContributionDate = lastDate;
}
decimal avcLastContributionAmount = 0;
decimal avcTotal = 0;
decimal employeeLastContributionAmount = 0;
decimal employeeTotal = 0;
decimal employerLastContributionAmount = 0;
decimal employerTotal = 0;
foreach (DataRow row in ds.Tables[0].Rows)
{
var contributionType = row["ContributionType"].ToString().Substring(0, 1); //Should return either A, E or R for AVC, Employee or Employer
if (!DateTime.TryParse(row["LastDate"].ToString().Trim(), out DateTime lastDate)) continue;
if (!decimal.TryParse(row["LastAmount"].ToString().Trim(), out decimal lastAmount)) continue;
if (!decimal.TryParse(row["Total"].ToString().Trim(), out decimal total)) continue;
if (avcLastContributionDate == lastDate && contributionType == "A")
{
avcLastContributionAmount += lastAmount;
avcTotal += total;
}
if (employeeLastContributionDate == lastDate && contributionType == "E")
{
employeeLastContributionAmount += lastAmount;
employeeTotal += total;
}
if (employerLastContributionDate == lastDate && contributionType == "R")
{
employerLastContributionAmount += lastAmount;
employerTotal += total;
}
}
if(avcTotal > 0)
contributionTotals.Add(new ContributionTotal("A", "Employee Voluntary", avcTotal, avcLastContributionDate, avcLastContributionAmount));
if (employeeTotal > 0)
contributionTotals.Add(new ContributionTotal("E", "Employee Regular", employeeTotal, employeeLastContributionDate, employeeLastContributionAmount));
if (employerTotal > 0)
contributionTotals.Add(new ContributionTotal("R", "Employer Regular", employerTotal, employerLastContributionDate, employerLastContributionAmount));
return contributionTotals;
}
}
}
The methods mentioned above are called from the code behind of my ASP.NET web application as shown below
private void GetContributionHistory()
{
var member = (Member)Session["Member"];
var contributions = Contribution.GetContributions(member, ContributionHistoryStartDate, ContributionHistoryEndDate);
rptContributionHistory.DataSource = contributions;
rptContributionHistory.DataBind();
}
private void GetTotalContributions()
{
var member = (Member)Session["Member"];
var totalContributions = ContributionTotal.GetContributionTotals(member);
rptContributionTotals.DataSource = totalContributions;
rptContributionTotals.DataBind();
var total = totalContributions.Sum(totalContribution => totalContribution.Total);
ltTotalContributions.Text = Common.FormatCurrency(total);
ltContributionsSinceJoining.Text = Common.FormatCurrency(total);
}