0

I'm getting a pipe-delimited flat file feed from a service provider that contains invoice, line item, and allocation information all merged into one. However, my object model for handling this information is normalized.

I have a (simplified) object model as follows:

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

My feed file resembles this:

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

In this example, Invoice 1 has two LineItems and LineItem 1 has 2 Allocations.

I have loaded the feed file into a variable records as IList<string[]>, split at the pipes.

How can I build this as a graph in a single Linq statement? It seems like it should be relatively straightforward but I get lost at the second level when I lose the reference to the pertinent records variable.

4

2 に答える 2

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;
        }
    }
}
于 2013-11-08T16:48:49.053 に答える
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);
}
于 2013-11-08T17:09:17.020 に答える