hasokeric
9/18/2016 - 12:31 AM

Erik Johnson From Epicor on BO and WCF

Erik Johnson From Epicor on BO and WCF


The service contract (Erp.Contracts.BO.OrderAlloc) passes a plain old class object (POCO) which Epicor derives from our own Tableset base class. We did this because it is faster to serialize these objects and there is better interoperability across platforms than with Datasets. Tablesets are just lists of lists of objects that make up the same content as a Dataset. But instead of calling Dataset methods to query or sort the data, you can use Linq to C#, which opens up a lot of other options for reuse.

All that said, our client application still data binds to Dataset fields. So the “Impl” classes (Erp.Proxy.BO.OrderAllocImpl) convert the Tableset representation to a Dataset. Both assemblies require a reference to Epicor.ServiceModel. The Impl classes have some helpers for communications (like retries) and security. The Contract classes are a little closer to the metal and use some Windows Communications Foundation libraries to execute. Here a console app example of using both…

static void TestUsingPartImpl()
{
	var wcfBinding = NetTcp.UsernameWindowsChannel();
	var appServer = new Uri("net.tcp://localhost/epicor10/erp/bo/part.svc");
	using (var partClient = new PartImpl(wcfBinding, appServer))
	{
		partClient.ClientCredentials.UserName.UserName = "Manager";
		partClient.ClientCredentials.UserName.Password = "Epicor123";
		bool morePages;
		var myPartDataset = partClient.GetList("", 10, 1, out morePages);
		foreach (var partRec in myPartDataset.PartList.Rows.Cast<PartListDataSet.PartListRow>())
		{
			Console.WriteLine(partRec.PartNum);
		}
		partClient.Close();
	}
}

static void TestUsingPartContract()
{
	var wcfBinding = NetTcp.UsernameWindowsChannel();
	var appServer = new Uri("net.tcp://localhost/epicor10/erp/bo/part.svc");
	using (ChannelFactory<PartSvcContract> cf = new ChannelFactory<PartSvcContract>(wcfBinding))
	{
		cf.Credentials.UserName.UserName = "Manager";
		cf.Credentials.UserName.Password = "Epicor123";

		var partClient = cf.CreateChannel(new EndpointAddress(appServer));
		bool morePages;
		var myPartTableset = partClient.GetList("", 10, 1, out morePages);

		foreach (var partRec in myPartTableset.PartList)
		{
			Console.WriteLine(partRec.PartNum);
		}
		cf.Close();
	}
}

Both approaches can be used in a BPM directive – it’s really a choice of which programming approach you want – basic object graphs or Datasets. Both approaches send the call through the network stack and into the server just like a call coming from a client app. Each call will have to authenticate/authorize the call, which adds some overhead.

There is apparently a better way using an internal Epicor class called the ServiceRenderer<T> where T is (I think) the actual service type (e.g. Erp.Services.BO.OrderAlloc). Using this would bypass the network stack and much of the setup/teardown code that happens when a call comes into the system. But I need some confirmation from R&D that this is actually an option and that it can work. I’m working with someone on the question, but he is on the road today. Hopefully we can get some better advice out by early next week.

-Erik