C# 如何使用EPPlus将子对象导出为Excel

C# 如何使用EPPlus将子对象导出为Excel,c#,.net,excel,export-to-excel,epplus,C#,.net,Excel,Export To Excel,Epplus,我正在使用EPPlus帮助我将数据导出为excel。我仍在学习正确地导出数据,但不知何故,我无法导出子对象全部展开的对象 ParentObject public string A; public string B; public ChildObject ChildObject; ChildObject public string C; public string D; 因此,我希望导出的excel看起来像 A B C

我正在使用
EPPlus
帮助我将数据导出为excel。我仍在学习正确地导出数据,但不知何故,我无法导出子对象全部展开的对象

ParentObject
    public string A;
    public string B;
    public ChildObject ChildObject;

ChildObject
    public string C;
    public string D;
因此,我希望导出的excel看起来像

A        B        C        D
aa1      bb1      cc1      dd1
aa2      bb2      cc2      dd2
aa3      bb3      cc3      dd3
这就是我当前实现的样子

public void CreateExcel(IEnumerable<T> dataCollection, string fullyQualifiedFileName, string worksheetName)
    {
        using (var package = new ExcelPackage(new FileInfo(fullyQualifiedFileName)))
        {
            var worksheet =
                package.Workbook.Worksheets.FirstOrDefault(excelWorksheet => excelWorksheet.Name == worksheetName) ??
                package.Workbook.Worksheets.Add(worksheetName);

            var membersToInclude = typeof(T)
                .GetMembers(BindingFlags.Instance | BindingFlags.Public)
                .Where(p => Attribute.IsDefined(p, typeof(ExcelUtilityIgnoreAttribute)) == false
                            || p.GetCustomAttribute<ExcelUtilityIgnoreAttribute>().IsIgnored == false)
                .ToArray();

            worksheet.Cells["A1"].LoadFromCollection(dataCollection, true, OfficeOpenXml.Table.TableStyles.None,
                BindingFlags.Public, membersToInclude);

            package.Save();
        }
    }
public void CreateExcel(IEnumerable数据集合、字符串fullyQualifiedFileName、字符串工作表名称)
{
使用(var package=new ExcelPackage(new FileInfo(fullyQualifiedFileName)))
{
风险值工作表=
package.Workbook.Worksheets.FirstOrDefault(excelWorksheet=>excelWorksheet.Name==worksheetName)??
package.Workbook.Worksheets.Add(工作表名称);
var membersToInclude=typeof(T)
.GetMembers(BindingFlags.Instance | BindingFlags.Public)
.Where(p=>Attribute.IsDefined(p,typeof(ExcelUtilityIgnoreAttribute))==false
||p.GetCustomAttribute().IsIgnored==false)
.ToArray();
工作表.Cells[“A1”].LoadFromCollection(dataCollection,true,OfficeOpenXml.Table.TableStyles.None,
BindingFlags.Public,membersToInclude);
package.Save();
}
}
我尝试使用expando对象使用Microsoft泛型,但EPPlus无法使用泛型,是否有一种方法可以使用子对象导出对象


还有:我还可以使用其他库吗?

没有本机函数可以这样做。很难想出一些通用的东西,因为它需要大量的假设。应自动导出的特性类型与应将其视为子节点并将其特性导出或展开的特性类型。但如果你想到了这一点,那就是一个基本的树遍历

下面是我从一个类似的任务中改编的东西。这里,我假设任何是
字符串
或没有属性的数据类型的内容都被视为导出的
类型(
int
double
,等等)。但它很容易根据需要进行调整。我将其放在一起,因此可能无法完全优化:

public static void ExportFlatExcel<T>(IEnumerable<T> dataCollection, FileInfo file, string worksheetName)
{
    using (var package = new ExcelPackage(file))
    {
        var worksheet =
            package.Workbook.Worksheets.FirstOrDefault(excelWorksheet => excelWorksheet.Name == worksheetName) ??
            package.Workbook.Worksheets.Add(worksheetName);

        const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public;
        var props = typeof (T).GetProperties(flags);

        //Map the properties to types
        var rootTree = new Branch<PropertyInfo>(null);

        var stack = new Stack<KeyValuePair<PropertyInfo, IBranch<PropertyInfo>>>(
            props
                .Reverse()
                .Select(pi =>
                    new KeyValuePair<PropertyInfo, IBranch<PropertyInfo>>(
                        pi
                        , rootTree
                        )
                )
            );

        //Do a non-recursive traversal of the properties
        while (stack.Any())
        {
            var node = stack.Pop();
            var prop = node.Key;
            var branch = node.Value;

            //Print strings
            if (prop.PropertyType == typeof (string))
            {
                branch.AddNode(new Leaf<PropertyInfo>(prop));
                continue;
            }

            //Values type do not have properties
            var childProps = prop.PropertyType.GetProperties(flags);
            if (!childProps.Any())
            {
                branch.AddNode(new Leaf<PropertyInfo>(prop));
                continue;
            }

            //Add children to stack
            var child = new Branch<PropertyInfo>(prop);
            branch.AddNode(child);

            childProps
                .Reverse()
                .ToList()
                .ForEach(pi => stack
                    .Push(new KeyValuePair<PropertyInfo, IBranch<PropertyInfo>>(
                        pi
                        , child
                        )
                    )
                );
        }

        //Go through the data
        var rows = dataCollection.ToList();
        for (var r = 0; r < rows.Count; r++)
        {
            var currRow = rows[r];
            var col = 0;

            foreach (var child in rootTree.Children)
            {
                var nodestack = new Stack<Tuple<INode, object>>();
                nodestack.Push(new Tuple<INode, object>(child, currRow));

                while (nodestack.Any())
                {
                    var tuple = nodestack.Pop();
                    var node = tuple.Item1;
                    var currobj = tuple.Item2;

                    var branch = node as IBranch<PropertyInfo>;
                    if (branch != null)
                    {
                        currobj = branch.Data.GetValue(currobj, null);

                        branch
                            .Children
                            .Reverse()
                            .ToList()
                            .ForEach(cnode => nodestack.Push(
                                new Tuple<INode, object>(cnode, currobj)
                                ));

                        continue;
                    }

                    var leaf = node as ILeaf<PropertyInfo>;
                    if (leaf == null)
                        continue;

                    worksheet.Cells[r + 2, ++col].Value = leaf.Data.GetValue(currobj, null);

                    if (r == 0)
                        worksheet.Cells[r + 1, col].Value = leaf.Data.Name;
                }
            }
        }

        package.Save();
        package.Dispose();
    }
}
公共静态void ExportFlatExcel(IEnumerable数据集合、文件信息文件、字符串工作表名称)
{
使用(var包=新的ExcelPackage(文件))
{
风险值工作表=
package.Workbook.Worksheets.FirstOrDefault(excelWorksheet=>excelWorksheet.Name==worksheetName)??
package.Workbook.Worksheets.Add(工作表名称);
const BindingFlags flags=BindingFlags.Instance | BindingFlags.Public;
var props=typeof(T).GetProperties(标志);
//将属性映射到类型
var rootTree=新分支(空);
var stack=新堆栈(
道具
.Reverse()
.选择(pi=>
新的KeyValuePair(
圆周率
,根树
)
)
);
//对属性执行非递归遍历
while(stack.Any())
{
var node=stack.Pop();
var prop=node.Key;
var branch=node.Value;
//打印字符串
if(prop.PropertyType==typeof(string))
{
branch.AddNode(新叶(prop));
继续;
}
//值类型没有属性
var childProps=prop.PropertyType.GetProperties(标志);
如果(!childProps.Any())
{
branch.AddNode(新叶(prop));
继续;
}
//将子项添加到堆栈
var child=新分支(prop);
branch.AddNode(子节点);
儿童道具
.Reverse()
托利斯先生()
.ForEach(pi=>stack
.按下(新的键值对(
圆周率
小孩
)
)
);
}
//检查数据
var rows=dataCollection.ToList();
对于(var r=0;rnodestack.Push(
新元组(cnode、currobj)
));
继续;
}
var-leaf=作为ILeaf的节点;
if(leaf==null)
继续;
worksheet.Cells[r+2,++col].Value=leaf.Data.GetValue(currobj,null);
如果(r==0)
工作表.Cells[r+1,col].Value=leaf.Data.Name;
}
}
}
package.Save();
package.Dispose();
}
}
假设你把这些作为一个结构:

#region Classes

public class Parent
{
    public string A { get; set; }
    public Child1 Child1 { get; set; }
    public string D { get; set; }
    public int E { get; set; }
    public Child2 Child2 { get; set; }
}

public class Child1
{
    public string B { get; set; }
    public string C { get; set; }
}

public class Child2
{
    public Child1 Child1 { get; set; }
    public string F { get; set; }
    public string G { get; set; }
}

#endregion

#region Tree Nodes

public interface INode { }

public interface ILeaf<T> : INode
{
    T Data { get; set; }
}

public interface IBranch<T> : ILeaf<T>
{
    IList<INode> Children { get; }
    void AddNode(INode node);
}

public class Leaf<T> : ILeaf<T>
{
    public Leaf() { }

    public Leaf(T data) { Data = data; }

    public T Data { get; set; }
}

public class Branch<T> : IBranch<T>
{
    public Branch(T data) { Data = data; }

    public T Data { get; set; }

    public IList<INode> Children { get; } = new List<INode>();

    public void AddNode(INode node)
    {
        Children.Add(node);
    }
}

#endregion
#区域类
公共类父类
{
公共字符串A{get;set;}
公共Child1 Child1{get;set;}
公共字符串D{get;set;}
公共整数{get;set;}
公共Child2 Child2{get;set;}
}
公营儿童1
{
公共字符串B{get;
[TestMethod]
public void ExportFlatTest()
{
    var list = new List<Parent>();

    for (var i = 0; i < 20; i++)
        list.Add(new Parent
        {
            A = $"A-{i}",
            D = $"D-{i}",
            E = i*10,
            Child1 = new Child1
            {
                B = $"Child1-B-{i}",
                C = $"Child1-C-{i}",
            },
            Child2 = new Child2
            {
                F = $"F-{i}",
                G = $"G-{i}",
                Child1 = new Child1
                {
                    B = $"Child2-Child1-B-{i}",
                    C = $"Child2-Child1-C-{i}",
                }
            }
        });

    var file = new FileInfo(@"c:\temp\flat.xlsx");
    if (file.Exists)
        file.Delete();

    TestExtensions.ExportFlatExcel(
        list
        , file
        , "Test1"
        );
}