Monday, June 28, 2010

Retrieving Record Values in an Enterprise Portal Wizard/Tunnel

    
Let’s say you have a scenario in which you need to pull a field from a newly created record that was inserted into AX via an Enterprise Portal Wizard/Tunnel. This need could be useful in the event that after a record is created, you need to obtain the record’s Id, so that you can do something else which relates to the new record. In this example we will extend the code example found in the book “Microsoft Dynamics AX 2009 Programming: Getting Started” (page 304)

protected void WizardTunnel_FinishButtonClick(object sender,
WizardNavigationEventArgs e)
{
AxDataSourceView rentalTableView =
this.RentalDataSet.GetDataSourceView("RentalTable");


if (rentalTableView != null)
rentalTableView.EndEdit();


AxUrlMenuItem carRentalsMenuItem =
new AxUrlMenuItem("CarRentalList");
Response.Redirect(carRentalsMenuItem.Url.ToString());
}
To pull the value from the CarRentalId in the following modified event handler…

protected void WizardTunnel_FinishButtonClick(object sender,
WizardNavigationEventArgs e)
{
AxDataSourceView rentalTableView =
this.RentalDataSet.GetDataSourceView("RentalTable");


if (rentalTableView != null)
{


DataSetViewRow dvr = rentalTableView.DataSetView.GetCurrent();
string CarRentalId = dvr.GetFieldValue("CarRentalId").ToString();


rentalTableView.EndEdit();
}


AxUrlMenuItem carRentalsMenuItem =
new AxUrlMenuItem("CarRentalList");
Response.Redirect(carRentalsMenuItem.Url.ToString());
}
    

Thursday, June 24, 2010

Resolving one cause of the "Invalid datasource name" in Enterprise Portal

  
If you have developed a Web Control for Enterprise Portal, which contains an AxDataSource, and an AxForm, and then you attempt to include an AxLookup control you may receive the following error:

Invalid datasource name ‘[Your Data Source Name Here]’
However, you have verified that the data source name is most definitely correct and is indeed part of your Data Set. The problem may be that you have put your AxLookup inside the bounds of the AxForm control. Place your AxLookup control outside of the AxForm, and your problem will probably go away (unless of course it is being caused by another issue)
  

Wednesday, June 23, 2010

Importing AX Projects (.XPO) & Deploying Web Controls into Enterprise Portal: Resolution for Controls not showing in the “Managed content item” drop down.

       
Recently I seriously struggled for some time in exporting Web Controls in the AOT in my development environment, and importing them into the staging environment. I created a project to include all of my web controls, by adding the related web control nodes from [AOT > Web > Web Files > Web Controls]. I then imported this project into staging, and went into Enterprise Portal to setup my Web Part Page with my custom web controls in the AOT. The problem however was that when modifying my shared web part (a Dynamics User Control Web Part), the “Managed content item” drop down did not contain my custom web controls. Oddly enough, I could create a new project in Visual Studio, and select the “Add user control from AOT” option, and my custom controls were in the list. So what exactly was going on??

A veteran Axapta developer, whom I have the privilege of occasionally tapping for information if all else fails, revealed to me that Enterprise Portal does not pull its list of Web Controls from the [AOT > Web > Web Files > Web Controls] node, like Visual Studio does, but rather, in order to see your controls in the “Managed content item” drop down, you must include the references to the controls that are found in the [AOT > Web > Web Content > Managed] node. So when I added these references to my project in the development environment, exported the project, and then imported the project into staging, and then restarting both the AOS and IIS for good measure… My custom web controls finally showed up and could be used.
       

Resolving the “System.NullReferenceException: Object reference not set to an instance of an object” on a standard Enterprise Portal page

     
Recently we were running stress tests on various interfaces that came standard in Enterprise Portal. In particular, we were testing out the Purchase > Purchase requisitions interface. Everything worked until we clicked the New > New purchase requisition button. After that, things blew up and we got a really nasty error that began with the following:

An unexpected error has occurred.


Microsoft.Dynamics.Framework.BusinessConnector.Session.Exceptions.FatalSessionException: Dynamics Object Adapter Call failed. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.Dynamics.Framework.Data.Ax.DataSetViewRow.GetFieldValue(DataSetViewFieldMetadata field)
at System.Web.UI.WebControls.BoundField.GetValue(Control controlContainer)
at Microsoft.Dynamics.Framework.Portal.UI.WebControls.AxBoundField.OnDataBindField(Object sender, EventArgs e)
at System.Web.UI.Control.OnDataBinding(EventArgs e)
at System.Web.UI.Control.DataBind(Boolean raiseOnDataBinding)
at System.Web.UI.Control.DataBindChildren()

Long story short, I eventually found the solution thanks to a post made by someone else who was having a similar issue, and double-thanks to the same person who took the time to come back and post the solution when he/she found it. This post can be seen here.

The problem was that I had recently added a new Dimension to the system. There was however one step that I did not know I had to do after adding the dimension and doing a synchronize. The missing final step was to re-compile the “Data Sets” node in the AOT. After doing this, and then restarting IIS on the Enterprise Portal Machine, the error went away, and problem was solved!
  

Thursday, June 17, 2010

What is the “Record write validation failed. DataKey” error?

        
During Enterprise Portal Development, I received this error in a Tunnel/Wizard that I put together to create a new record in a custom table. I originally googled this error when I first received it, but absolutely no results were returned. Not too long after, a little debugging revealed that the problem was that I was not supplying the value for a mandatory (Number Sequence) field.

If I come across other causes for the problem, I will append them to this blog as I find them.

 

Monday, June 14, 2010

Working with Proxies and the Ambiguous “No .NET Business Connector session could be found” or “Cannot access a disposed object” Errors

Recently, I created an X++ class with a series of methods that I wanted to be able to access via a Web Part in Enterprise Portal. Microsoft’s documentation on Proxies provided a good basis for setting this up.

The problem however came in using the following using block to wrap the code calling the X++ method:

Everything worked great, that was until I used the same block in two different events (called during the same page load) to call another X++ method. It wasn’t immediately clear what was going on, because my page started throwing the ambiguous error “No .NET Business Connector session could be found”, which led me to believe that I either broke something in my code, or that Enterprise Portal was acting quirky.
using (IAxaptaAdapter a = this.AxSession.AxaptaAdapter)
{

}


As I would eventually find out, attempting to obtain a new session, for example, by using the helper function (below) more than once during a given page load, is where the trouble resides. The first time you get the session, everything is great; however the second time, the session is returned as null, which effectively causes the problem.
private ISession AxSession
{
get
{
AxBaseWebPart webpart = AxBaseWebPart.GetWebpart(this);
return webpart == null ? null : webpart.Session;
}
}

The simplest way to setup this error-producing scenario is to call this using block two times in a row, using similar code as seen below. Everything works great stepping through the first block, but the page blows up during the second.


protected void Page_Load(object sender, EventArgs e)
{


using (IAxaptaAdapter a = this.AxSession.AxaptaAdapter)
{
MyCustomClass test = new MyCustomClass(a);
string s = test.myCustomFunction();
}


using (IAxaptaAdapter a = this.AxSession.AxaptaAdapter)
{
MyCustomClass test = new MyCustomClass(a);
string s = test.myCustomFunction();
}


}
Even more ugly is the error that is thrown if you attempt to use the using code block on a web control that has an AxDataSource, this error actually reveals the root problem behind the previous error.

[ObjectDisposedException: Cannot access a disposed object.]

The solution to both errors is rather simple. Setup your IAxaptaAdapter as a global variable in your class, and establish the session only one time during your Page_Load event.

public partial class myClass : System.Web.UI.UserControl
{
private IAxaptaAdapter axapta;


private ISession AxSession
{
get
{
AxBaseWebPart webpart = AxBaseWebPart.GetWebpart(this);
return webpart == null ? null : webpart.Session;
}
}


protected void Page_Load(object sender, EventArgs e)
{
axapta = this.AxSession.AxaptaAdapter;

You will then be able to access the session multiple times, for example, you could call your X++ method in the page load event, and later in the Button1_Click event.


protected void Page_Load(object sender, EventArgs e)
{
axapta = this.AxSession.AxaptaAdapter;


MyCustomClass test = new MyCustomClass(axapta);
string s = test.myCustomFunction();


}


protected void Button1_Click(object sender, EventArgs e)
{
Tmt_CallReportClass test = new Tmt_CallReportClass(axapta);
string s = test.myCustomFunction();
}

This will also allow you to have a AxDataSource control on the page, without any issues.

Friday, June 11, 2010

Open a URL in a Browser from X++

Here is an easy way, using CLR, to load a URL in the client’s default browser via X++, from say, a button’s clicked event:


void clicked()
{
System.Diagnostics.Process pr = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo si = new System.Diagnostics.ProcessStartInfo('URL HERE');
pr.set_StartInfo(si);
pr.Start();
}

A Web Service Reference Regeneration Quirk

    
I had modified a function in a .NET Web Service while adding an additional function to it. The Web Service reference had been previously added to the AOT, so I only needed to refresh the reference, in order for my changes (the additional funtion) to be “seen” by the AX development environment.

After publishing my modified Web Service to the web server, I located my reference to it within the AOT, right-clicked it, and selected “Regenerate”. After getting the usual expected slew of errors, that ultimately reveal the need to restart the AOS so that the new assemblies can be loaded, I did just that. However that’s when things got strange. First, when I attempted to test my method in AX which calls the web service, I received the following error:

Type 'System.ServiceModel.Channels.ReceivedFault' in assembly 'System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' is not marked as serializable.
I verified that the function was a set to run on the server (public static server [return type] [method name])

I then recompiled my method, and received a “… does not contain this function” error, pointing to the call to my modified web service function, that I had previously been successfully calling in AX. I also verified that the new function I had created was not being seen by AX either. This was strange, because, I verified the function was indeed part of the service, by accessing it directly via the web browser. After doing several “regenerates” and restarts on the AOS, all to no avail, left me scratching my head. So I decided to try one more thing.

I had been executing the “regenerate” option on the service reference, via my the AX client application on my local machine. So this final time, I remoted into the machine where the AOS was actually running, and loaded up the client from there. I then executed the “regenerate” option again, and restarted the AOS one more time. To my pleasant surprise, my mysteriously missing web service function (and the new function I created) were suddenly visible in AX, and I could then execute my method call without receiving the System.ServiceModel.Channels.ReceivedFault error.

The so the moral of the story seems to be the following:

When you want to refresh your web service reference in AX, do it from the client application running on the same machine as the AOS!