使用Linq规范化平面文件

时间:2013-11-08 16:31:56

标签: c# linq normalization

我从服务提供商那里获得一个以竖线分隔的平面文件供稿,其中包含所有合并为一个的发票,行项目和分配信息。但是,我处理此信息的对象模型已标准化。

我有一个(简化的)对象模型如下:

public class Invoice
{
    public int InvoiceId {get; set;}
    public decimal Amount {get; set;}
    public virtual ICollection<LineItem> LineItems {get; set;}
}

public class LineItem
{
    public virtual Invoice Invoice {get; set}
    public int SequenceNumber {get; set;}
    public decimal Quantity {get; set;}
    public decimal PricePerUnit {get; set;}
    public virtual ICollection<Allocation> Allocations {get; set;}
}

public class Allocation
{
    public virtual LineItem LineItem {get; set;}
    public string Account {get; set;}
    public decimal Distribution {get; set;}
}

我的Feed文件类似于:

InvoiceId|Amount|LineItemSequenceNumber|Quantity|PricePerUnit|Account|Distribution
1|100.00|1|1.0|50.00|1234567890|25.00
1|100.00|1|1.0|50.00|1111111111|25.00
1|100.00|2|50.0|1.00|1234567890|50.00
2|50.00|1|1.0|50.00|1234567890|50.00

在此示例中,Invoice 1有两个LineItem s,LineItem 1有2个Allocation s。

我已将Feed文件作为records加载到变量IList<string[]>中,并在管道处拆分。

如何在单个Linq语句中将其构建为图形?看起来它应该相对简单,但是当我丢失对相关records变量的引用时,我会在第二级丢失。

2 个答案:

答案 0 :(得分:1)

var invoices = (from r in input.Split(new [] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Split('|'))
                let invoiceId = int.Parse(r[0], CultureInfo.InvariantCulture)
                let amount = decimal.Parse(r[1], CultureInfo.InvariantCulture)
                let itemSequenceNumber = int.Parse(r[2], CultureInfo.InvariantCulture)
                let quantity = decimal.Parse(r[3], CultureInfo.InvariantCulture)
                let pricePerUnit = decimal.Parse(r[4], CultureInfo.InvariantCulture)
                let account = r[5]
                let distribution = decimal.Parse(r[6], CultureInfo.InvariantCulture)
                group new { amount, itemSequenceNumber, quantity, pricePerUnit, account, distribution } by invoiceId into g
                select new Invoice() {
                    InvoiceId = g.Key,
                    Amount = g.First().amount,
                    LineItems = (from i in g
                                 group i by i.itemSequenceNumber into g2
                                 select new LineItem() {
                                     SequenceNumber = g2.Key,
                                     Quantity = g2.First().quantity,
                                     PricePerUnit = g2.First().pricePerUnit,
                                     Allocations = (from a in g2
                                                    select new Allocation() {
                                                        Account = a.account,
                                                        Distribution = a.distribution
                                                    }).ToList()
                                 }).ToList()
                }).ToList();

它不会设置导航属性。你必须使用其他查询或循环来设置它们,例如

foreach(var i in invoices)
{
    foreach(var l in i.LineItems)
    {
        l.Invoice = i;
        foreach(var a in l.Allocations)
        {
            a.LineItem = l;
        }
    }
}

答案 1 :(得分:0)

试试这个,看它是否有效:

var records = new List<string[]>();

var invoices = new List<Invoice>();

// load records from file

foreach (var record in records)
{
    var id = Convert.ToInt32(record[0]);
    var myInvoice = invoices.SingleOrDefault(i => i.Id == id);
    if (myInvoice == null)
    {
        myInvoice = new Invoice();
        myInvoice.Id = id;
        myInvoice.Amount = Convert.ToDecimal(record[1]);
        myInvoice.LineItems = new ICollection<LineItem>();
        invoices.Add(myInvoice);
    }

    var sequenceNumber = Convert.ToInt32(record[2]);
    var myLineItem = myInvoice.LineItems.SingleOrDefault(li => li.SequenceNumber == sequenceNumber);
    if (myLineItem == null)
    {
        myLineItem = new LineItem();
        myLineItem.SequenceNumber = sequenceNumber;
        myLineItem.Quantity = Convert.ToDecimal(record[3]);
        myLineItem.PricePerUnit = Convert.ToDecimal(record[4]);
        myLineItem.Allocations = new ICollection<Allocation>();
        myInvoice.LineItems.Add(myLineItem);
    }

    var myAllocation = new Allocation();
    myAllocation.Account = record[5];
    myAllocation.Distribution = Convert.ToDecimal(record[6]);
    myLineItem.Allocations.Add(myAllocation);
}