Calling a SOAP WCF web service from .NET Core

I had a problem at work today that I’d previously solved, almost exactly a year ago. The project I was working on then got almost completely rewritten, so the current version of that code doesn’t have any reference to calling WCF web services at all. I kind of remembered that I’d written up a blog post about it, but couldn’t find it, since I was searching for SOAP instead of WCF. So I’m writing a new blog entry, with “SOAP” in the title, so if I have the same problem again, and I search for “SOAP” again, I’ll at least find this post, with a reference to the previous post. (Having a blog comes in handy, when your present-day self has to solve a problem that your past self has solved, but forgotten about…)

I don’t really have anything to add to that previous post. One thing I will do, though, is post the actual code here, rather than just embed a gist, since I now have a syntax highlighting solution that won’t garble it the way the previous setup did.

// https://gist.github.com/andyhuey/d67f78f6568548f66aabd20eadff8acf
// old way:
        public async Task RunAsync()
        {
            CallContext context = new CallContext();
            context.Company = "axcompany";
            string pingResp = string.Empty;
            var client = new XYZPurchInfoServiceClient();
            var rv = await client.wsPingAsync(context);
            pingResp = rv.response;
            Console.WriteLine("Ping response: {0}", pingResp);
        }
/* app.config:
    <system.serviceModel>
        <bindings>
            <netTcpBinding>
                <binding name="NetTcpBinding_XYZPurchInfoService" />
            </netTcpBinding>
        </bindings>
        <client>
            <endpoint address="net.tcp://myserver:8201/DynamicsAx/Services/XYZPurchInfoServices"
                binding="netTcpBinding" bindingConfiguration="NetTcpBinding_XYZPurchInfoService"
                contract="XYZPurchInfoSvcRef.XYZPurchInfoService" name="NetTcpBinding_XYZPurchInfoService">
                <identity>
                    <userPrincipalName value="myservice@corp.local" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
*/

// new way:
	CallContext context = new CallContext();
	context.Company = "axcompany";
	string pingResp = string.Empty;
	var client = new XYZPurchInfoServiceClient(GetBinding(), GetEndpointAddr());
	var rv = await client.wsPingAsync(context);
	pingResp = rv.response;
	Console.WriteLine("Ping response: {0}", pingResp);
	
	private NetTcpBinding GetBinding()
	{
		var netTcpBinding = new NetTcpBinding();
		netTcpBinding.Name = "NetTcpBinding_XYZPurchInfoService";
		netTcpBinding.MaxBufferSize = int.MaxValue;
		netTcpBinding.MaxReceivedMessageSize = int.MaxValue;
		return netTcpBinding;
	}

	private EndpointAddress GetEndpointAddr()
	{
		string url = "net.tcp://myserver:8201/DynamicsAx/Services/XYZPurchInfoServices";
		string user = "myservice@corp.local";

		var uri = new Uri(url);
		var epid = new UpnEndpointIdentity(user);
		var addrHdrs = new AddressHeader[0];
		var endpointAddr = new EndpointAddress(uri, epid, addrHdrs);
		return endpointAddr;
	}

Calling a Dynamics AX WCF service from .NET Core

A big part of my job these days is interop between Dynamics AX and various external services/resources. A WCF service hosted in our AX environment is often a key part of that equation. With older .NET Framework applications, it’s easy to add a reference to a WCF web service. And I’ve done that so often that I could probably do it in my sleep. If I need to interface with a new AX service, I’ll generally just go through the “Add Service Reference” procedure, then copy & paste some code from a previous project and adjust it for my curent needs.

I was recently working on a new program that I decided to try to write using .NET Core instead of .NET Framework. It took me quite a while to figure out how to deal with calling an AX web service under .NET Core, so I thought I’d write it up, briefly, with a couple of sample code snippets.

First, there is a facility for adding a WCF service reference in a .NET Core 2 project in VS 2017. (I think this might have been missing in earlier versions of VS and/or earlier versions of .NET Core.) It’s pretty similar to the tool that works with .NET Framework projects, but there are a few key differences in the generated code. The biggest difference is that it doesn’t add anything to app.config/web.config, and in fact isn’t set up to read any configuration info from the config files at all. So you need to do the config in your code. (Of course, you can write your own code to read from your config file.) Anyway, it took a lot of trial and error before I figured out what I needed to do. There’s not as much documentation on this as there could be. So here’s a simple example, showing a bit of code (and config) from a .NET Framework project, and the equivalent code from a .NET Core project.

(I’m embedding it below as a Gist, since I can’t get WordPress to play nice with the XML config sample right now.)


// old way:
public async Task RunAsync()
{
CallContext context = new CallContext();
context.Company = "axcompany";
string pingResp = string.Empty;
var client = new XYZPurchInfoServiceClient();
var rv = await client.wsPingAsync(context);
pingResp = rv.response;
Console.WriteLine("Ping response: {0}", pingResp);
}
/* app.config:
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_XYZPurchInfoService" />
</netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://myserver:8201/DynamicsAx/Services/XYZPurchInfoServices"
binding="netTcpBinding" bindingConfiguration="NetTcpBinding_XYZPurchInfoService"
contract="XYZPurchInfoSvcRef.XYZPurchInfoService" name="NetTcpBinding_XYZPurchInfoService">
<identity>
<userPrincipalName value="myservice@corp.local" />
</identity>
</endpoint>
</client>
</system.serviceModel>
*/
// new way:
CallContext context = new CallContext();
context.Company = "axcompany";
string pingResp = string.Empty;
var client = new XYZPurchInfoServiceClient(GetBinding(), GetEndpointAddr());
var rv = await client.wsPingAsync(context);
pingResp = rv.response;
Console.WriteLine("Ping response: {0}", pingResp);
private NetTcpBinding GetBinding()
{
var netTcpBinding = new NetTcpBinding();
netTcpBinding.Name = "NetTcpBinding_XYZPurchInfoService";
netTcpBinding.MaxBufferSize = int.MaxValue;
netTcpBinding.MaxReceivedMessageSize = int.MaxValue;
return netTcpBinding;
}
private EndpointAddress GetEndpointAddr()
{
string url = "net.tcp://myserver:8201/DynamicsAx/Services/XYZPurchInfoServices";
string user = "myservice@corp.local";
var uri = new Uri(url);
var epid = new UpnEndpointIdentity(user);
var addrHdrs = new AddressHeader[0];
var endpointAddr = new EndpointAddress(uri, epid, addrHdrs);
return endpointAddr;
}

view raw

wcf-example.cs

hosted with ❤ by GitHub

This example obviously isn’t applicable in all use cases. But I think it could point you in the right direction, if you’re trying to do this and you’re as befuddled as I was when I started this. I should also mention that reading the auto-generated code produced by the tool is somewhat useful, though the code is about as messy as most auto-generated code tends to be.

Some useful resources:

 

FizzBuzz

We’re hiring a new developer in my group at work, and my boss is including me in the interviewing process. It’s been a few years since I’ve done developer interviews, so I’m a bit rusty. I suggested having candidates do a FizzBuzz test on a whiteboard as part of the interview.

Jeff Atwood wrote a good post about FizzBuzz on his blog back in 2007. It seems like an overly simple test, but it can be quite useful. I’ve only been asked to do FizzBuzz once myself, and it was a good experience. The interviewer was really sharp and asked me a lot of good questions about how I could do it differently or why I chose to do something a certain way. He turned a simple 12-line program into a good conversation.

At very least, FizzBuzz should help filter out candidates who are exaggerating on their resumes. If you say you’ve got five years of C# experience and you can’t write a FizzBuzz program, you’re lying. The two candidates we’ve looked at so far both have an MS in Comp Sci, so they’re both better-educated than I am, at least, and they should both be able to handle FizzBuzz.

Anyway, it occurred to me that I never wrote a FizzBuzz program in X++. So here’s a short job to solve FizzBuzz in X++. I might post it to RosettaCode, if I get around to it. Not that the world really needs one more FizzBuzz solution.

static void AjhFizzBuzz(Args _args)
{
    /* Write a program that prints the numbers from 1 to 100. 
    If it’s a multiple of 3, it should print “Fizz”. 
    If it’s a multiple of 5, it should print “Buzz”. 
    If it’s a multiple of 3 and 5, it should print “Fizz Buzz”. 
    */
    int i;
    
    for (i = 1; i <= 100; i++)
    {
        if (i mod 3 == 0 && i mod 5 == 0)
            info("Fizz Buzz");
        else if (i mod 3 == 0)
            info("Fizz");
        else if (i mod 5 == 0)
            info("Buzz");
        else
            info(int2str(i));
    }
}

Five year work anniversary

I hit my five-year anniversary at SHI this week. I don’t really have much to say about that, but I thought I should mark it with a quick blog post. I mentioned SHI in my New Year’s Day post, so that covered my current status pretty well. I first mentioned the job on my blog in March 2013, after I’d been there for a couple of months. My current projects are a mix of straight Dynamics AX work in X++, some .NET stuff, using C#, and some research into Power BI. So it’s a pretty good mix. I think I might see some opportunities to do stuff with Power BI in the cloud and maybe some Azure stuff this year. So that could be fun.

We recently (finally) got current Visual Studio subscriptions at work, so I now have access to VS 2017 Pro and the other random fun stuff that comes with a VS subscription. (Previously, we had some kind of standalone licenses to VS 2012 and 2013.)

I’m always a little worried about stagnating and turning into “that guy” who has been doing COBOL programming for years and hasn’t learned anything new since the Nixon administration. But SHI seems to be giving me enough opportunities to work on new and interesting stuff, and I’m still trying to keep current independently, via services like Pluralsight and Safari, and podcasts like .NET Rocks and Hanselminutes. So I guess I’m still on the right career track.

Database snapshots

At work, we do our Dynamics AX development on VMs, set up with a full install of AX (SQL Server, AOS, and AX client). Prior to our R3 upgrade, we were using local VMs, under VMWare Workstation. This worked out quite well. One of my favorite things about this setup was the ease with which I could take VM snapshots, allowing me to run destructive tests, then roll back, fix bugs, and rerun the tests without having to jump through hoops to reset my environment or set up new test orders, or whatever. It was all pretty clean and easy.

But, after we upgraded to R3, we set up new VMs on vSphere. There are a number of advantages to this, but one disadvantage is that I don’t have rights on vSphere to snapshot my own VM. (I’m sure I could ask an admin to snapshot my VM, but the typical testing cycle of snapshotting, rolling back, fixing code, snapshotting again, etc., would probably annoy the admins.) So I’ve been looking for an alternative way to manage testing destructive processes.

I’ve settled on using SQL database snapshots. AX 2012 R3 stores all data in one database, and all code in a separate model database. (Versions prior to R2 mixed code and data in one database.) I’ve worked out a process by which I can pretty quickly take a snapshot, run my tests, delete the snapshot, and start again.

Given a database called DAX12_PROD, here’s a quick run-down on how to execute this process.

(1) Stop the AOS server.

(2) Create a snapshot:

CREATE DATABASE DAX12_PROD_SS1 ON
 ( NAME = DAX12_PROD, FILENAME = 'E:\your_sql_data_folder\DAX12_PROD_SS1.ss'
 ) AS SNAPSHOT OF DAX12_PROD

(3) Start the AOS & run your tests.

(4) Stop the AOS.

(5) Restore from the snapshot.

ALTER DATABASE DAX12_PROD
 SET SINGLE_USER WITH ROLLBACK IMMEDIATE

RESTORE DATABASE DAX12_PROD
 FROM DATABASE_SNAPSHOT = 'DAX12_PROD_SS1'

(6) If you’re done, then drop the snapshot.

DROP DATABASE DAX12_PROD_SS1

(6) Start the AOS.

So that doesn’t take too much effort and is pretty quick to run. The snapshot file is a sparse file, created with the same size as the actual database file, but not actually taking that much space on disk. So you don’t need to have a ridiculous amount of free space on your VM (as long as your test isn’t changing a ridiculous amount of data).

And yes, I know that it would be so much better if I could just run unit tests that don’t touch actual data, but it’s nearly impossible to do that for a lot of the stuff I have to do in AX. There are some interesting things you can do, in certain scenarios, like getting creative with setTmp, but that’s too simplistic for a lot of the testing I need to do.

Hosting a web browser on a Dynamics AX form

I’m working on an interesting little project at work right now. We use SharePoint to facilitate some workflow around our sales orders and purchase orders. But there’s currently no link between AX and SharePoint, so the sales and purchasing reps have to copy & paste information from AX to SharePoint forms. Not a huge deal, but a bit of a waste of time for everyone. So the idea was to add buttons to various forms in AX that would open a new SharePoint form, with certain fields pre-populated. I might write up some stuff on the SharePoint side of this later, but this post is going to be about the AX side.

The first (obvious) idea was just to launch an URL in the default web browser. And that works fine. Except that everyone is accessing AX through terminal servers. And, while IE is installed on those servers, the internet connection on those servers isn’t filtered the same way it is on end-user machines. So clever users could launch IE from AX, then navigate to restricted sites and possibly infect the terminal servers with malware. Which would be very bad.

My first thought was that there ought to be a way to launch IE on the end-user’s actual PC from the terminal server, but if there’s a way to do that, I can’t figure it out. (And it makes sense that there isn’t, really.) So my next thought was to launch the SharePoint site in a web browser control hosted in an AX form, with no address bar and no way to navigate away from that SharePoint site. Simple enough, right?

After a bit of web searching, I found this article on hosting an instance of System.Windows.Forms.WebBrowser in an AX form. I got pretty far with that, including preventing new windows from opening (which would allow them to break out of the control and into IE), and also preventing them from following links to other sites. But there was one key issue I couldn’t get past: the tab key and control keys wouldn’t work in the control. So the user wouldn’t be able to tab from field to field, or copy & paste information with Ctrl-C and Ctrl-V. I found a few references to this issue on StackOverflow and elsewhere, but no solutions that would have worked easily in Dynamics AX. (They mostly relied on doing things that would work in a real Window Forms app, in C++ or C#, but that I wasn’t going to be able to do in AX.)

So I punted on that, and decided to try just adding the ActiveX web browser control to the form. I’d never actually added an ActiveX control to a form; there’s a good overview about how to do that here. The most important thing I picked up from that is the “ActiveX Explorer” function that can be accessed form the context menu after you add an ActiveX control to a form. That’s how you hook into control events.

I managed to do everything I needed with the control:

  1. Set it to suppress JavaScript errors, via the silent flag. (Our SharePoint site has some messy JavaScript on it, that doesn’t cause any issues, but throws up some errors, if you don’t suppress them.)
  2. Prevent navigation outside the SharePoint site, which I can do by setting a cancel flag in the BeforeNavigate2 event handler.
  3. Prevent opening new windows, which I can do by setting a cancel flag in the NewWindow2 event handler.

And it handles the tab key and control keys normally, without any workarounds.

So that’s about it. ActiveX is a twenty-year-old technology, but it still works. As much as I would have liked to do something fancier, I can’t complain!

another busy week

My company’s upgrade to Dynamics AX 2012 R3 over the past weekend went well, all things considered. We’ve been having lingering problems all week, but we expected that we would, and we were prepared for it.

My hernia surgery is still scheduled for Monday, so I’ll be out all next week. I still have some open issues at work that I’ll have to close out or hand off to someone else today, but I’m sure they’ll get along fine without me.

Meanwhile, my warranty replacement SSD arrived from Samsung yesterday, so I can spend some time this weekend rebuilding my MacBook, hopefully. I have a tentative plan for doing a clean El Capitan install on the new drive, and then migrating my user data over from my old backup drive, but I’m not sure if it’ll work or not. Either way, I should have plenty of time to work on it next week.

A busy week

It’s been a busy week, starting with my follow-up visit to my doctor, where I found out I have a hernia. So a lot of my energy this week has been spent just thinking about that and planning for the surgery that I’m likely going to need.

And, at work, we’re in the last stretch of our upgrade from Dynamics AX 2012 RTM to R3. This is a really big update for us, as we have a lot of custom code, so there was a lot of work to do. The final upgrade is scheduled for this weekend, so I’ll have to work on Sunday. And the first week after the upgrade is liable to be a doozy, as various stuff we didn’t catch in testing shakes out.

Meanwhile, the RMA process for my MacBook’s SSD is plodding along. I got the RMA # and the UPS return label this week. I need to print out the label and package up the drive this weekend. I’ve been without my MacBook this week. and have been using my ThinkPad a lot, which is actually a pretty good experience, except that I keep wanting to use Emacs key bindings for a few things, like Ctrl-A and Ctrl-E for beginning of line and end of line. As soon as I get used to using ‘home’ and ‘end’, I’ll probably get the MacBook back up and running, and I’ll have to re-learn the Emacs key bindings.

And I’ve got my TiVo Bolt set up now. I dropped by the cable company office after work yesterday and picked up a CableCARD. I got it installed easy enough, but it took two calls to Optimum support to get it working. I think that “support tech roulette” gave me a clueless rep on the first go-round, and a more experienced one on the second. But both reps were polite and patient. Then it took me a couple of hours to set up the channels and my recordings. I’ve discovered that the channel line-up is a little different when you’re using a CableCARD than when you’re using a box. So I’m going to have to get used to some new channel numbers. The recordings seem to be working out OK too. I’ve got last night’s Daily Show, Nightly Show, and Late Show all on there, ready to watch. But I’ve also just figured out that I needed to re-run the guided setup to get the TiVo to fully recognize the channels that didn’t show up the first time around. Hopefully, that’s the last thing I’ll have to do, and it’ll be nice and stable now, with all the right channels and a fully-populated guide.

fun with credit card expiration dates

I’m doing some work on credit card processing right now. This is all related to my company’s upgrade from Dynamics AX 2012 RTM to 2012 R3. There were major changes to the credit card processing code in R2, and we’ve customized the code from RTM quite a bit, so there’s a lot to do. I’d like to write more about it, but it probably wouldn’t be of any general interest, plus I don’t want to get into any company-specific stuff.

But one very minor thing came up yesterday that was a little bit interesting. I noticed a support ticket from a user who was entering a new card, and wanted to enter an expiration date that wasn’t shown in the drop-down. (It was too far in the future.) This didn’t have anything to do with the upgrade, per se, but it made me curious enough to look at the code in RTM and in R3, to see what was being done. I was curious if maybe we had something in there like this example from The Daily WTF, or something where the upper-limit on the year was hard-coded to a specific year.

Well, it’s not quite that bad, but it is a bit questionable. In RTM, there’s a drop-down that is populated with years from (current year) to (current year + 7). In R3, it’s pretty similar, except that, when editing the expiration date after initial entry, you can go to current year + 8. (That’s probably an off-by-one error, rather than an intentional extension of the range.)

This all made me wonder what a reasonable upper-bound for expiration year would be, which lead me to this Stack Overflow question, which seems to indicate that there’s no agreed-upon maximum, but that it’s probably safe to go with 15-20 years. So Dynamics AX could stand to be be a bit more flexible on that range.

The other thing that bothers me about the way AX handles expiration dates is that (in 2012 RTM) they’re stored in the database as “MMYY”, which makes it difficult to sort and filter out expired cards. I was hoping that would be corrected in R3, but they’ve only changed it to “MM/YYYY” (without any leading zeroes on the month), which has the advantage of being more easily-readable by a human, and also of storing the full 4-digit year, but the disadvantage of being even more difficult to sort on, since the year sometimes starts in the 3rd position and sometimes in the 4th.

And let me end this blog post with a link to my very favorite publicly-documented method in AX, related to credit card processing: CreditCardWizard.codeCoverageFudge. I’m not entirely sure why this method exists in the released product, but I’m guessing it was added to fudge the code coverage numbers in some unit testing metrics. Maybe a certain percentage of code coverage had to be achieved, but there was no easy way to write meaningful tests for the remaining uncovered code, so they just wrote a method that does nothing, and a unit test for it?

AX 2012, the Wizard Wizard, and Origin GUIDs

I had a weird issue in AX recently, and, since I didn’t find much mention of it on the web, I thought I’d write it up for my blog.

A while back, I had used the “Wizard Wizard” to create a wizard form in AX. The name of this tool is kind of silly, but it’s basically correct — it’s a wizard that helps you create the form and class objects necessary to create a standard wizard control in AX. It worked fine, and the resulting wizard was deployed to production a few months ago.

I needed to make a change to it recently, though, and when I tried to import it to our testing environment, I got the following error: “Unable to save Form MyWizard. Origin value: {GUID value here} conflicts with element from model store.”

This led me down quite a path, as I haven’t really ever had a reason to dig into origin GUIDs before.
There’s a little bit of history on the origin GUID here and the best explanation I could find on origin GUIDs is here.

So, once I knew a bit about origin GUIDs, I did a little digging, and discovered that the origin GUID for my form was different in each of our environments (dev/TFS, staging, and prod). This should never happen; as the article above states, “this origin will be the same for this element in all installations, environments, versions and variations.” So… huh. How did that happen?

I looked into the TFS history, and discovered that the initial check-in for the form had no origin GUID at all. That line was simply missing from the XPO. So, if I understand things correctly, the lack of an origin GUID field in TFS is likely what caused it to be assigned new and different GUIDs in staging and production. (And, at some point, the field got filled in on my VM and made it into TFS with a completely different GUID.)

I looked into a number of other forms I’d created in the past, and couldn’t find a single instance of this ever happening before. I quickly realized that the one thing that was different about this particular form was that it had been created with the Wizard Wizard. I couldn’t initially find any evidence of an issue with this wizard, but eventually I found this question on Stack Overflow, from someone else who seems to have had the same issue.

So the end result is that I now know that, if I’m going to use the Wizard Wizard, I need to make sure I do something to force the generation of an origin GUID before I check anything in to TFS. (And that Stack Overflow question indicates that the “best practices” checker would likely have flagged this, so I should probably use that more often.)

I couldn’t come up with a particularly good solution to clean up this issue, other than duplicating the form to a new object, and deleting the old form. I briefly considered taking the GUID from prod, and trying to shoehorn it into the other environments, but that seemed like a bad idea, since I’d need to mess with TFS and with the ModelElement table in the database in each environment.