Category Archives: Enterprise Portal Development

Unable to render content due to an error. Please contact your system administrator.

Recently I experienced a strange error in an usercontrol. The error would cause the classic “Unable to render content due to an error. Please contact your system administrator.” message on EP and in the eventviewer I saw this:

EV1

EV2

EV3

The strange thjing turned out to be that the code for the usercontrol was an exact match with the one from a reference environment where everything worked just fine.

Trying to narrow things down I ended up finding that it was the AllowPaging property on the grid that was set to false that caused the error. Setting this property to true, would make the control work just fine. However my customer didn’t want paging – and it woked just fine in the reference environment. Giving this thought I concluded that the error was related to data. In my environment the dataset would return 55 records and in the reference environment I would have just 12 records. I started to play around with the PageSize property of the grid and found the the magic limit was 45 records. A limit of 44 in the PageSize property would make the control work just fine, whereas a limit of 45 would make the control crash with the above eventlog entries to follow.

I don’t think that the error is related to 45 records but more likely the size of the entire dataset. In my case the dataset contains information about expences and in that context some image/binary bitmap data. I therefore expect the data to be quite big.

 

Conclusion:

If you begin to experience strange errors like the ones above – maybe it would be a good idea to look into the size of data.

Advertisements

Troubleshooting Enterprise Portal error messages

If you have been working with enterprise portal development, you will probably have gone through the same levels of frustration as I because error messages are not good in adding usable information into these messages. Over time I have collected some sites with suggestions on how to troubleshoot these more or less cryptric messages.

Error: Enterprise Portal Web Parts show "Unable to render content" error
https://blogs.msdn.microsoft.com/emeadaxsupport/2010/09/23/enterprise-portal-web-parts-show-unable-to-render-content-error/

Error: Unable to render content due to an error. Please contact your system administrator.
https://blogs.msdn.microsoft.com/emeadaxsupport/2010/11/17/how-to-troubleshoot-enterprise-portal-error-unable-to-render-content-due-to-an-error-please-contact-your-system-administrator/

Generel troubleshooting: https://msdn.microsoft.com/en-us/library/cc554278.aspx

Record won’t update from EP–it almost seems like the dataset has been disconnected from AX

In a recent customer project I had to fix an error where a certain record seemed to be updated on EP but not in AX.

The scenario:

In an expense module one would have the ability to upload expenses/receipts. For each expence you would be able to add expense lines.
In this case when uploading an expence and returning and trying to add expence lines in an AX bound grid – the grid would update just fine, but when looking in AX, the record wouldn’t have updated.

I spent the first couple of days trying to figure out a way to force EP to update the record in Ax without any luck – and I was really trying everything and everyway like described here.
I almost by accident discovered that having uploaded a new receipt and trying to update the record from EP – it was also not possible to update the record from within AX – ax just hang. This was somekind of breakthrough since I now had an idea of the record being locked. I started to take interest in the database and analyzing locks:

select * from sys.tables where OBJECT_id in
(select distinct rsc_objid from sys.syslockinfo)
   

Navigating my flow on EP, I realized that a lock would be created in the EPSTATESTORE table and this lock wouldn’t disappear by itself as it should. The big question was now when this lock would be created. It took some time narrowing down where the lock would be created and wich of my webparts actually caused it but finally I was certain to have found the right webpart. Analyzing the code gave me absolutely no clue ablut where the lock could be created –everything seemed just fine. Once again I had to take drastic measures and started to tear down the webpart commenting one method at a time trying to identify which method would cause the lock. As You can imagine this was pretty time consuming but I was certain that finding and removing the cause of the lock would solve almost every problem we had.

Finally i found that a call to a static method of the dataset in AX would cause the lock and lloking into that method in AX It was obvious that an inbalance in ttsbegin and tts commit caused the lock. Ttsbegin was places outside an if statement where the ttscommit was placed inside the if statement. 

The great point of this article is that next time when I experience something not working from EP that should be working I will start looking at possible locks a bit sooner as i did facing this problem.

A link to two other sql scripts that I used can be found here

Display image from AX as embedded object in usercontrol

Example of usercontrol markup:

<%@ Control Language=”C#” AutoEventWireup=”true” CodeFile=”DIVendInvoiceImage.ascx.cs” Inherits=”DIVendInvoiceImage” %>

<%@ Register Assembly=”Microsoft.Dynamics.Framework.Portal, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″

    Namespace=”Microsoft.Dynamics.Framework.Portal.UI.WebControls.WebParts” TagPrefix=”dynamics” %>

<%@ Register Assembly=”Microsoft.Dynamics.Framework.Portal, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″

    Namespace=”Microsoft.Dynamics.Framework.Portal.UI.WebControls” TagPrefix=”dynamics” %>

<%@ Register Assembly=”Microsoft.Dynamics.Framework.Data.Ax, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″

    Namespace=”Microsoft.Dynamics.Framework.Data.Ax” TagPrefix=”dynamics” %>

<%@ Register Assembly=”Microsoft.Dynamics.Framework.Metadata.Ax, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″

    Namespace=”Microsoft.Dynamics.Framework.Metadata.Ax” TagPrefix=”dynamics” %>

<%@ Register Assembly=”Microsoft.Dynamics.Framework.BusinessConnector, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″

    Namespace=”Microsoft.Dynamics.Framework.BusinessConnector” TagPrefix=”dynamics” %>

<%@ Register Assembly=”Microsoft.Dynamics.Framework.BusinessConnector.Proxy, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″

    Namespace=”Microsoft.Dynamics.Framework.BusinessConnector.Proxy” TagPrefix=”dynamics” %>   

<%@ Register Assembly=”Microsoft.Dynamics.Framework.Portal, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″

    Namespace=”Microsoft.Dynamics.Framework.Portal.UI.WebControls.Workflow” TagPrefix=”dynamics” %>    

 

 <dynamics:AxDataSource ID=”DS_VendInvoiceImage” runat=”server”

    DataSetName=”DIVendInvoiceImage” ProviderView=”LedgerJournalTrans” />

 

<dynamics:AxMultiSection

    ID=”AxMultiSection1″ runat=”server”>

<dynamics:AxSection ID=”AxSection_Image” runat=”server” Caption=”Faktura”

        Expanded=”True” >

    <%=showImage() %> 

</dynamics:AxSection>

</dynamics:AxMultiSection>

Usercontrol codebehind

using System;

using System.Collections;

using System.Web;

using System.Web.Security;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.WebControls.WebParts;

using System.Web.UI.HtmlControls;

using System.Globalization;

using Microsoft.Dynamics.Framework.Portal;

using Microsoft.Dynamics.Framework.Portal.UI.WebControls;

using Microsoft.Dynamics.Framework.Portal.UI.WebControls.WebParts;

using Microsoft.Dynamics.Framework.Portal.UI.WebControls.Workflow;

using Microsoft.Dynamics.Framework.Portal.UI;

using Microsoft.Dynamics.Framework.Data.Ax;

using Microsoft.Dynamics.Framework.Metadata.Ax;

using Microsoft.Dynamics.Framework.BusinessConnector;

using Proxy = Microsoft.Dynamics.Framework.BusinessConnector.Proxy;

using ApplicationProxy = Microsoft.Dynamics.Portal.Application.Proxy;

using Microsoft.Dynamics.Framework.BusinessConnector.Session;

using Microsoft.Dynamics.Framework.BusinessConnector.Adapter;

using System.Collections.Generic;

using System.Diagnostics;

 

public partial class DIVendInvoiceImage : System.Web.UI.UserControl

{

    string imageHtml;

 

    protected string showImage()

    {

        return imageHtml;    

    }

    protected void Page_Load(object sender, EventArgs e)

    {

        AxBaseWebPart.GetWebpart(this).ExternalContextChanged += new EventHandler<AxExternalContextChangedEventArgs>(ExternalContextChanged);

       

    }

 

    void ExternalContextChanged(object sender, AxExternalContextChangedEventArgs e)

    {

        IAxaptaRecordAdapter currentRecord = AxBaseWebPart.GetWebpart(this).ExternalRecord;

        if (currentRecord != null)

        {

            imageHtml = (string)this.DS_VendInvoiceImage.GetDataSet().DataSetRun.AxaptaObjectAdapter.Call(“showImage”, (Int64)currentRecord.GetField(“RecId”), Convert.ToString(AxBaseWebPart.GetWebpart(this).Width), Convert.ToString(AxBaseWebPart.GetWebpart(this).Height));

 

        }

    }

 

}

 

ShowImage method on dataset (x++ code) – pushing differenet kinds of object tags

//Called from ASP.NET

str showImage(RecId _ledgerJournalTransRecId, str _widthZone = ‘450px’, str _heightZone = ‘600px’)
{
#Web
NAX_ScanImageTable      NAX_ScanImageTable;
str                     ret;
str                     url;
LedgerJournalTrans      ledgerJournalTransLocal = LedgerJournalTrans::findRecId(_ledgerJournalTransRecId, false);
EPWebsiteParameters     EPWebsiteParameters;
;

    _widthZone = ‘470px’; // tec, cts, 2011.01.20, Defect 229,335  width changed from 250px to 350px
_heightZone = ‘450px’;

    ret = ‘<P style=”TEXT-ALIGN: center”>’+’@VIA98’+'</p>’;
NAX_ScanImageTable = NAX_ScanImageTable::find(ledgerJournalTransLocal.naxImageId);
if (NAX_ScanImageTable.ScanImage)
{
select firstOnly EPWebsiteParameters;
url = EPWebsiteParameters.ExternalUrl+’/’+#resourcesRelDir+’/diimageaction.aspx?’;
url +=’table=’+int2str(NAX_ScanImageTable.tableId);
url +=’&field=’+int2str(fieldnum(NAX_ScanImageTable,ScanImage));
url +=’&recid=’+int642str(NAX_ScanImageTable.RecId);
//EG/2015.01.07/EGTOP, 00414358 Integration til Picasso –>
url += ‘&fileType=’+NAX_ScanImageTable.ImageFormat;
//EG/2015.01.07/EGTOP, 00414358 Integration til Picasso <–
//EG/2015.01.07/EGTOP, 00414358 Integration til Picasso –>
//do something to handle pdf file extension
if (NAX_ScanImageTable.ImageFormat == ‘pdf’)
{
ret = strfmt(‘http://%2‘, url, url);
}
else
{
//if not pdf do as usual – handle as tiff
//EG/2015.01.07/EGTOP, 00414358 Integration til Picasso <–
ret = strfmt(‘http://%2‘, url, url);

          //EG/2015.01.07/EGTOP, 00414358 Integration til Picasso –>
}
//EG/2015.01.07/EGTOP, 00414358 Integration til Picasso <–
}
else if(NAX_ScanImageTable.Text)
{
ret = ‘<DIV id=”bsplustxt”>’+NAX_ScanImageTable.Text+'</DIV>’;
}
return ret;
}

diimageCaption.aspx markup and code – setting the content type according to the memorystream content:

<%@ Page Language=”C#” Trace=”false” %>

<%@ Assembly Name=”Microsoft.Dynamics.BusinessConnectorNet, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, Custom=null” %>

<%@ Assembly Name=”Microsoft.Dynamics.Framework.Portal, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, Custom=null” %>

<%@ Assembly Name=”Microsoft.Dynamics.Framework.BusinessConnector, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, Custom=null” %>

<%@ Assembly Name=”Microsoft.Dynamics.Framework.BusinessConnector.Proxy, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, Custom=null” %>

<%@ Import Namespace=”Microsoft.Dynamics.BusinessConnectorNet” %>

<%@ Import Namespace=”Microsoft.Dynamics.Framework.Portal” %>

<%@ Import Namespace=”Microsoft.Dynamics.Framework.BusinessConnector.Proxy” %>

<%@ Import Namespace=”Microsoft.Dynamics.Framework.BusinessConnector.Session” %>

<%@ Import Namespace=”Microsoft.Dynamics.Framework.BusinessConnector.Adapter” %>

 

<script runat=”server”>

    void Page_Load(object sender, EventArgs e)

    {

        string table = this.Request.QueryString[“table”];

        string field = this.Request.QueryString[“field”];

        string recid = this.Request.QueryString[“recid”];

        //EG/2015.01.07/EGTOP, 00414358 Integration til Picasso –>

        string fileType = this.Request.QueryString[“fileType”];

        //EG/2015.01.07/EGTOP, 00414358 Integration til Picasso <–       

        if (recid != null && recid != String.Empty)

        {

            AxSharepointWebSession session = null;

            try

            {

                table = table == null ? String.Empty : table;

                field = field == null ? String.Empty : field;

 

                session = SessionHelper.Instance.GetSharepointSession();

 

                if (session != null)

                {

                    session.InitWeb(HttpContext.Current, Page.IsPostBack);

                    string base64 = (string)session.AxaptaAdapter.CallStaticClassMethod(“Webapplication”, “GetImage”,

                                    int.Parse(table), int.Parse(field), long.Parse(recid));

                    

                    byte[] bytes = Convert.FromBase64String(base64);                   

                    if (bytes != null && bytes.GetLength(0)>0)

                    {

                        //EG/2015.01.07/EGTOP, 00414358 Integration til Picasso –>

                        string contentType;

                        if (fileType.Equals(“pdf”))

                        {

                            contentType = “application/pdf”;

                        }

                        else

                        {

                            //EG/2015.01.07/EGTOP, 00414358 Integration til Picasso <–

                            contentType = “image/tiff”;

                            //EG/2015.01.07/EGTOP, 00414358 Integration til Picasso –>                           

                        }

                        //EG/2015.01.07/EGTOP, 00414358 Integration til Picasso <–

                        this.Response.ContentType = contentType;

                        this.Response.BinaryWrite(bytes);

                    }

                }

            }

            catch (System.Exception E)

            {

                System.Diagnostics.EventLog.WriteEntry(“EP”, string.Format(“Error {0}”, E.Message),System.Diagnostics.EventLogEntryType.Error, 3);

           

            }

            finally

            {

                if (session != null)

                {

                      SessionHelper.Instance.ReleaseSharepointSession(session);

                }

            }

        }

    }   

</script>

Multiselection checkboxes in an AxGridview

For a customer Ihad to fix a very non user friendly behavier in an AxGridview control.

The scenario was that they had an AxGridview control containing attachments in an expence module. For a single Expence you would one or more attachments – those attachments would be shown in the AxGridview when viewin the given expence post. In the gridview you would be able to select and deselect attachments but in the standard behavior the user flow would be something like this:

  1. Mark a row in the gridview – wait for postback to finish
  2. now the row is in edit mode – check the combobox – wait for postback
  3. click the update command button in the row – wait for poatback to finish

That’s a lot of waiting and posting back just to check a combobox. Ofcourse the customer wanted a more smooth solution.

My workaround was to try and create comboboxes that would be checkable but without posting back all the time. Doing this I had to consider how I would read the checked state from the comboboxes and persist the state to Ax/the underlying dataset. Here’s what I ended up with…

The markup

In order to have a combobox that is checkable at all times – not just in editmode, you will have to create a template field
EP_Multiselect_markup
Please notice that the AxGridview doesn’t allow paging and editing. Also you should notice that the combox doesn’t postback.

The first time the page loads checkboxes should be checked according to values from AX. Notice that the checked propety is handled by the Marked method

private Hashtable checkbox_values = new Hashtable();

    /// <summary>

    /// Method used in when each row is databound (See markup)

    /// </summary>

    /// <param name="dataItem"></param>

    /// <param name="container"></param>

    /// <returns></returns>

    protected bool Marked(object dataItem, object container)

    {

        AxGridViewRow gridViewRow = (AxGridViewRow)container;

        //CheckBox checkbox = (CheckBox)gridViewRow.FindControl("markCheckbox");

        DataSetViewRow row = dataItem as DataSetViewRow;

        if (row != null)

        {

            //string hashcode = checkbox.UniqueID;

            string hashcode = gridViewRow.RowIndex.ToString();

            if (this.checkbox_values.ContainsKey(hashcode))

            {

                //return row.Mark((bool)this.checkbox_values[hashcode]);

                return (bool)checkbox_values[hashcode];

            }           

        }

        return false;

    }

You will notice that the checked state depends on whether the current rows combobox has been added to a hashtable. The hashtable contains a boolean value for each rows combobox indicating whether it should be checked or not. The hashtable is populated on every postback like this:

 

   protected void Page_Load(object sender, EventArgs e)

    {

        getChecked(false);

 

 

//EG/2014.10.28/tomph, 00392460 XpenseAttachments –>

    /// <summary>

    /// Method used to fetch all marked checkboxes from the grid.

    /// This approach is used because we use a classic .net combobox with no postback (not an axboundfield)

    /// </summary>

    /// <param name="_saveToDataset">indicates whether this method is triggered from the save button click event or is just a normal postback</param>

    protected void getChecked(bool _saveToDataset)

    {

        foreach (GridViewRow row in AxGridView1.Rows)

        {

            if (row.RowType == DataControlRowType.DataRow)

            {

                CheckBox chk = (CheckBox)row.FindControl("markCheckbox");

                string hashcode = row.RowIndex.ToString();

                    if (chk.Checked)

                    {

                        if (!checkbox_values.ContainsKey(hashcode))

                        {

                            //only add if it does not exist

                            checkbox_values.Add(hashcode, true);

                        }

                        else

                        {

                            //update value if exists

                            checkbox_values[hashcode] = false;

                        }

                        if (_saveToDataset)

                        {      

                               //save the checked state to the datasat

                               DataSetView dsv = this.ImageDS.GetDataSourceView(this.AxGridView1.DataMember).DataSetView;

                               dsv.SetCurrent(row.RowIndex);

                               DataSetViewRow dsvr = dsv.GetCurrent();

                               dsvr.BeginEdit();

                               dsvr.SetFieldValue("selection**", 1);

                               dsvr.EndEdit();

                               this.AxGridView1.UpdateRow(row.RowIndex, false);

                        }

                    }

                    else

                    {

                        if (!checkbox_values.ContainsKey(hashcode))

                        {

                            //only add if it does not exist

                            checkbox_values.Add(hashcode, false);

                        }

                        else

                        {

                            //update value if exists

                            checkbox_values[hashcode] = false;

                        }

                        if (_saveToDataset)

                        {

                            //save the unchecked state to the datasat

                            DataSetView dsv = this.ImageDS.GetDataSourceView(this.AxGridView1.DataMember).DataSetView;

                            dsv.SetCurrent(row.RowIndex);

                            DataSetViewRow dsvr = dsv.GetCurrent();

                            dsvr.BeginEdit();

                            dsvr.SetFieldValue("selection**", 0);

                            dsvr.EndEdit();

                            this.AxGridView1.UpdateRow(row.RowIndex, false);

                        }

                    }               

            }

        }

    }

The only thing we will have to do know is to persist the checked comboboxes to the data, when clicking a “save/update” button. The save button click event calls the same method as above. Only difference is that the boolean value true is passed which again causes each row to be updated.

    protected void UpdateButton_Click(object sender, EventArgs e)

    {

        getChecked(true);

        this.ImageDS.GetDataSet().DataSetRun.AxaptaObjectAdapter.Call("updateSelections");

    }

Handling timeout issues on the EnterPrise Portal

Lately I have been struggling with a timeout issue for a customer on the Enterprise Portal for Dynamics AX 2009.

I ended up doing two things – here are my experiences:

The problem:

If You have been working with EP, You have probably seen something like this on occasion
EP_TimeOut

The solution:

I will not claim to have a bulletproof solution but there are things that You might want to try

  1. In the AX client navigate to administration—>setup—>internet—>Enterprise Portal—>Parameters
    EP_parameters
    You could try to increase the values for timeout and history and see how that plays out for you.
  2. Another approach could be to increase the session timeout for the AX website in IIS. In order to do that you will have to do two things…
    Find the web.config file for your ax site. The path may look something similar to this C:\inetpub\wwwroot\wss\VirtualDirectories\80 – but ofcourse this may vary depending on your installation structure.
    In the configsections (in the top section of the web.config) you will have to add

    <sectionGroup name="Microsoft.Dynamics">
          <section name="Session" type="System.Configuration.SingleTagSectionHandler, System, Version=1.0.5000.0, Culture=neutral,PublicKeyToken=b77a5c561934e089" />
    </sectionGroup>

    EP_webConfig1
    Now scroll down and place the pointer after <system.web/> and before <runtime> and add

    <Microsoft.Dynamics>
        <Session Timeout="120" />
    </Microsoft.Dynamics>

    EP_webConfig2
    The session timeout is set in seconds and according to your needs.

    Good luck…

 

 

Thanks to my collegue Martin Lindhoff Lysgaard for input and assistance.

How to debug x++ code from Enterprise Portal

This article is mainly to support my own memory on how to configure things and being able to debug x++ code when executed from Enterprise portal (EP).

In order to being able to debug x++ executed from EP you need to follow these few steps:

  1. Log on to the server runnning the AOS
  2. Open the Microsoft Dynamics AX Server Configuration utility. To open Start –> Control Panel –> Administrative Tools –> Microsoft Dynamics AX 2009 Server Configuration.
  3. If you are not allowed to alter the existing configuration, create a new one that supports debugging.
    Click Manage then Create configuration. Give a name to the new configuration in the Create Configuration window then click OK

    On the Application Object Server tab, select Enable breakpoints to debug code X++ code running on this server. Click on Apply button.
    ServerConfiguration

  4. Click on the OK button to close the configuration window. You will get a message that indicates AOS is going to be restarted.’>Click on OK button to close the configuration window. You will get a message that indicates AOS is going to be restarted.
  5. Actually the debugger works on the server where IIS is running and Enterprise Portal is hosted. I used a single system and all the things are on the same system. If you are accessing a system remotely using Terminal Services then from the Start menu click Run. Type mstsc /consolein the Open text box and click OK
    This opens a console session in Terminal Services. Console session is required to debug Ax EP
  6. Open the Services window for the system. To open it Start –> Control Panel –> Administrative Tools –> Services.
    Right-click the World Wide Web Publishing Service and click Properties.
    Click the Log On tab
    Select Allow service to interact with desktop as shown below
    ServicesProperties
  7. Open the web.config file located in :\Inetpub\wwwroot\wss\VirtualDirectories\\, here the port number of the Enterprise Portal site. If you forgot the port then there is a way to check it out. Open Ax client then Administration main menu –> Setup –> Internet –> Enterprise Portal –> Web sites, here you can see the port
  8. Locate out the compilation element then set the debug attribute to true.
    Save the changes.
    Reset IIS (shouldn’t be necessary but I always do this just to make sure)
  9. Open the Microsoft Dynamics AX Configuration utility. To open it Start –> Control Panel –> Administrative Tools –> Microsoft Dynamics AX 2009 Configuration
  10. Set the Application Object Server Instance drop-down menu to Business Connector (non-interactive use only).
  11. If you are not allowed to alter the existing configuration, create a new one that supports debugging.
    Click on Manage button then click Create configuration. In the Create Configuration window, name the new configuration then click OK button.
    On the Developer tab, select Enable user breakpoints to debug code in the Business Connector and Enable global breakpoints to debug code running in the Business Connector or client then click on Apply button
  12. Click on OK button to close the configuration window
  13. Open the Microsoft Dynamics AX client
    On the Tools menu, click Options to display the Options window.
    On the Development tab, select When Breakpoint from the Debug mode list box, and then click Apply. This enables debugging mode on the client
    OptionsMenu
  14. Close the options window.
  15. Set the breakpoint of your choice in the x++ code
  16. Open the Debugger window manually. To open debugger manually click on Tools > Development Tools > Debugger
  17. Now execute the web form where the method has been invoked. It should work now and You should hit you breakpoint – nice:-)