Passing a table using Nav Automation

I just had someone comment on my previous blog post asking how to pass a table using Nav Automation. Great question.

You can pass complex objects using Nav Automation, but only ones that you define (or that have been explicitly written to work with COM). This means that you can’t pass a DataTable object, but you can create your own implementation of a DataTable object and pass it.

In your complex object, you have to explicitly define every property and method that you need to call on that complex object and surface those properties and methods through COM.

Enough talk. Let’s see the code. Here is some sample .Net code showing how to pass a table of addresses.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Diagnostics;
using System.ComponentModel;

[assembly: ClassInterface(ClassInterfaceType.AutoDual)]
namespace Tim.NavAutomation
{
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid(“12345678-a39f-4fde-9779-fc46749a7ff0”)]
[ComVisible(true)]
public interface IAddressDetail
{
[DispId(13200)]
string Address1 { get; set; }
[DispId(13201)]
string Address2 { get; set; }
[DispId(13202)]
string City { get; set; }
[DispId(13203)]
string State { get; set; }
[DispId(13204)]
string PostCode { get; set; }
[DispId(13205)]
string Country { get; set; }

[DispId(12306)]
public bool IsValidAddress();
}

[ComVisible(true)]
[Guid(“12345678-9e1f-4b4e-9a27-5e583cad9acf”)]
[ClassInterface(ClassInterfaceType.None)]
public class AddressDetail : IAddressDetail
{
private string _Address1;
public string Address1
{
get { return _Address1; }
set { _Address1 = value; }
}
private string _Address2;
public string Address2
{
get { return _Address2; }
set { _Address2 = value; }
}
private string _City;
public string City
{
get { return _City; }
set { _City = value; }
}
private string _State;
public string State
{
get { return _State; }
set { _State = value; }
}
private string _PostCode;
public string PostCode
{
get { return _PostCode; }
set { _PostCode = value; }
}
private string _Country;
public string Country
{
get { return _Country; }
set { _Country = value; }
}

public bool IsValidAddress()
{

// This is where you can put your own
// logic to validate that the address
// is valid (i.e., check with USPS
// web service, etc.)
throw new NotImplementedException();
}
}

[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid(“12345678-59f6-4d1e-803e-080c09572c52”)]
[ComVisible(true)]
public interface IAddressTable
{
[DispId(13210)]
int Length { get; }
[DispId(13211)]
AddressDetail Item(int i);
[DispId(13212)]
void Add(Address item);
}

[ComVisible(true)]
[Guid(“12345678-df87-4ab5-80ac-d979e38a3a58”)]
[ClassInterface(ClassInterfaceType.None)]
public class AddressTable : IAddressTable
{
public List
InternalAddressTable;

public int Length
{
get
{
if (InternalAddressTable == null)
{
return 0;
}
else
{
return InternalAddressTable.Count;
}
}
}
public TransactionDetail Item(int i)
{

if (i <>= InternalAddressTable.Count)
{
return null;
}
else
{
return InternalAddressTable[i];
}
}
public void Add(AddressDetail address)
{

if (InternalAddressTable == null)
{
InternalAddressTable = new List();
}
InternalAddressTable.Add(Item);
}
}

[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid(“12345678-6292-4F39-BB5B-1BDE0F69007B”)]
[ComVisible(true)]
public interface IAddressFunctions
{
[DispId(13300)]
public AddressTable GetNewAddresses();
[DispId(13301)]
public bool SendJunkMail(AddressTable addresses);
}

[ComVisible(true)]
[Guid(“12345678-B073-4C6A-B21E-6A19F3A7DE4E”)]
[ClassInterface(ClassInterfaceType.None)]
public class AddressFunctions : IAddressFunctions
{
public AddressTable GetNewAddresses()
{

AddressTable t = new AddressTable();

// Here is where you could insert your
// own logic to get a table of
// addresses from somewhere
AddressDetail det = new AddressDetail;
det.Address1 = “123 Main Street”;
det.City = “Baltimore”;
det.State = “Maryland”;
det.PostCode = “01252”;
t.Add(det);

det = new AddressDetail();
det.Address1 = “5123 Governor Lane”;
det.City = “Chesterville”;
det.State = “Kansas”;
det.PostCode = “71321”;
t.Add(det);

return t;
}

public bool SendJunkMail(AddressTable addresses)
{

foreach (AddressDetail d
in addresses.InternalAddressTable)
{
// Here is where you would call some
// external function to send junk
// mail to each Addresse
}
}
}
}

So how do we work with this on the Nav side? I’m glad you asked. Here is some sample (untested) code that shows the general idea. Note that AddrTable, AddrDetail, and AddrFunc are automation objects in Nav that have been tied to the automation types defined in the .Net code from above.

First, we’ll show how to take a table of data and pass it to .Net so that .Net can do something with the data.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CREATE(AddrTable);
CREATE(AddrDetail);
AddrDetail.Address1 := '5432 Small Dr.';
AddrDetail.City := "Smallsville";
AddrDetail.State := 'NY';
AddrDetail.PostCode := '12345';
AddrTable.Add(AddrDetail);

CLEAR(AddrDetail);
CREATE(AddrDetail);
AddrDetail.Address1 := '813 Garden Path';
AddrDetail.City := 'Mechanicsburg';
AddrDetail.State := 'Michigan';
AddrDetail.PostCode := '51238';
AddrTable.Add(AddrDetail);

CREATE(AddrFunc);
success := AddrFunc.SendJunkMail(AddrTable);

CLEAR(AddrFunc);
CLEAR(AddrDetail);
CLEAR(AddrTable);

And now I’ll show how to retrieve a table of data from .Net and then do something with it in Nav

CREATE(AddrFunc);
AddrTable := AddrFunc.GetNewAddresses();
IF AddrTable.Length &gt; 0 THEN BEGIN
i := 0;
REPEAT
AddrDetail := AddrTable.Item(i);
// Do something with AddrDetail.Address1,
// AddrDetail.Address2, etc. Perhaps
// you could add them to the contact table.
i := i + 1;
UNTIL i = AddrTable.Length;
END;

I hope that helps to show what you can do. Yes, it’s a pain. But that’s the nature of working with COM.