The first service we wrote simply created a facade for the class we had already written. When we write services, we usually write them in a single class that acts as a facade to other business logic. The aim of a facade is to simplify the instantiation and usage of a class. While doing this, we should keep the service methods we write within a topic, such as services methods that interact with vehicles. We wouldn't add methods that interact with service orders; these should be placed in a new class.
Since the ConVMSVehicleGroupChange class already accepted a contract as an input parameter to its Run method, the service method was relatively straightforward. In fact, we could have created a service directly to this class, but this wouldn't be good practice as this limits future extensions by tying the class directly to the service method.
The service method in our case accepted the same data contract that we passed to the ConVMSVehicleGroupChange.Run method, but this isn't always the case. We might have to look up some data in the method and perform more heavy lifting in the service method. The difference in our case was to construct a reply message contract that is returned to the caller.
The second method we wrote had a new concept, in that it returned a collection. In C#, we would return a typed collection or an array (MyClass[]), and we could use the foreach command to iterate through it. This is not supported in X++, so we have to return a List instead. Since we actually want to return a typed collection to the caller, we will use the AifDataCollectionType attribute to tell the compiler how to do this. You may notice that DataCollection is added to some data methods for the same purpose. This is correct for data contracts, but it does not work when it's used to write service methods. If we wrote an envelope data contract for the vehicle list, we would use DataCollection alongside the DataMethod decoration. An example of this is as follows:
[DataContract]
public class ConVMSVehicleTableListEnvelopeContract
{
List vehicles;
[DataMethod, DataCollection(Types::class,
classStr(ConVMSVehicleTableContract)]
public List Vehicles(List _vehicles = vehicles)
{
_vehicles = vehicles;
return vehicles;
}
}
The service method, in this case, would construct and return this data contract, and the AifCollectionType attribute would not be required.
The next part was to create a service and service group, which simply instructs the system to generate public services exposing the methods we added to the service.
We will see how this is used in the next recipe.