0

This is a continuation of another post. I'm trying to create an interface that will let me walk through a collection of objects, and access the name of the properties of the object.

A Report object will have ReportSections. A ReportSection will have an ICollection of items which will change depending on usage.

Here's how I'm trying to define it now.

public interface IReport
{
    string ReportName { get; set; }

    ICollection<IReportSection> ReportSections { get; }
}

public interface IReportSection
{
    string ReportSectionName { get; set; }

    ICollection ReportItems { get; }
}

public abstract class ReportBase : IReport
{
    virtual public string ReportType { get; set; }

    virtual public string ReportName { get; set; }

    virtual public ICollection<IReportSection> ReportSections { get; set; }

}

public abstract class ReportSectionBase : IReportSection
{
    public string ReportSectionName { get; set; }

    public ICollection ReportItems { get; set; }
} 

In my code, I would do this:

public class BookAffiliates : ReportSectionBase
{
    public override string ReportSectionName { get { return "Book Affiliates"; } }

    public override ICollection ReportItems { get; set; }
}

public class SomeClass
{
    public ICollection<AuthorsViewModel> Authors { get; set; }

    public ICollection<ProjectSubmissionViewModel> Submissions { get; set; }

    public string ProcessAuthorsReport()
    {
        var report = new ContribAuthorsReport{ ReportType = "CSV" };

        var authorAffil = new BookAffiliates {ReportItems = Authors };
        report.ReportSections.Add(chapAffil);

        var submissionAffil = new BookAffiliates {ReportItems = Submissions};
        report.ReportSections.Add(submissionAffil );

        return RenderReport(report)
    }

}

In RenderReport I would like to walk through the collections and use the PropertyNames:

private string RenderReport(ReportBase currentReport)
{
    var reportBody = new StringBuilder();
    reportBody.Append(currentReport.ReportName + Environment.NewLine + Environment.NewLine);

    foreach (var thisSection in currentReport.ReportSections)
    {
        reportBody.Append(thisSection.ReportSectionName + Environment.NewLine);

        /// ---- Here! Here! I don't know what type, I want the 
        /// code to get the type based on ReportSectionBase<T>
        var firstItem = thisSection.ReportItems.OfType<???Type???>().FirstOrDefault();

        // I would actually like to go through each property of 
        // the ReportItem type and list it here.
        foreach(var prop in firstItem.GetType().GetProperties()) 
        {
               reportBody.AppendLine(string.Format("{0}:{1}" prop.Name, prop.Value));
        }
    }

    return reportBody.ToString();
}

I'm not sure how to best define this. I'm pretty sure I've done it before, but it's not coming to me.

M Kenyon II
  • 4,136
  • 4
  • 46
  • 94

2 Answers2

0

You would use Reflection to do it.

foreach(var prop in thisItem.GetType().GetProperties()) 
{
       reportBody.AppendLine(string.Format("{0}:{1}" prop.Name, prop.Value));
}
Marshal
  • 6,551
  • 13
  • 55
  • 91
0

Took a while, a lot of questions, and figuring out what I really wanted to ask. I came up with this.

Here are my interfaces and base classes:

public class ReportBase
{
    public ReportBase()
    {
        ReportSections = new List<IReportSection>();
    }

    public string ReportType { get; set; }

    public string ReportName { get; set; }

    public ICollection<IReportSection> ReportSections { get; set; }

}
public interface IReportSection
{
    string ReportSectionName { get; }

    ICollection ReportItems { get; set; }
}

public class ReportSection<T> : IReportSection
{
    public string ReportSectionName { get; set; }

    public ICollection<T> ReportItems { get; set; }

    ICollection IReportSection.ReportItems
    {
        get { return ReportItems as ICollection; }
        set { ReportItems = value as ICollection<T>; }
    }
}

I create my objects like this:

public ReportBase GetContribAuthorsReport
(
    ICollection<ProjectAffiliateViewModel> projectAffiliates, 
    ICollection<SubmissionAffiliateViewModel> submissionAffiliates
)
{
    //var caReport = new ContributingAffiliates("CSV", projectAffiliates, submissionAffiliates);
    var caReport = new ReportBase { ReportType = "CSV", ReportName = "Reviewers' Contact Information" };

    caReport.ReportSections.Add(new ReportSection<ProjectAffiliateViewModel> { ReportItems = projectAffiliates });
    caReport.ReportSections.Add(new ReportSection<SubmissionAffiliateViewModel> { ReportItems = submissionAffiliates });

    return caReport;//.Report;
}

Then I iterate through the objects like this:

public class DownloadCsvActionResult : ActionResult
{
    public ReportBase Report { get; set; }
    public string fileName { get; set; }

    private string ReportData { get; set; }

    public DownloadCsvActionResult(ReportBase report, string pFileName)
    {
        Report = report;
        fileName = pFileName;
        ReportData = RenderReport();
    }

    private string RenderReport()
    {
        var reportBody = new StringBuilder();

        reportBody.AppendLine(Report.ReportName);
        reportBody.Append(Environment.NewLine + Environment.NewLine);

        foreach (var thisSection in Report.ReportSections)
        {
            reportBody.Append(thisSection.ReportSectionName + Environment.NewLine);

            if (thisSection.ReportItems != null)
            {
                var itemType = thisSection.ReportItems.GetType().GetGenericArguments().Single();

                var first = true;
                foreach (var prop in itemType.GetProperties())
                {
                    if (!first) reportBody.Append(",");

                    DisplayAttribute attribute = prop.GetCustomAttributes(typeof(DisplayAttribute), false)
                         .Cast<DisplayAttribute>()
                         .SingleOrDefault();

                    string displayName = (attribute != null) ? attribute.Name : prop.Name;
                    reportBody.Append(displayName);

                    first = false;
                }
                reportBody.Append(Environment.NewLine);

                foreach (var thisItem in thisSection.ReportItems)
                {
                    var firstData = true;
                    foreach (var prop in itemType.GetProperties())
                    {
                        if (!firstData) reportBody.Append(",");
                        reportBody.Append(prop.GetValue(thisItem,null));
                        firstData = false;
                    }
                    reportBody.Append(Environment.NewLine);
                }
            }

            reportBody.Append(Environment.NewLine);
        }

        return reportBody.ToString();
    }

    public override void ExecuteResult(ControllerContext context)
    {
        //Create a response stream to create and write the Excel file
        HttpContext curContext = HttpContext.Current;
        curContext.Response.Clear();
        curContext.Response.AddHeader("content-disposition", "attachment;filename=" + fileName);
        curContext.Response.Charset = "";
        curContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
        curContext.Response.ContentType = "application/vnd.ms-excel";

        //Write the stream back to the response
        curContext.Response.Write(ReportData);
        curContext.Response.End();

    }
}

This gives me what I need for now. Sorry I wasn't as clear in the first place, and thank you for all your help.

M Kenyon II
  • 4,136
  • 4
  • 46
  • 94