Philippsen's Blog

Everyday findings in my world of .net and related stuff

Posts Tagged ‘AIF’

How to export configuration settings for AIF inbound/outbound ports

Posted by Torben M. Philippsen on July 14, 2016

A very tedious task of mine has often been to deploy AIF services accross environments. I am used to do this the old school way = manually.

Today I had something that might be compared to as an epiphany – I stumbled upon this great technet artichle descripting how to export the configuration settings using the data import/export framework. Tried it out for the first time today and it’s working just great. The only downside so far, is that I haven’t been aware of this feature before…:-)

Posted in AIF, Microsoft Dynamics AX | Tagged: , , , , | Comments Off on How to export configuration settings for AIF inbound/outbound ports

AIF – getting offline WSDL files

Posted by Torben M. Philippsen on March 2, 2016

Quite often when dealing with AIF service I am asked to send a WSDL for a service to a 3rd party partner. Typically the partner dowsn’t have access to the service in that phase of the project. This means that I would have to send physical wsdl files (and not just an URL). In my experience the “singleWSDL” feature introduced with .net framework 4.5 doesn’t work for AIF services. In these cases AIF can be quite thedius to work with because the wsdl references other wsdl files as well as xsd’s. So in order to being able to send an offline wsdl file to a partner, you have to do a little bit of manual work. Ofcourse there are many ways to achieve the same, but here’s a guide of way, that I have found quite easy to work with.

  • Make sure the service has been published.
  • Open visual studio and create a console application. Add a new service reference pointing to the your service. Visuial studio now creates proxy classes as well as downloads referenced wsdl’s and xsd’s. You should see something similar to this in your project:
    wsdls
    Please note the 3 WSDL files and 6 XSD files.
  • Locate the physical location of the files (right click in VS and look at the file properties)
  • Copy all of the WSDL and XSD to a new folder of your choice (outside the project). This folder now contains the files that we want to send to our partner. But before sending them, we need to do some minor modifications.
  • Open the RoutingService.wsdl in your editor (VS will do just fine). Find and edit all import statements:
    import
    You will se that the Locations attribute references a http address. Edit the location to point to the local file residing in the current folder.
    You will want to do the same for the service location:
    Service
    Make sure to point to the local file instead of the url.
  • Do the same for the other 2 WSDL files. It’s quickly done.
  • When finished You can test your work by going back into visual studio. Just simulate adding a new service reference pointing to the local wsdl files instead. Remember it is the RoutingService.wsdl that is Your point of entry. If VS doesn’t complain You are now finished.
  • Finally zip the folder and sent it to the partner.

If You know of a better/easier way please post a comment…

Posted in AIF, Microsoft Dynamics AX | Tagged: , | Comments Off on AIF – getting offline WSDL files

How to implement basic authentication in Dynamics AX2012 webservice

Posted by Torben M. Philippsen on February 26, 2016

Recently I had to configure an inbound port for basic authentication. Furthermore I had to produce a guide in order to being able to set it up in the same way in other environments.

Lucky for me, I found this great guide, that I would like to share. You can also use it as a template for configuring other types of authentication.

https://blogs.msdn.microsoft.com/axsupport/2013/04/23/how-to-implement-basic-authentication-with-dynamics-ax-2012-web-services/

Posted in AIF, Microsoft Dynamics AX | Tagged: , , , | Comments Off on How to implement basic authentication in Dynamics AX2012 webservice

AIf – Some or all identity references could not be translated.

Posted by Torben M. Philippsen on May 11, 2015

In a recent task i struggled with that error when calling an AIF http service. It would work from visual studios internal casini browser but when the client was deployed to IIS the error “ Some or all identity references could not be translated.” would display in the AIF exception log.

It was not possible to set up proper debugging in a timely manner so a little fix was created in order to being able to see why the error was being thrown.

Analyzing the labelid’s it turned out that the error with that labelid would only be thrown from one single location data dictionary/tables/AifPortUser. From there a call to to AifUtil::getWindowsUserSid(windowsUser) would occur causing the exception to be thrown. So the interesting thing would be to analyze what windows user actually was submitted by the client. In order to do that the AifUtil::getWindowsUserSid was modified in order to write the domain, alias and usersid to a text file:

        //ztmp
        TextIo textIo;
        FileIOPermission fileio;   
        //ztmp
  

        .

        .

        .

 

        //ztmp
        fileio = new FileIOPermission(@"C:\test\", #io_append);
        fileio.assert();
       
        textIo = new TextIo(@"C:\test\textIOtest.txt", #IO_WRITE);
        textIo.write("domain: " + domain + "\n");
        textIo.write("alias: " + alias + "\n");
        textIo.write("userSid: " + userSid + "\n");
        CodeAccessPermission::revertAssert();
        //ztmp

That little snipped showed that the identity of the default apppool was submitted and not the actual windows user. Changing the apppool identity fixed the problem.

Posted in AIF, Microsoft Dynamics AX | Tagged: , | Comments Off on AIf – Some or all identity references could not be translated.

A call to SSPI failed–DynAx 2012 AIF/WCF

Posted by Torben M. Philippsen on January 13, 2015

Recently I deployed an AIF service to a customer environment. Everything was working fine in my single server development environment, but after deploy to the distributed customer environment, calls to the webservice resulted in the error “A call to sspi failed”.

The scenario:

  • My service – a simple document service. No hex about that.
  • I needed to deploy the service to IIS in order for it to be consumable from a corporate website
  • The customer environment contained a standalone server for the AOS and a standalone server for IIS
  • I created a simple test webform – my test client, in order to being able to test that everything was working ok.

Having deployed the service, the service was browsable. The identity of the application running the AIF site was the same as the one used for the Business connector proxy account (System administration –> setup –-> service accounts) The app pool was configured like this:

AppPoolConfig

Authentication was configured like this:

Authentication

Here’s a nice reference on how to install AIF on IIS when using Ax2012

From AX my service was configured to use a customBinding using NTLM and my clienct was also configured to use NTLM. Any call from the client to the service would result in the error “A call to SSPI failed – see inner exception…” – and no inner exception were to be found.

Trying to narrow down the problem a basicHTTPBinding was tried – still the same error.

As different kinds of blogposts suggested, I was able to call the AIF/WCF service when the service itself was using the ipaddress (to avoid the use of kerberos) of the aos server instead of the url. However this wasn’t an acceptable solution, as any new deployment of the service from AX, would result in a non working webservice, since the web.config would be overwritten when deploying from AX. And as it turned out, it was not possible to alter settings in AX forcing ax to deploy the service and having the endpoint in web.config reference the ip address instead af the FQDN. However the problem was now narrowed down to be caused by kerberos. I found this great blogpost explaining some basic things about Kerberos.

Another thing we tried out was to set the spn for the user running the service:

Setspn –A HTTP/2012webtest.myDomain.local myDomain\sa-proxy-lon

Having done that we tried to setup trust for delegation in AD according to this. We are not sure whether this had any effect, but we didn’t reverse the process.

This blogpost (see comment from Eric Ledoux and Brian Kinser) suggested that this might be caused by a kernel error. My customer recently upgraded to R2CU7 and I was expecting this to be fine, but talking with the technician from the customer revealed that IIS might not have been updated in that process with the new AX components. Running the setup file from the CU7 install media, suggested to update some core AX components. Choosing yes to update, restarting IIS and the AOS service, fault messages from ax started to show up when calling the webservice – meaning that everything was starting to work as expected.

Conclusion

In my case the “a call to sspi failed” error turned out to be resolved when upgrading to CU7. The problem I was facing was just caused by the fact that only the AOS had been upgraded – not IIS. Resolving this mismatch solved the problem.

Thanks to my colleague Morten Uldall for both moral and technical support:-)

Posted in AIF, Microsoft Dynamics AX | Tagged: , , , , | Comments Off on A call to SSPI failed–DynAx 2012 AIF/WCF

AX2012 AIF – CallContext

Posted by Torben M. Philippsen on October 8, 2014

Working with AIF on the Dynamics AX 2009 platform you had to create the SoapHeader manually. In that you had to specify the destination endpoint and the source endpoint user in order to being able to target a specific company using and maybe using a specific AX user. It could look like something similar to this:

 

public static class SoapHeader

    {

 

        /// <summary>

        /// Helper method – adds a SOAP Header defining the destination endpoint (local endpoint) in Dynamics AX

        /// </summary>

        /// <param name="nameOfEndpoint">The name of the local endpoint</param>

        public static void SetDestinationEndpoint(string _nameOfEndpoint)

        {

            OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("DestinationEndpoint", "http://schemas.microsoft.com/dynamics/2008/01/services&quot;, _nameOfEndpoint));

        }

 

        /// <summary>

        /// Helper method – adds a SOAP Header defining the source endpoint name and the source endpoint user to use

        /// </summary>

        /// <param name="sourceEndpointName">the name of the source endpoint</param>

        public static void SetSourceEndpointAndUser(string _sourceEndpointName, string _userName)

        {

            //string userName = HttpContext.Current.User.Identity.Name.ToString(); //returns the current user and domian – eg. egdk\tomph

            var addressHeader = AddressHeader.CreateAddressHeader("SourceEndpointUser", "http://schemas.microsoft.com/dynamics/2008/01/services&quot;, _userName);

            var addressBuilder = new EndpointAddressBuilder(

            new EndpointAddress(new Uri("urn:" + _sourceEndpointName), addressHeader));

            var endpointAddress = addressBuilder.ToEndpointAddress();

            OperationContext.Current.OutgoingMessageHeaders.From = endpointAddress;

        }

 

 

    }

            //call the webservice’s find method

            try

            {

                //SOAP header info

                using (new OperationContextScope(client.InnerChannel))

                {

                    //CREATE HEADER TO SET SOURCE ENDPOINT

       //this assumes that a endpoint (inside AX) with the selected (dataareaid in this case) name has
       been created for all companies

                    SoapHeader.SetDestinationEndpoint(ddlDataAraeId.SelectedValue);

                    //CREATE HEADER TO SET TARGET COMPANY

       //this assumes that a local endpoint (inside ax) with the name [ddlDataAraeId.SelectedValue] has
       been configured and is associated with a company that exists in dynamics ax

                    SoapHeader.SetSourceEndpointAndUser("Default", Helper.GetCurrentUser());

                    //submit the request and retrieve the respons

                }   

                response = client.find(qc);

                enumerEmplTable = response.EmplTable.GetEnumerator();

            }

 

 

Now in AX 2012 AIF you can simply specify the CallContext – which I may say is a h… of a lot easier…

 

            //create the AX call context in order to being able to define Ax company and submitting user

            CallContext axContext = new CallContext();

            axContext.Company = ddlDataAraeId.SelectedValue;

            axContext.LogonAsUser = Helper.GetCurrentUser();

 

            try

            {

                #if DEBUG

                CreateXmlMessageTextFileFromCreate(axdEGF_HRMWebRecruitment, Guid.NewGuid().ToString());

                #endif

                keys = client.create(axContext, axdEGF_HRMWebRecruitment);

                txtResult.Text = "SUCCESS: " + keys[0].KeyData[0].Field + " = " + keys[0].KeyData[0].Value;

            }   

Thank You MS for making my life just a little bit easier:-)

You may find it relevant to look at the this peace of documention on technet.

Posted in AIF | Tagged: , , , , | Comments Off on AX2012 AIF – CallContext

AIF–Invalid data container type

Posted by Torben M. Philippsen on October 8, 2014

Recently Ive been developping a AIF document service. After days of testing and customizing my requests suddenly started to fail and I would receive an AIF error in the Exception log in AX stating that “Invalid data container type”. Tome, this came out of the blue, since everything up to taht point worked just fine. I can’t explain the error, but the sollution to me was to:

  • update the document service using the AIF wizard with the switch to update AxBC classes (not regenerate)
  • Recompile the entire private service project.
  • Perform incremental CIL
  • Unpublish an publish the service.

I’m not sure whart part actually solved the problem – but the main goal was to get everything working again – and it did.

Posted in AIF | Tagged: , , | Comments Off on AIF–Invalid data container type

AIF–handling related incoming records

Posted by Torben M. Philippsen on October 7, 2014

In a recent task for a customer I had to being able to handle HRM applicants and applications from a corparate website. In basics the customer wanted to have a webform where the applicant could enter his/her personal data and attach files like eg. a CV, the application or any other relevant stuff.

To handle that scenario I created two tables in AX – one to handle the applicant and application related stuff and another to handle the file attachments. One application should be able to have many attachments. Therefore the first attachment should relate to one application and the second attachment to the same application and so on…

Having the datamodel set, I created a document service.

I created a simple webform in c# that consumed the AIF service and I was soon able to send data into AX. Then it occured to me, that I wasn’t able to create the relation between attachments and applications from the webclient because I needed the RecID og the Application in order to put in onto the attachments.

Inside ax i found the PrepareForSaveExtended method of my AXD class to be useful. Here I could make sure that applications would be processed before attachments. And once the applications were processed I could fetch the RecId and put in on each attachment.

I ended up with this:

Class declaration:

 

class Axdegf_HRMWebRecruitment extends AxdBase

{

    #define.WebRecruitment_DataSourceName(‘WebRecruitment’)

    #define.Attachments_DataSourceName(‘Attachments’)

   

    //EG/2014.10.07/TOMPH, 00328407_3 HRMWebRecruitment –>   

   AxEGF_HRMWebRecruitment            axbc_WebRecruitment;

    AxEGF_HRMWebRecruitmentAttachments axbc_Attachments;

 //EG/2014.10.07/TOMPH, 00328407_3 HRMWebRecruitment <–

}

PrepareForSaveExtended:

public boolean prepareForSaveExtended(

    AxdStack                    _axBcStack,

    str                         _dataSourceName,

    AxdRecordProcessingContext  _recordProcessingContext,

    AxInternalBase              _childRecord)

{

    //TODO: Add code here to ensure that required fields specified in the initMandatoryFieldsMap method are sent in by the service caller.

    switch (_dataSourceName)

    {

        // ———————————————————————-

        // Process WebRecruitment records

        // ———————————————————————-

        case #WebRecruitment_DataSourceName:

 

            axbc_WebRecruitment = _axBcStack.top();

            switch (_recordProcessingContext)

            {

                //EG/2014.10.01/TOMPH, 00328407_3 HRMWebRecruitment –>

               case AxdRecordProcessingContext::BeforeChildRecordProcessed:

                    switch(_childRecord.dataSourceName())

                    {

                        case #Attachments_DataSourceName:

                            if(!axbc_WebRecruitment.isProcessed())

                            {

                                return true;

                            }

                            break;

                   }

                    return false;

                    //EG/2014.10.01/TOMPH, 00328407_3 HRMWebRecruitment <–

                // Ensure WebRecruitment record is saved

                case AxdRecordProcessingContext::AfterAllChildRecordsProcessed:

                    if (!axbc_WebRecruitment.isProcessed())

                    {

                        return true;

                    }

                    return false;

            }

            return false;

        // ———————————————————————-

        // Process Attachments records

        // ———————————————————————-

        case #Attachments_DataSourceName:

 

            axbc_Attachments = _axBcStack.top();

            //axbc_Attachments.parmDescription("dudelidut");

            switch (_recordProcessingContext)

            {

                //EG/2014.10.01/TOMPH, 00328407_3 HRMWebRecruitment –>

                // Propagate parent’s key

                case AxdRecordProcessingContext::BeforeAnyChildRecordsProcessed:

                        axbc_WebRecruitment = axbc_Attachments.parentAxBC();

                        axbc_Attachments.parmHRMWebRecruitmentRecIdRef(axbc_WebRecruitment.currentRecord().RecId);

                        return false;

                //EG/2014.10.01/TOMPH, 00328407_3 HRMWebRecruitment <–

 

                // Ensure Attachments record is saved

                case AxdRecordProcessingContext::AfterAllChildRecordsProcessed:

                    if (!axbc_Attachments.isProcessed())

                    {

 

                        return true;

                    }

 

                    return false;

            }

            return false;

 

        // ———————————————————————-

        // Unsupported data sources

        // ———————————————————————-

        default:

            error(strfmt("@SYS88979",classId2Name(classidget(_axBcStack.top()))));

            return false;

    }

    return false;

}

 

Posted in AIF | Tagged: , , , | 2 Comments »

Digging into AIF

Posted by Torben M. Philippsen on November 29, 2011

Given my experiance in WCF I recently had the pleasure to dig into AIF (the Application Integration Framework in Dynamics AX).

As a newbie I quickly had to learn my lessons – and to avoid having to learn the same lessons twice I decided to add this post:-)

Initially I had to realize that these questions needed an answer:

  • Inside Dynmics AX – what is an endpoint?
  • Inside AX – what’s the idea of having local endpoints along with endpoints – what’s the difference?
  • How do I target a specific company?
  • How do I configure my .net client to use a certain AX endpoint?
  • How do I make sure that a certain datapolicy is actually applied to incoming messages?

In my examples I will use the build in LedgerPurchaseInvoiceService.

Asuming that this service has been enabled in “basic -> setup -> Application Integration Framework -> services” go to “basic -> setup -> Application Integration Framework -> endpoints”. There You will at least find the default Endpoint. AX uses the “Default Endpoint” as the destination endpoint and the local endpoint of the user calling the service for processing WCF requests. I wanted to be able to specify which endpoints my requests were using in order to take advantage of the security and processing features in AIF, This means that if no endpoint is specified from the client side when sending messages through AIF, the default endpoint is used. This also means that if You configure Your own endpoints these need to be specified from the client side…

We need to answer some of the questions above:

  1. What is an endpoint?
    Well I will try to explain this in my own terms… An endpoint is the single point of entry that is used from the client side. An endpoint is associated to a company which means if you configure an endpoint in company “A” you won’t be able to use that endpoint from comapny “B”.
    The endpoint is where you define the datapolicies – which fields are enabled, which fields are required and so on…
    An endpoint is based on a local endpoint.
  2. What’s the idea of having local endpoints?
    Local endpoints are global:-) This means that any of the local endpoints are visible through out all companies. A local endpoint is used to point to a certain company. Which brings us to question number 3
  3. If I want to target a certain company from the client side I will have to point to an endpoint configured in that company that uses a local endpoint associated to the same company.
    endpoints

    endpoints

    local_endpoints

    local_endpoints

    In my case I want to use the “SignFlow” endpoint from the client – which brings us to question number 4

  4. How do I configure my .net client to use a certain AX endpoint?
    The  magic word is “SOAP Headers”. Before calling any of the functions of your client object. You need to specify the destination endpoint, the sourceEndpoint and the sourceEndpointUser. The last two headers requires that You use a binding type that supports WS-Addressing.
    To add the SOAP Header You do the following:
 			PurchaseInvoiceServiceClient client = new PurchaseInvoiceServiceClient ();
 			try
 			{
 				//SOAP header info
 				using (new OperationContextScope (client.InnerChannel))
 				{
 					//CREATE HEADER TO SET SOURCE ENDPOINT
 					//this assumes that a endpoint (inside AX) with the name [SignFlow] has been created for all companies
 					SetDestinationEndpoint(ddlDataAraeId.SelectedValue);
 					//CREATE HEADER TO SET TARGET COMPANY
 					//this assumes that a local endpoint (inside ax) with the name [ddlDataAraeId.SelectedValue] has been configured
 					//and is associated with a company that exists in dynamics ax
 					SetSourceEndpointAndUser("SignFlow" );
 					//submit the request and retrieve the respons 
 					keys = client.create(recordWrapper);
 				}
 			}

 		/// <summary>
 		/// Helper method - adds a SOAP Header defining the destination endpoint (local endpoint) in Dynamics AX
 		/// </summary>
 		/// <param name="nameOfEndpoint">The name of the local endpoint</param>
 		private void SetDestinationEndpoint(string nameOfEndpoint)
 		{
 				OperationContext .Current.OutgoingMessageHeaders.Add(MessageHeader .CreateHeader("DestinationEndpoint" , "http://schemas.microsoft.com/dynamics/2008/01/services" , nameOfEndpoint)); 
 		}

 		/// <summary>
 		/// Helper method - adds a SOAP Header defining the source endpoint name and the source endpoint user to use
 		/// </summary>
 		/// <param name="sourceEndpointName">the name of the source endpoint</param>
 		private void SetSourceEndpointAndUser(string sourceEndpointName)
 		{
 			string userName = HttpContext .Current.User.Identity.Name.ToString(); //returns the current user and domian - eg. domainname\\username
 			var addressHeader = AddressHeader .CreateAddressHeader("SourceEndpointUser" , "http://schemas.microsoft.com/dynamics/2008/01/services" , userName);
 			var addressBuilder = new EndpointAddressBuilder (
 			new EndpointAddress (new Uri ("urn:" + sourceEndpointName), addressHeader));
 			var endpointAddress = addressBuilder.ToEndpointAddress();
 			OperationContext .Current.OutgoingMessageHeaders.From = endpointAddress;
 		}

The entire client code looks like this:

 using System;
 using System.Web;
 using System.Web.UI;
 using System.ServiceModel;
 using System.ServiceModel.Channels;
 using SignFlowIntegration.EG_InitialPurchaseInvoiceReg;

 namespace SignFlowIntegration
 {
 	public partial class InitialReg : System.Web.UI.Page
 	{
 		private enum DataAreaIdCollection { SVE, DMO, DAT, LON, MAS, FIN };
 		private enum CurrencyCodeCollection { DKK, AUD, CHF, CAD, EUR, GBP, NOK, SEK, USD, PLN };
 		protected void Page_Load(object sender, EventArgs e)
 		{
 			if (!Page.IsPostBack)
 			{
 				ddlDataAraeId.DataSource = System.Enum .GetValues(typeof (DataAreaIdCollection ));
 				ddlCurrencyCode.DataSource = System.Enum .GetValues(typeof (CurrencyCodeCollection ));
 				ddlDataAraeId.DataBind();
 				ddlCurrencyCode.DataBind();
 			}
 		}

 		/// <summary>
 		/// Handles the submit button click event
 		/// </summary>
 		/// <param name="sender"></param>
 		/// <param name="e"></param>
 		protected void btnSubmit_Click(object sender, EventArgs e)
 		{
 			decimal amountCur;
 			DateTime documentDate;
 			//create the client
 			PurchaseInvoiceServiceClient client = new PurchaseInvoiceServiceClient ();
 			//create wrapper for record object
 			AxdPurchaseInvoice recordWrapper = new AxdPurchaseInvoice ();
 			//create record objects
 			AxdEntity_LedgerJournalTable ledgerJournalTableObj = new AxdEntity_LedgerJournalTable ();
 			AxdEntity_LedgerJournalTrans ledgerJournalTransObj = new AxdEntity_LedgerJournalTrans (); 
 			//-----------------------------------------
 			//step 1
 			//-----------------------------------------
 			//assign values to record objects
 			ledgerJournalTableObj.JournalName = txtJournalName.Text;
 			ledgerJournalTableObj.JournalTypeSpecified = true ;
 			ledgerJournalTableObj.JournalType = AxdEnum_LedgerJournalType .PurchaseLedger;
 			ledgerJournalTransObj.AccountNum = txtAccountNum.Text;
 			ledgerJournalTransObj.EG_SignFlowCaseId = txtSignFlowsagsnr.Text;
 			if (DateTime .TryParse(txtDocumentDate.Text, out documentDate))
 			{
 				//set documentdate
 				ledgerJournalTransObj.DocumentDate = documentDate;
 			}
 			else
 			{
 				//default
 				ledgerJournalTransObj.DocumentDate = DateTime .Now;
 			}
 			ledgerJournalTransObj.CurrencyCode = ddlCurrencyCode.SelectedValue;
 			ledgerJournalTransObj.ApprovedBy = txtApprovedBy.Text;
 			ledgerJournalTransObj.Invoice = txtInvoice.Text;
 			if (decimal .TryParse(txtAmountCurCredit.Text, out amountCur))
 			{
 				ledgerJournalTransObj.AmountCurCreditSpecified = true ;
 				ledgerJournalTransObj.AmountCurCredit = amountCur;
 			}
 			else if (decimal .TryParse(txtAmountCurDebit.Text, out amountCur))
 			{
 				ledgerJournalTransObj.AmountCurDebitSpecified = true ;
 				ledgerJournalTransObj.AmountCurDebit = amountCur;
 			}
 			else
 			{
 				//default
 				ledgerJournalTransObj.AmountCurCreditSpecified = true ;
 				ledgerJournalTransObj.AmountCurCredit = 0;
 			}
 			//-----------------------------
 			//step 2 - add record to wrapper
 			//-----------------------------
 			recordWrapper.LedgerJournalTable = new AxdEntity_LedgerJournalTable [1]; 
 			ledgerJournalTableObj.LedgerJournalTrans = new AxdEntity_LedgerJournalTrans [1];
 			ledgerJournalTableObj.LedgerJournalTrans[0] = ledgerJournalTransObj;
 			recordWrapper.LedgerJournalTable[0] = ledgerJournalTableObj;
 			//Set impersonation level
 			client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel .Impersonation;
 			//prepare the repons object
 			EntityKey [] keys = new EntityKey [1];
 			//--------------------------------------
 			//step 3 - call create method on service
 			//add proper error handling
 			//--------------------------------------

 			try
 			{
 				//SOAP header info
 				using (new OperationContextScope (client.InnerChannel))
 				{
 					//CREATE HEADER TO SET SOURCE ENDPOINT
 					//this assumes that a endpoint (inside AX) with the name [SignFlow] has been created for all companies
 					SetDestinationEndpoint(ddlDataAraeId.SelectedValue);
 					//CREATE HEADER TO SET TARGET COMPANY
 					//this assumes that a local endpoint (inside ax) with the name [ddlDataAraeId.SelectedValue] has been configured
 					//and is associated with a company that exists in dynamics ax
 					SetSourceEndpointAndUser("SignFlow" );
 					//submit the request and retrieve the respons 
 					keys = client.create(recordWrapper);
 				}
 			}
 			catch (System.ServiceModel.FaultException <AifFault > aex)
 			{

 				//TODO add proper error handling
 				txtResult.Text += "AIF ERROR\\n" ;
 				txtResult.Text += "Message: " + aex.Message + "\\n" ;
 				txtResult.Text += "Source: " + aex.Source + "\\n" ;
 				txtResult.Text += "StackTrace: " + aex.StackTrace + "\\n" ;
 				txtResult.Text += "----------------------------------------\\n" ;
 				//txtResult.Text += aex.Detail.FaultMessageListArray[0].FaultMessageArray[0].Message;
 			}
 			catch (System.ServiceModel.FaultException fex)
 			{
 				//SOAP errors are handled here
 				//TODO add proper error handling
 				txtResult.Text += "Fault ERROR\\n" ;
 				txtResult.Text += "Message: " + fex.Message + "\\n" ;
 				txtResult.Text += "Source: " + fex.Source + "\\n" ;
 				txtResult.Text += "StackTrace: " + fex.StackTrace + "\\n" ;
 			}
 			catch (Exception ex)
 			{
 				//TODO add proper error handling
 				txtResult.Text += "ERROR\\n" ;
 				txtResult.Text += "Message: " + ex.Message + "\\n" ;
 				txtResult.Text += "Source: " + ex.Source + "\\n" ;
 				txtResult.Text += "StackTrace: " + ex.StackTrace + "\\n" ;
 			}
 			finally
 			{
 				//close the client
 				client.Close();
 			}

 			//----------------------------------
 			//Step 4 - Handle the response
 			//----------------------------------
 			//examine the respons - is it valid?
 			if (keys[0] != null ) // the repons contains info ->'s valid
 			{
 				txtResult.Text += "SUCCESS\\n" ;
 				KeyField [] fields = keys[0].KeyData;
 				foreach (KeyField field in fields)
 				{
 					//print the result
 					txtResult.Text += field.Field + ": " + field.Value + "\\n" ;
 				}
 			}
 		}

 		/// <summary>
 		/// Helper method - adds a SOAP Header defining the destination endpoint (local endpoint) in Dynamics AX
 		/// </summary>
 		/// <param name="nameOfEndpoint">The name of the local endpoint</param>
 		private void SetDestinationEndpoint(string nameOfEndpoint)
 		{
 				OperationContext .Current.OutgoingMessageHeaders.Add(MessageHeader .CreateHeader("DestinationEndpoint" , "http://schemas.microsoft.com/dynamics/2008/01/services" , nameOfEndpoint)); 
 		}

 		/// <summary>
 		/// Helper method - adds a SOAP Header defining the source endpoint name and the source endpoint user to use
 		/// </summary>
 		/// <param name="sourceEndpointName">the name of the source endpoint</param>
 		private void SetSourceEndpointAndUser(string sourceEndpointName)
 		{
 			string userName = HttpContext .Current.User.Identity.Name.ToString(); //returns the current user and domian - eg. egdk\\tomph
 			var addressHeader = AddressHeader .CreateAddressHeader("SourceEndpointUser" , "http://schemas.microsoft.com/dynamics/2008/01/services" , userName);
 			var addressBuilder = new EndpointAddressBuilder (
 			new EndpointAddress (new Uri ("urn:" + sourceEndpointName), addressHeader));
 			var endpointAddress = addressBuilder.ToEndpointAddress();
 			OperationContext .Current.OutgoingMessageHeaders.From = endpointAddress;
 		}

 	}

 }

Please remember that You have to use a bindign that support WS-Addressing. You can either use any of the pre defined ws bindings or use the “custom binding”. In my case I have used the custom binding, which is the most easy one to configure if You just want to test this out. Just set the Authentication property to NTLM instead of anonymous – we are not interested in anonymous user in AX.

Please keep in mind, that if Your setup allows it, You are always able to use the default endpoint, which means that all the SOAP header stuff can be left out of the equation.
Thanks to my collegue Michael Cronqvist for assisting me and also great thanks to my former collegue Tue Theilmann Jørgensen for pointing me in the SOAP Headers direction.

These aditional ressources might come in handy:

Posted in AIF, Microsoft Dynamics AX | Tagged: , , , , , , | Comments Off on Digging into AIF

SVC error when deploying AIF service to IIS

Posted by Torben M. Philippsen on November 21, 2011

If you are viewing an IIS error page like this after having published Your AIF service to IIS, the svc extension is probably missing in IIS:

missing_svc_mimetype

missing_svc_mimetype

This is probably caused by WCF not being correctly installed or registered in IIS.
To fix this do the following:

  1. Open a command prompt
  2. run the following command %windir%\Microsoft.net\framework\v3.0\WindowsCommunicationFoundation\servicemodelreg -i

If you are running a 64 bit OS, You will have to decide whether to run the above command from the “framework64” folder og just the “framework” folder. The easy way to determine this, is to find out whether Your application pool for AIF is running in 32 bit mode. If not you can run the command for the 64 bit version  – otherwise You will have to use the 32 bit version.

To find out whether yourt application pool runs in 64 bit mode do the following:

AppPoolMode

AppPoolMode

  1. Go to the application pool section in IIS
  2. right click Your application pool – in this case the “MicrosoftDynamicsAXAif50” pool and select advanced settings
  3. Search the property “Enable 32 bit applications”. A value equal to false means we are running in 64 bit mode and vice versa.

Posted in AIF, Microsoft Dynamics AX | Tagged: , , | 5 Comments »

 
%d bloggers like this: