1

I have this piece of code

 public class Ticket
    {
        public string strArticleID { get; set; }
        public string strArticleDescription { get; set; }
        public decimal decArticlePrice { get; set; }
        public decimal decArticleVAT { get; set; }
        public decimal decArticuleNetPrice { get; set; }
        public decimal decArticleDiscount { get; set; }
        public decimal decArticleQuantity { get; set; }

    }
    public static List<Ticket> _lstCurrentTicket = new List<Ticket>();

That I want so send to an external DLL to get all the lines in _lstCurrentTicket to print a ticket through

for (int i = 0; i < dataGridView1.Rows.Count; i++)
        {
            Ticket ticket = new Ticket();

            string strRefID = this.dataGridView1.Rows[i].Cells[0].Value.ToString();
            string strDescription = this.dataGridView1.Rows[i].Cells[1].Value.ToString();
            decimal decQuantity = (decimal)this.dataGridView1.Rows[i].Cells[2].Value;
            decimal decUPrice = (decimal)this.dataGridView1.Rows[i].Cells[3].Value;
            decimal decDiscount = Convert.ToDecimal(this.dataGridView1.Rows[i].Cells[4].Value.ToString().Substring(0, this.dataGridView1.Rows[i].Cells[4].Value.ToString().Length - 1));
            decimal decVAT = Convert.ToDecimal(this.dataGridView1.Rows[i].Cells[5].Value.ToString().Substring(0, this.dataGridView1.Rows[i].Cells[5].Value.ToString().Length - 1));
            decimal decGPrice = (decimal)this.dataGridView1.Rows[i].Cells[6].Value;


            ticket.strArticleID = strRefID;
            ticket.strArticleDescription = strDescription;
            ticket.decArticlePrice = decUPrice;
            ticket.decArticleVAT = decVAT;
            ticket.decArticuleNetPrice = decGPrice;
            ticket.decArticleDiscount = decDiscount;
            ticket.decArticleQuantity = decQuantity;

            _lstCurrentTicket.Add(ticket);
        }


 TicketPrinting tktPrint = new TicketPrinting ();
 //Ticket and copies
 tktPrint.PrintTicketFromList(_lstCurrentTicket, 2);

Since it is an external DLL, I thought the easiest way to work with it in target DLL was

 public void PrintTicketFromList<T>(List<T> lstArticles, short intCopies)
    {            
        foreach (var prop in lstArticles.GetType().GetProperties())
        {

            if (prop.Name == "Item")
            {
                //Copy external list to local class for printing

            }
        }...

But I'm stuck there. How can I iterate each property and value from each original class in the list so I can copy it? If I make a breakpoint I can see that the fields and values are correctly passed, but I do not get how to access them so I can do something like creating a local class exactly like the original and clone the list (and if I try it will say local list(Ticket) and passed List(T) are not the same type).

Or how could I copy it if I create an exact class in the target and do something like

public void PrintTicketFromList(object lstArticles, short intCopies)
    {
        List<TargetDLLTicket> lst =((List<TargetDLLTicket>)lstArticles).ToList(); }

Any thoughts?

Mario
  • 283
  • 2
  • 13
  • 5
    What is an "external DLL" and why can't you pass that a `List`? – Jeroen Mostert Mar 31 '16 at 13:10
  • Please add some information on what this "external DLL" is supposed to to and what kind of interface it provides. – Codor Mar 31 '16 at 13:13
  • I mean, I created a DLL to print stuff and added it to the project as a reference. If I just do tktPrint.PrintTicketFromList(_lstCurrentTicket, 2); it will throw an error as I cannot access the elements in the list. – Mario Mar 31 '16 at 13:14
  • You could use something like AutoMapper to copy the properties you want, but I don't see why `PrintTicketFromList` needs to be generic in the first place. – Bradley Uffner Mar 31 '16 at 13:19
  • If the code where `PrintTicketFromList` lives does not reference the assembly where `Ticket` is defined (and you don't want it to), then you would need to use reflection to get the properties, or define an interface that your `Ticket` could use if there would be enough overlap of properties. – crashmstr Mar 31 '16 at 13:20
  • Reflection is used already. That is how I can get to see that lstArticles has a quantity X of "Tickets", but I can't find how to go deeper and get the variables. – Mario Mar 31 '16 at 13:22
  • Your printing logic depends on your `Ticket` class, so why not let the printing dll reference the assembly that contains that `Ticket` class? By inverting that dependency, you have to resort to reflection - which, in this case, takes more work *and* makes your code more brittle, because the compiler can no longer warn you about changes in `Ticket` that would break the printing logic. – Pieter Witvoet Mar 31 '16 at 13:40
  • Because the printing class is a module of the class where Ticket is contained and I cannot change the workflow... at least not now. Thanks for the ideas though. – Mario Mar 31 '16 at 13:47

3 Answers3

3

It sounds like you have a circular dependency issue. You need to move the types you are sending to your print function to a common assembly (new project) that is then referenced by both the calling project and your print project. Then both projects can access this shared type.

A note about your design. The way you are going about this is probably not good to begin with thus your error. The actual printer function should not have to know anything about the types passed in. A good rule of thumb is to try to make your code as loosly coupled as possible. A better idea is to create an Interface that takes care of writing to the printer canvas (or something like that, you did not provide your printer code so this is a guess) and the printer function can call that method on the incoming object. The printer method should then also only accept that interface as a parameter. This is based on a Visitor pattern. Here is an example.

public interface IPrintable {
   void WriteToPrinter(PrinterCanvas canvas);
}

public class Printer {
   public void Print(IPrintable somethingToPrint) {
      var canvas = getCanvas();
      somethingToPrint.WriteToPrinter(canvas);
   }
}
Igor
  • 60,821
  • 10
  • 100
  • 175
  • Printing method is a prototype only. It takes data from SQL so the only thing I need to pass is a string to be added to an SQL query as WHERE filter. Design and data are handled through XML. But I came across the idea to send the list (since it is also used for 3-4 additional processes) in case the connection with the SQL server was lost. – Mario Mar 31 '16 at 13:26
  • 2
    @Mario - I would decouple the getting of data to print from the actual printing of the data. Either standardize what is sent to the printer method so it is one expected type or use a visitor pattern like above. You can then have multiple methods that are in charge of gathering the data to print like your list function or your sql function but they must convert the data to the expected type for the printer function OR implement a common interface. – Igor Mar 31 '16 at 13:29
2

If at any point possible you should try to avoid reflection like Igor does in his answer.

But if you really want to use reflection you are currently not inspecting the item but the list of items.

You should try something like (writing this from memory):

public void PrintTicketFromList<T>(List<T> lstArticles, short intCopies)
{   
    foreach (var item in lstArticles)
    {
        foreach (var prop in typeof(T).GetProperties())
        {
            var value = prop.getValue(item);
        }
    }
}
Jan-Peter Vos
  • 3,157
  • 1
  • 18
  • 21
0

Instead of List<T> create an interface, ITicket for example and accept List<ITicket>. Using List<T> as a generic whenever you know you only can work with something that is a Ticket is creating an unnecessary wide range of potential inputs. Using an interface allows you to not worry about the concrete implementation, and instead get at only what your dll is concerned with, the contract.

You could put the interface in one of two places, either another external common assembly that both of your assemblies reference, or you could put the interface into your assembly that has the ticket printing logic. Your Ticket class could then implement the interface.

An example of what this could look like:

public interface ITicket
{
   //properties and methods you want to have all implementations to contain.
}

public class Ticket : ITicket
{
}

public class LastTicket :ITicket
{
}

public void PrintTicketFromList(List<ITicket> lstArticles, short intCopies)
{
}
davidallyoung
  • 1,302
  • 11
  • 15