我希望这是您正在寻找的。据我所知,目标是“扁平化”给定“类”中的数据。我将放弃导出到 Excel,因为这似乎是一个不同的问题。 “类”存在,我猜是返回 DataTable 或您想要的任何“集合”类型的方法。我猜这会使导出到 Excel 时变得更容易。
在Catergory 类中,它有一个Article’s 的“集合”。每个Article 代表集合(Excel 电子表格)中的一个“行”。每个Article 都有一个ArticleReviews 的“集合”,称为Reviews。正如你所说……
我的一个问题是 Reviewer Point 列是动态更改的,
一篇文章可能有 3 列(如上图),但在
另一个可能是 4 或 5 个 Reviewer Point。
听起来每个Article 可能有很多审稿人,此外,并非所有审稿人都会“审阅”所有文章。鉴于这一点以及“扁平化”这些数据的要求,这意味着为“每个”审阅者创建一个列。另外,我假设只列出了审过一篇文章的审稿人,否则,为每个审稿人创建一个列会很简单。我猜目标是只有评论者“评论”了至少一 (1) 篇文章的专栏。
话虽如此,我猜第一个问题是弄清楚我们需要为审稿人提供“多少”列,以及这些审稿人的姓名是“什么”。我们需要一些方法来识别“哪个”列属于哪个审阅者。我使用Reviewer 名称来识别正确的列。那么我们如何找到审稿人……
Category 类有一个Artlicles 列表很方便。如果创建了一个方法来遍历每篇文章,然后遍历文章的每条评论并收集所有评论者并忽略重复项……这应该为我们提供需要为其添加列的“评论者”列表。如果该方法重新调整了Reviewer 的列表,我们可以使用它来确定我们需要多少列,以及这些列的名称应该是什么。
其中一个可能的问题是列顺序可能无法预测。根据首先是哪篇文章,将确定列的顺序。因此,我建议对列进行一些“排序”以保持某种顺序。
我添加了一个类Reviewer,它应该有助于对列名进行排序和比较。这是一个简单的Reviewer 类,如下所示。请注意排序使用的compareTo 方法。它按审阅者 ID 排序。这将保持相同的列顺序。
public class Reviewer : IComparable<Reviewer> {
public int ReviewerID { get; set; }
public string ReviewerName { get; set; }
public Reviewer() {
}
public Reviewer(int reviewerID, string reviewerName) {
ReviewerID = reviewerID;
ReviewerName = reviewerName;
}
public override string ToString() {
return "ReviewerID: " + ReviewerID.ToString();
}
public override bool Equals(object obj) {
return this.ReviewerName.Equals(((Reviewer)obj).ReviewerName);
}
public override int GetHashCode() {
return ReviewerName.GetHashCode();
}
public int CompareTo(Reviewer other) {
return this.ReviewerID.CompareTo(other.ReviewerID);
}
}
这将影响ArticleReview 类,需要在那里进行一些更改。有些变量看起来是不必要的,只显示需要的变量。主要变化是上面的Reviewer 对象来定义审阅者。
public class ArticleReview {
public long ArticleId { get; set; }
public Reviewer TheReviewer { get; set; }
public int ReviewPoint { get; set; }
public ArticleReview() {
}
public ArticleReview (long articleId, Reviewer reviewerId, int reviewPoint) {
ArticleId = articleId;
TheReviewer = reviewerId;
ReviewPoint = reviewPoint;
}
}
接下来是Article 类。它包含该文章的所有评论。似乎有一个名为“平均点”的列。这看起来像是评论中的“计算”值。因此,我猜想Article 类可以方便地为我们“计算”这个值。它包含所有评论……所需要的只是将所有分数相加并除以评论数量。这个方法被添加到Article 类中。
public class Article {
public long ArticleId { get; set; }
public string ArticleTitle { get; set; }
public int NumberOfComment { get; set; }
public int NumberOfView { get; set; }
public virtual ICollection<ArticleReview> Reviews { get; set; }
public Article() {
}
public Article(long articleId, string articleTitle, int numberOfComment, int numberOfView, ICollection<ArticleReview> reviews) {
ArticleId = articleId;
ArticleTitle = articleTitle;
NumberOfComment = numberOfComment;
NumberOfView = numberOfView;
Reviews = reviews;
}
public decimal GetAverage() {
if (Reviews.Count <= 0)
return 0;
decimal divisor = Reviews.Count;
int totPoints = 0;
foreach (ArticleReview review in Reviews) {
totPoints += review.ReviewPoint;
}
return totPoints / divisor;
}
}
最后,Category 类包含所有 Articles。这个类是我们需要完成前面描述的所有列内容的地方。第一部分是得到一个没有重复的List<Reviewer>。这将需要遍历所有文章,然后遍历每篇文章中的所有评论。在此过程中,我们可以检查“审阅者”并创建所有用户的非重复列表。代码创建一个新的空List<Reviewer> 然后循环遍历每篇文章,循环遍历每条评论。检查“reviewer”是否已经在列表中,如果没有,则添加它们,否则忽略重复的“reviewer”。对列表进行排序以保持列顺序,然后将其返回。
我猜这个列表可以通过多种方式来解决“列”难题。在这个例子中,另一个方法被添加到Category 类。 GetDataTable 方法从文章中的数据返回一个DataTable。开始前四列添加到表中,“标题”、“#ofView”、“#ofComment”和“平均点”。接下来循环遍历所有审阅者以添加审阅者列。审阅者姓名用作列名。这就是我们在添加数据时识别哪个列属于哪个审阅者的方式。
最后,循环遍历每个Article 以添加数据。每篇文章都会创建一个新行。可以设置行中的前三列……标题、视图、评论和平均值。接下来,我们遍历所有评论。对于每条评论,targetName 设置为评论者的姓名,然后循环遍历每一列,直到找到与评论者姓名匹配的列名。找到后,我们知道这是数据所属的列。添加值并跳出列循环并进行下一次审查。
public class Category {
public long CategoryId { get; set; }
public string CategoryTitle { get; set; }
//...
public virtual ICollection<Article> Articles { get; set; }
public Category() {
}
public Category(long categoryId, string categoryTitle, ICollection<Article> articles) {
CategoryId = categoryId;
CategoryTitle = categoryTitle;
Articles = articles;
}
public DataTable GetDataTable() {
List<Reviewer> allReviewers = GetNumberOfReviewers();
DataTable dt = new DataTable();
dt.Columns.Add("Title", typeof(string));
dt.Columns.Add("#ofView", typeof(long));
dt.Columns.Add("#ofComment", typeof(long));
dt.Columns.Add("Average point", typeof(decimal));
foreach (Reviewer reviewer in allReviewers) {
dt.Columns.Add(reviewer.ReviewerName, typeof(long));
}
foreach (Article article in Articles) {
DataRow newRow = dt.NewRow();
newRow["Title"] = article.ArticleTitle;
newRow["#ofView"] = article.NumberOfView;
newRow["#ofComment"] = article.NumberOfComment;
newRow["Average point"] = article.GetAverage();
foreach (ArticleReview review in article.Reviews) {
string targetName = review.TheReviewer.ReviewerName;
for (int i = 4; i < dt.Columns.Count; i++) {
if (targetName == dt.Columns[i].ColumnName) {
newRow[review.TheReviewer.ReviewerName] = review.ReviewPoint;
break;
}
}
}
dt.Rows.Add(newRow);
}
return dt;
}
private List<Reviewer> GetNumberOfReviewers() {
// we need a list of all the different reviewers
List<Reviewer> reviewers = new List<Reviewer>();
foreach (Article article in Articles) {
foreach (ArticleReview review in article.Reviews) {
if (!reviewers.Contains(review.TheReviewer)) {
reviewers.Add(review.TheReviewer);
}
}
}
reviewers.Sort();
return reviewers;
}
}
将所有这些放在一起,下面的代码会创建一些数据来演示。然后,DataTable 用作DataSource 到DataGridView。我希望这会有所帮助。
DataTable dt;
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
Category cat = new Category();
cat.CategoryId = 1;
cat.CategoryTitle = "Category 1";
cat.Articles = GetArticles();
dt = cat.GetDataTable();
dataGridView1.DataSource = dt;
}
private List<Article> GetArticles() {
List<Article> articles = new List<Article>();
Article art = new Article(1, "Article 1 Title", 10, 1200, GetReviews(1));
articles.Add(art);
art = new Article(2, "Article 2 Title", 32, 578, GetReviews(2));
articles.Add(art);
art = new Article(3, "Article 3 Title", 15, 132, GetReviews(3));
articles.Add(art);
art = new Article(4, "Article 4 Title", 13, 133, GetReviews(4));
articles.Add(art);
art = new Article(5, "Article 5 Title", 55, 555, GetReviews(5));
articles.Add(art);
art = new Article(6, "Article 6 Title", 0, 0, GetReviews(6));
articles.Add(art);
return articles;
}
private ICollection<ArticleReview> GetReviews(int reviewId) {
ICollection<ArticleReview> reviews = new List<ArticleReview>();
ArticleReview ar;
Reviewer Reviewer1 = new Reviewer(1, "Reviewer 1");
Reviewer Reviewer2 = new Reviewer(2, "Reviewer 2");
Reviewer Reviewer3 = new Reviewer(3, "Reviewer 3");
Reviewer Reviewer4 = new Reviewer(4, "Reviewer 4");
Reviewer Reviewer5 = new Reviewer(5, "Reviewer 5");
Reviewer Reviewer6 = new Reviewer(6, "Reviewer 6");
switch (reviewId) {
case 1:
ar = new ArticleReview(1, Reviewer1, 15);
reviews.Add(ar);
ar = new ArticleReview(1, Reviewer2, 35);
reviews.Add(ar);
ar = new ArticleReview(1, Reviewer3, 80);
reviews.Add(ar);
ar = new ArticleReview(1, Reviewer5, 55);
reviews.Add(ar);
ar = new ArticleReview(1, Reviewer6, 666);
reviews.Add(ar);
break;
case 2:
ar = new ArticleReview(2, Reviewer1, 50);
reviews.Add(ar);
ar = new ArticleReview(2, Reviewer2, 60);
reviews.Add(ar);
ar = new ArticleReview(2, Reviewer3, 40);
reviews.Add(ar);
break;
case 3:
ar = new ArticleReview(3, Reviewer1, 60);
reviews.Add(ar);
ar = new ArticleReview(3, Reviewer2, 60);
reviews.Add(ar);
ar = new ArticleReview(3, Reviewer3, 80);
reviews.Add(ar);
break;
case 4:
ar = new ArticleReview(4, Reviewer1, 30);
reviews.Add(ar);
ar = new ArticleReview(4, Reviewer2, 70);
reviews.Add(ar);
ar = new ArticleReview(4, Reviewer3, 70);
reviews.Add(ar);
break;
case 5:
ar = new ArticleReview(5, Reviewer3, 44);
reviews.Add(ar);
break;
case 6:
break;
default:
break;
}
return reviews;
}
使用 EPPlus,下面是使用上面的 DataTable 并将 DataTable 导出到 Excel 工作表的一种方法。
private void btn_ExportToExcel_Click(object sender, EventArgs e) {
using (var p = new ExcelPackage()) {
var ws = p.Workbook.Worksheets.Add("MySheet");
ws.Cells["A1"].LoadFromDataTable(dt, true);
p.SaveAs(new FileInfo(@"D:\Test\ExcelFiles\EpplusExport.xlsx"));
}
}