当前位置:网站首页>0x05 - synthesis example, export to CSV

0x05 - synthesis example, export to CSV

2020-12-14 23:27:40 Newbe36524

Now? , Let's complete a slightly more complex scenario use case .

Export entities as CSV file

In order to make the following examples more in line with the actual production , Let's introduce a specific scenario here .

We need to export entities as CSV file .

CSV A document usually consists of two parts .

The first part is the head of the file . The header contains the column name of each column .

This is the second part . Each line in the content section is a record .

Whether it's the header or the content section, each attribute is separated by a comma .

Here we use  OrderInfo  demonstrate :

public class OrderInfo
{
    public int OrderId { get; set; }
    public string Buyer { get; set; }
    public decimal TotalPrice { get; set; }
}

Then it leads to CSV A sample of :

OrderId,Buyer,TotalPrice
1,yueluo,99999
2,newbe36524,36524
3,traceless,123456

Analysis of implementation ideas

This business scenario can be implemented in a very diverse way , Here we briefly divide the logic into the following parts :

  1. Use object visitor visit  OrderInfo  All attributes of
  2. Pass all attributes to CSV Writer For the output

Realization CSV Writer

First , Let's start by adding 2 What step needs CSV Writer :

public interface ICsvWriter
{
    ICsvWriter WriteHeader(string header);
    ICsvWriter FinishHead();
    ICsvWriter WriteCell(string cell);
    ICsvWriter FinishRow();
}

public class CsvWriter : ICsvWriter
{
    public string Separator { get; set; } = ",";
    private readonly TextWriter _writer;

    public CsvWriter(
        TextWriter writer)
    {
        _writer = writer;
    }

    private bool _firstHead = true;

    public ICsvWriter WriteHeader(string header)
    {
        if (_firstHead)
        {
            _firstHead = false;
        }
        else
        {
            _writer.Write(Separator);
        }

        _writer.Write(header);

        return this;
    }

    public ICsvWriter FinishHead()
    {
        _writer.WriteLine();
        return this;
    }

    private bool _firstCell = true;

    public ICsvWriter WriteCell(string cell)
    {
        if (_firstCell)
        {
            _firstCell = false;
        }
        else
        {
            _writer.Write(Separator);
        }

        _writer.Write(cell);

        return this;
    }

    public ICsvWriter FinishRow()
    {
        _firstCell = true;
        _writer.WriteLine();

        return this;
    }
}

With such a foundation  CsvWriter, We can first generate a table in the sample :

var sb = new StringBuilder();
var csvWriter = new CsvWriter(new StringWriter(sb));
csvWriter.WriteHeader("OrderId")
    .WriteHeader("Buyer")
    .WriteHeader("TotalPrice")
    .FinishHead()
    .WriteCell("1")
    .WriteCell("yueluo")
    .WriteCell("99999")
    .FinishRow()
    .WriteCell("2")
    .WriteCell("newbe36524")
    .WriteCell("36524")
    .FinishRow()
    .WriteCell("3")
    .WriteCell("traceless")
    .WriteCell("123456")
    .FinishRow();
Console.WriteLine(sb.ToString());

Output header

Now? , We use the first object visitor To call... Above CsvWriter Output from the header :

var sb = new StringBuilder();
var csvWriter = new CsvWriter(new StringWriter(sb));

default(OrderInfo)
    .V()
    .WithExtendObject<OrderInfo, CsvWriter>()
    .ForEach((name, value, w) => w.WriteHeader(name))
    .Run(new OrderInfo(), csvWriter);
csvWriter.FinishHead();

Console.WriteLine(sb.ToString());

So I get the following results :

OrderId,Buyer,TotalPrice

Output table rows

Now? , We're adding a object visitor To output each row of the table :

var rowWriter = default(OrderInfo)
    .V()
    .WithExtendObject<OrderInfo, CsvWriter>()
    .ForEach((name, value, w) => w.WriteCell(value != null ? value.ToString() : ""))
    .Cache();

var orders = new List<OrderInfo>
{
    new OrderInfo
    {
        OrderId = 1,
        Buyer = "yueluo",
        TotalPrice = 99999M
    },
    new OrderInfo
    {
        OrderId = 2,
        Buyer = "newbe36524",
        TotalPrice = 36524M
    },
    new OrderInfo
    {
        OrderId = 3,
        Buyer = "traceless",
        TotalPrice = 123456M
    }
};
foreach (var order in orders)
{
    rowWriter.Run(order, csvWriter);
    csvWriter.FinishRow();
}

Console.WriteLine(sb.ToString());

So you get the following :

1,yueluo,99999
2,newbe36524,36524
3,traceless,123456

This is exactly what we expect from the row data in the table .

establish CsvExtensions

Now? , We integrate the above table header and table row logic , Add them all to one CsvExtensions Of the . And add to  IEnumerable<T>  Extension method of , This makes it easier to make calls . This will be modelled on the previous  FormatToStringExtensions  In the practice .

public static class CsvExtensions
{
    public static string ToCsv<T>(this IEnumerable<T> items)
        where T : new()
    {
        var re = CsvFilerHelper<T>.Instance.ToCsv(items);
        return re;
    }

    private static class CsvFilerHelper<T>
        where T : new()
    {
        internal static readonly ICsvHelper Instance = new CsvHelper();

        public interface ICsvHelper
        {
            string ToCsv(IEnumerable<T> items);
        }

        private class CsvHelper : ICsvHelper
        {
            private readonly ICachedObjectVisitor<T, CsvWriter> _headerWriter;
            private readonly ICachedObjectVisitor<T, CsvWriter> _bodyWriter;

            public CsvHelper()
            {
                _headerWriter = default(T)
                    .V()
                    .WithExtendObject<T, CsvWriter>()
                    .ForEach((name, value, w) => w.WriteHeader(name))
                    .Cache();

                _bodyWriter = default(T)
                    .V()
                    .WithExtendObject<T, CsvWriter>()
                    .ForEach((name, value, w) => w.WriteCell(value != null ? value.ToString() : ""))
                    .Cache();
            }

            public string ToCsv(IEnumerable<T> items)
            {
                var sb = new StringBuilder();
                var csvWriter = new CsvWriter(new StringWriter(sb));
                _headerWriter.Run(new T(), csvWriter);
                csvWriter.FinishHead();
                foreach (var item in items)
                {
                    _bodyWriter.Run(item, csvWriter);
                    csvWriter.FinishRow();
                }

                var re = sb.ToString();
                return re;
            }
        }
    }
}

With the previous  FormatToStringExtensions  equally , Here we use the form of generic static class with extension method to create help method . The difference is , In this example, there are two object visitor. So think more about abstracting one  ICsvHelper  And implementation classes to implement complex logic aggregation .

Skip specific columns when outputting

We want to output CSV Skip some specific columns , This requires filtering the attributes .

We add a new one Attribute:

public class IgnoreAttribute : Attribute
{
}

And then put this Attribute Mark on attributes that you don't want output :

public class OrderInfo
{
    public int OrderId { get; set; }
    [Ignore]
    public string Buyer { get; set; }
    public decimal TotalPrice { get; set; }
}

then , All we have to do is object visitor These are marked as Ingore The properties of .

The core changes are as follows :

static bool Filter(PropertyInfo p) => p.GetCustomAttribute<IgnoreAttribute>() == null;
_headerWriter = default(T)
    .V()
    .WithExtendObject<T, CsvWriter>()
    .FilterProperty(Filter)
    .ForEach((name, value, w) => w.WriteHeader(name))
    .Cache();

_bodyWriter = default(T)
    .V()
    .WithExtendObject<T, CsvWriter>()
    .FilterProperty(Filter)
    .ForEach((name, value, w) => w.WriteCell(value != null ? value.ToString() : ""))
    .Cache();

So we're exporting CSV There is no such column :

OrderId,TotalPrice
1,99999
2,36524
3,123456

summary

Let's understand... Through a simple production example object visitor Usage of . The actual production problem will be more complicated than this . Developers can try it out in production practice , Strengthen understanding .

Release notes

Use samples

Development documents may change with version , To view the latest development documents, you need to step forward  http://cn.ov.newbe.pro

Share with others

GitHub Project address :https://github.com/newbe36524/Newbe.ObjectVisitor

Gitee Project address :https://gitee.com/yks/Newbe.ObjectVisitor

Newbe.ObjectVisitor

​​​​​​​

------ In this paper, the end ------

版权声明
本文为[Newbe36524]所创,转载请带上原文链接,感谢
https://chowdera.com/2020/12/20201214232731776f.html

随机推荐