代码之家  ›  专栏  ›  技术社区  ›  AminM LordDoskias

将具有不同关系值的表转换为excel列

  •  2
  • AminM LordDoskias  · 技术社区  · 7 年前

    我有这些桌子:

    类别

    CategoryId
    CategoryTitle
    ...
    ICollection<Article> Articles  
    

    每个类别可以有多篇文章:

    ArticleId
    ArticleTitle  
    NumberOfComment
    NumberOfView
    ...
    ICollection<ArticleReview> Reviews 
    

    文章综述

    ArticleReviewId 
    ReviewPoint
    ArticleId
    ReviewerId
    

    EPPlus 包裹
    这是我的 ExcelExport

    public class excelExport 
    {
        public string ArticleTitle { get; set; }
    
        public int NumberOfComment { get; set; }
        public int NumberOfReviews { get; set; }
        public List<ResearchReviewReport> Reviews { get; set; }
    }
    
    public class ArticleReviewReport
    {
        public string Reviewer { get; set; }
        public int ReviewPoint { get; set; }
    }
    

    一对多 关系,但在最终结果中,它们都应该在一行中展平。现在,我创建了不属于数据库的新类,并将该类传递给 ExcelPackage xlsx 作为输出:

    ArticleTitle 
    Reviewer1Point
    Reviewer2Point
    ............
    ReviewerNPoint
    ReviewersAvaragePoint
    NumberOfComment
    NumberOfView  
    

    如何填充 卓越出口 通过使用另外3个类来初始化?


    编辑

    Excel final Export

    我的问题之一是Reviewer Point列被动态更改,
    一篇文章可能有3列(如上图),但另一篇文章可能有4或5个评论员点。

    我忘了说每篇文章都有一些问题,每个问题都有评审员的回答,所以如果我们有3个问题,有2个评审员,第6篇文章评审,我应该得到每个评审员的平均文章评审,并把它放在单个单元格中

    5 回复  |  直到 7 年前
        1
  •  1
  •   Reza Aghaei    7 年前

    为了保持简单,我假设您使用以下简化模型,并将描述解决方案。您可以轻松地将其适应您的车型:

    public class Article
    {
        public string Title { get; set; }
        public DateTime Date { get; set; }
        public List<Review> Reviews { get; set; }
    }
    public class Review
    {
        public int Points { get; set; }
    }
    

    现在,我们将根据输入数据生成以下输出,具有动态数量的reviewer列:

    enter image description here

    创建一个函数来转换 List<Article> DataTable . 创造 数据表 ,适用于 Article Reviews 列出并添加该数量的列。然后在一个循环中,为每个 包括它的 Review 列表,创建对象数组并添加到

    以下是函数:

    public DataTable GetData(List<Article> list)
    {
        var dt = new DataTable();
        dt.Columns.Add("Title", typeof(string));
        dt.Columns.Add("Date", typeof(DateTime));
        var max = list.Max(x => x.Reviews.Count());
        for (int i = 0; i < max; i++)
            dt.Columns.Add($"Reviewer {i + 1} Points", typeof(int));
        foreach (var item in list)
            dt.Rows.Add(new object[] { item.Title, item.Date }.Concat(
                item.Reviews.Select(x => x.Points).Cast<object>()).ToArray());
        return dt;
    }
    

    测试数据

    var list = new List<Article>
    {
        new Article(){
            Title = "Article 1", Date = new DateTime(2018,1,1),
            Reviews = new List<Review> {
                new Review(){Points=10},
            },
        },
        new Article(){
            Title = "Article 2", Date = new DateTime(2018,1,2),
            Reviews = new List<Review> {
                new Review(){Points=10}, new Review(){Points=9}, new Review(){Points=8},
            },
        },
        new Article(){
            Title = "Article 3", Date = new DateTime(2018,1,3),
            Reviews = new List<Review> {
                new Review(){Points=9},
            },
        },
    };
    
        2
  •  1
  •   Szymon Tomczyk    7 年前

    public class Category
    {
        public long CategoryId { get; set; }
        public string CategoryTitle { get; set; }
        public virtual ICollection<Article> Articles { get; set; }
    }
    
    public class Article
    {
        public long ArticleId { get; set; }
        public long CategoryId { get; set; }
        public string ArticleTitle { get; set; }
        public int NumberOfComment { get; set; }
        public int NumberOfView { get; set; }
        public virtual Category Category { get; set; }
        public virtual ICollection<ArticleReview> Reviews { get; set; }
    }
    public class ArticleReview
    {
        public long ArticleReviewId { get; set; }
        public long ArticleId { get; set; }
        public string ReviewerId { get; set; }
        public int ReviewPoint { get; set; }
        public virtual Article Article { get; set; }
    }
    public class ExcelExport
    {
        public string ArticleTitle { get; set; }
        public int NumberOfComment { get; set; }
        public int NumberOfReviews { get; set; }
        public List<ArticleReviewReport> Reviews { get; set; }
    }
    
    public class ArticleReviewReport
    {
        public string Reviewer { get; set; }
        public int ReviewPoint { get; set; }
    }
    

    最终,你会得到一份 ExcelExport _context 是实体的实例(DbContext):

    public List<ExcelExport> GetExcelExports()
    {
        return _context.Articles.Select(a => new ExcelExport
        {
            ArticleTitle = a.ArticleTitle,
            NumberOfComment = a.NumberOfComment,
            NumberOfReviews = a.NumberOfView,
            Reviews = a.Reviews.Select(r => new ArticleReviewReport
            {
                Reviewer = r.ReviewerId,
                ReviewPoint = r.ReviewPoint
            }).ToList()
        }).ToList();
    }
    
        3
  •  1
  •   JohnG    7 年前

    DataTable 或任何您想要的收藏类型。我猜这将使导出到Excel时更加容易。

    Catergory 类,它有一个 Article’s . 每个 Article 表示集合(Excel电子表格)中的一行。每个 文章 ArticleReviews 打电话 Reviews . 正如你所说的

    我的问题之一是Reviewer Point列被动态更改, 另一个可能是4或5个审核点。

    这听起来好像每个人都有很多评论员 文章 此外,并非所有的审稿人都会对所有的文章进行审稿。考虑到这一点以及将数据展平的要求,将意味着为每个审阅者创建一列。此外,我假设只列出审阅其中一篇文章的审阅者,否则,为每个审阅者创建一个列将很简单。我猜目标是只让专栏中的评论者至少审阅了一(1)篇文章。

    话虽如此,我猜第一个问题是计算出我们需要多少列评论人,这些评论人的名字是什么。我们需要一些方法来确定哪个列属于哪个审阅者。我正在使用 Reviewer

    方便的是 Category 类有一个 Artlicle s如果创建了一个方法,该方法遍历每一篇文章,然后遍历每一篇文章的评论,并收集所有的审阅者并忽略重复项,那么这将为我们提供需要添加列的审阅者列表。如果该方法返回了 审核人 我们不仅可以使用它来确定需要多少列,还可以确定这些列的名称。

    其中一个可能的问题是列顺序可能不可预测。根据第一篇文章,将确定列的顺序。因此,我建议对列进行一些排序,以保持一定的顺序。

    我加了一个班 审核人 这将有助于对列名进行排序和比较。这是一个简单的问题 审核人 下面是一节课。注意 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 审核人 对象来定义审阅者。

    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;
      }
    }
    

    接下来是 文章 班级。它保存了那篇文章的所有评论。似乎有一列叫做“平均点”。这看起来像是从审查中计算出来的值。因此,我想这对政府来说是方便的 文章 文章

    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;
      }
    }
    

    最后 类别 班上有所有的学生 文章 s我们需要在这个类中完成前面描述的所有列内容。第一部分是获得一个 List<Reviewer> 没有副本。这将需要遍历所有文章,然后遍历每篇文章中的所有评论。在这个过程中,我们可以检查审阅者并创建一个所有用户的非重复列表。代码将创建一个新的空 然后循环阅读每一篇文章,循环阅读每一篇评论。检查审阅者是否已经在列表中,如果没有,则添加他们,否则忽略重复的审阅者。对列表进行排序以保持列顺序,然后返回。

    我猜这个列表可以用很多方法来解决专栏的难题。在本例中,另一个方法被添加到 类别 班级。这个 GetDataTable 方法返回一个 数据表 根据文章中的数据。首先,将前四列添加到表中,标题为#of view、#of component和Average point。接下来,循环所有审阅者以添加审阅者列。审阅者名称用作列名。这就是我们在添加数据时识别哪个列属于哪个审阅者的方式。

    最后,通过每个 文章 添加数据。每篇文章创建一个新行。行中的前三列可以设置为Title、view、comment和Average。接下来,我们循环浏览所有评论。每次审查 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; 
      }
    }
    

    把这些放在一起,下面的代码创建一些数据来演示。然后, 用作 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,下面是使用 并导出 数据表 到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"));
      }
    }
    

    enter image description here

        4
  •  0
  •   Amirhossein Mehrvarzi GGregson    7 年前

    如何使用另外3个类填充excelExport类?

    ExcelExport 课程内容如下:

    NumberOfComment 等于 article.NumberOfComment 对于每个文章条目! 除非 ArticleComment 利用 在…内 Article public virtual ICollection<ArticleComment> Comments { get; set;} ),然后使用 article.Comments.Count() .

    NumberOfReviews article.Reviews.Count()

    Reviews 对于每一篇文章,可以是以下内容:

    article.Reviews.Select(s => new ArticleReviewReport { 
           Reviewer = r.ReviewerId, // user id
           ReviewPoint = r.ReviewPoint
    });
    

    似乎您还必须将另一个属性添加到您的 卓越出口 课堂展示 ReviewersAvaragePoint 并列举如下:

    var reviewPoints = article.Reviews.Select(s => s.ReviewPoint);
    ReviewersAvaragePoint = reviewPoints.Sum()/reviewPoints.Count();
    

    基于OP的编辑进行编辑

    List 属于 ArticleReviewReport (例如。 List<ArticleReviewReport> Reviews ReviewerId ArticleReview

    var allReviewers = db.articleReviews/*condition*/.Select(s => s.ReviewerId).Distinct();
    

    现在您可以分配每个 文章综述报告 List<Dictionary<string, string>> 这将是一个好主意 数据类型 对于 成员。

        5
  •  0
  •   Roma Ruzich    7 年前
    public IEnumarable<ExcelExport> GetExcelExports()
    {
        return _context.Articles.Select(a => new ExcelExport
        {
           ArticleTitle = a.ArticleTitle,
           NumberOfComment = a.NumberOfComment,
           NumberOfReviews = a.NumberOfView,
           Reviewer1Point = a.Reviews.Any(e => e.ReviewerId = 1) ? a.Reviews.Where(e => e.ReviewerId = 1).Sum(e => e.ReviewPoint) : 0,
           Reviewer2Point = a.Reviews.Any(e => e.ReviewerId = 2) ? a.Reviews.Where(e => e.ReviewerId = 2).Sum(e => e.ReviewPoint) : 0,
           ....
           ReviewerNPoint = a.Reviews.Any(e => e.ReviewerId = N) ? a.Reviews.Where(e => e.ReviewerId = N).Sum(e => e.ReviewPoint) : 0
         });
    }