I think that the essence of the problem you are facing is that you have implemented OrderItem as a subclass of Item , and now you find that this is really not always appropriate.
Given what you described, here is how I would try to implement this:
Create an Order class that implements public properties for each unique data item that you want to open for data binding: order number, date, customer, general fees, general discounts, etc. It seems like you need to display specific fees / discounts as separate values; if so, implement public properties for them.
Create an abstract OrderItem class that implements public properties for each data item that you want to bind in the grid, and for each data item for which you want to sort the items. (You can also do this with the IOrderItem interface, it really depends on whether there will be methods common to all order elements.)
Subclass OrderItem (or classes that implement IOrderItem ) for specific item types that may appear in order: ProductOrderItem , FeeOrderItem , DiscountOrderItem , etc.
In your implementation of ProductItem , implement a property of type Item - it will look something like this:
public class ProductItem : OrderItem { public Item Item { get; set; } public string Description { get { return Item.Description; } } public int Quantity { get; set; } public decimal Amount { get { return Item.Price * Quantity; } } }
Add a property of type IEnumerable<OrderItem> inside the Order to store all positions. Implement the AddItem method to add OrderItems , for example:
public void AddItem(OrderItem item) { _Items.Add(item);
which you can name quite simply:
Order o = new Order(); o.AddItem(new ProductOrderItem { Item = GetItem(1), Quantity = 2 }); o.AddItem(new FeeItem { Description = "Special Fee", Amount = 100 }); o.AddItem(new DiscountItem { DiscountAmount = .05 });
Record implementations of those unique fields that should extract values from this list, for example:
public decimal TotalFees { get { return (from OrderItem item in Items where item is FeeItem select item.Amount).Sum(); } }
You can come back later and, if necessary, optimize these properties (for example, save calculations as soon as you have done this once).
Note that you can also limit AddItem adding ProductItem s and use other methods in Order to add other types of elements. For example, if an order can have only one discount:
public void SetDiscountAmount(decimal discountAmount) { DiscountOrderItem item = _Items .Where(x => x is DiscountOrderItem) .SingleOrDefault(); if (item == null) { item = new DiscountOrderItem(); _Items.Add(item); } item.DiscountAmount = discountAmount; }
You would use this approach if you would like to display the discount amount in the appropriate place in the grid of order elements, but also want the discount sum of the order to be the only value. (You might want to set the DiscountAmount property of Order , create a DiscountOrderItem in your setter, and DiscountOrderItem to get Amount from Order.DiscountAmount . I think both approaches have their pros and cons.)