0

Firstly, thanks in advance for anyone who can help.

I have a header and lines scenario. I want the lines object to auto-populate with the headerId from the Header. Please can someone help?

public class Header
{
    public int headerId { get; set; }
    public List<Lines> lines { get; set; }
}

public class Lines
{
    public int lineId { get; set; }
    public int headerId { get; set; }   //<<< want to autopopulate this from Header object
}
Ňɏssa Pøngjǣrdenlarp
  • 38,411
  • 12
  • 59
  • 178
dev123
  • 25
  • 1
  • 9
  • what version of visual studio are you using? – Matthew Whited Sep 15 '17 at 21:50
  • Hi Matt, VS2015 – dev123 Sep 15 '17 at 21:51
  • Where are you initializing the `Header` class and the list? – Jakub Dąbek Sep 15 '17 at 21:52
  • you don't really have a good option based on your design – Matthew Whited Sep 15 '17 at 21:52
  • I tried creating a constructor on the Lines object to set headerId somehow, but that didn't really work out. – dev123 Sep 15 '17 at 21:56
  • 2
    You may want to rethink your design choice on this. Ideally, since your `Header` class already contains `headerId`, then there is no reason to propagate that variable down to every Lines object in that list since it's already associated with the parent class of `Header`. – Arman Peiravi Sep 15 '17 at 21:58
  • Thanks Aman, it's so I could just access the lines list property and get back a list of all the lines in one go and pass straight into a DataTable. I have a workaround, just wondering if there was an easy way of populating it. – dev123 Sep 15 '17 at 22:05

2 Answers2

1

There's a number of ways you can do this.

Option 1 - Fix on collection set.

public class Header
{
    private List<Lines> _lines
    public int HeaderId { get; set; }
    public List<Line> Lines 
    { 
        get { return _lines; }
        set 
        { 
            _lines = value;
            if(_lines != null)
            {
                foreach(var line in _lines)
                    line.HeaderId = HeaderId;
            }
        }
    }
}

public class Line
{
    public int LineId { get; set; }
    public int HeaderId { get; set; }  
}

The problem with this route is that the HeaderId is only set when you set Lines collection. That means you can change it after the fact or any new items you add to Lines will not be corrected.

Option 2 - Pass in the Header when constructing the Line

public class Header
{

    public int HeaderId { get; set; }
    public List<Line> Lines { get; set; }
}

public class Line
{
    public Header ParentHeader { get; }
    public int LineId { get; set; }
    public int? HeaderId { get { return ParentHeader?.HeaderId; }  

    public Line(Header header)
    {
        ParentHeader = header;
    }
}

As long as you have the same Header instance for all of the lines, if you were change the Header.HeaderId all of the lines will automatically be updated.

Option 3 - Don't add an instance of Line directly to the collection

public class Header
{
    List<Line> _lines = new List<Line>();

    public int HeaderId { get; set; }
    public ReadOnlyCollection<Line> Lines { get {return _lines.AsReadOnly(); }


    public void AddLine(Line line)
    {               
        line.HeaderId = HeaderId;
        _lines.Add(line);
    }
}

This will have the same issue as Option 1.

TyCobb
  • 8,909
  • 1
  • 33
  • 53
  • On Option 1 if you copy your `Lines_set()` logic to the `HeaderId_set()` function then you can keep the Lines.HeaderID in sync with the Header.HeaderID. (one-way sync from Header to Line) – Sam Axe Sep 15 '17 at 22:13
  • Sam Axe, thanks for your reply also, I'll try that out too. – dev123 Sep 15 '17 at 22:24
0

You could do something like this...

public class Header
{
    static int _headerIdSeed = 0;

    public int HeaderId { get; internal set; } = _headerIdSeed++;
    public IList<Line> Lines { get; } = new List<Line>();

    public Header Add(Line line)
    {
        line.HeaderId = this.HeaderId;
        this.Lines.Add(line);
        return this;
    }

    public Header Add(params Line[] lines)
    {
        return this.AddRange(lines);
    }

    public Header AddRange(IEnumerable<Line> lines)
    {
        foreach (var line in lines)
        {
            line.HeaderId = this.HeaderId;
            this.Lines.Add(line);
        }
        return this;
    }
}

public class Line
{
    static int _lineIdSeed = 0;
    public int LineId { get; internal set; } = _lineIdSeed++;
    public int HeaderId { get; internal set; }
}

... and you can use it like this ...

var header = new Header
{
    HeaderId = 1,
}.Add(new Line { },
      new Line { },
      new Line { });

foreach (var line in header.Lines)
    Console.WriteLine($"{line.HeaderId}-{line.LineId}");
/*
1-0
1-1
1-2
*/
Matthew Whited
  • 22,160
  • 4
  • 52
  • 69