I'm Joris "Interface" de Gruyter. Welcome To My

Code Crib

All Blog Posts - page 6

Page: 6 of 16

Nov 6, 2013 - What You Need To Know About the 15-minute CU7 Compiler

Filed under: #daxmusings #bizapps

If you hadn’t heard yet, CU7 has been released for AX 2012 R2. Lots of things to get excited about (comparing reports anyone?), the thing I’m most excited about is the new compile optimization. We did a lot of work in the past to identify the bottlenecks and speed up the compile by minimizing those bottlenecks. If you’re on AX 2012 RTM, that’s your best bet. However, the main issue still remained: the compiler didn’t use more than 1 core and so didn’t scale with hardware.

So, no more! CU7 includes a new way to compile the X++ code (CIL is still the way it was). First, it eliminates the client altogether, which removes another communication bottleneck by taking the 32 bit client (and the communication with the AOS) entirely out of the picture. The other advantage to this is that the server component is a 64 bit process, allowing access to more memory. Secondly, it will spin up multiple processes to compile pieces of the X++ code, effectively using multiple cores.

There are a few gotchas that you need to be aware of, though.

By moving the compile into a 64-bit process, there are limitations to loading certain dependent client-side (32 bit) components for the compile. These could be ActiveX controls, COM objects or even DLLs and assemblies. To be able to do the compile, the compiler will use reflection on all these objects, effectively allowing it to check everything without having to actually load these components. Obviously to be able to check all these dependencies correctly, the compiler needs access to client-side components it typically does not have installed. To that end the compiler needs the path to a folder that contains all these dlls it may need. Typically pointing it to the folder of an installed AX client is enough. When calling COM objects in X++, you can “chain” the method calls (for example: COM.method1().method2(); ) relying on the fact that the first method call will return an object of a certain type on which you can call the second method. With the server-side compile, this chaining cannot be checked, so code has to be refactored into multiple calls (for example: o = COM.method1(); o.method2(); ). Note that you will run into this trying to compile a model store version prior to CU7 with the new compiler (SysInetHTMLEditor has calls like this that need to be fixed). Finally, because it starts multiple instances of the server process to do the compile, hotswapping needs to be turned OFF.

So, how does it work?

Well, first: check and make sure hotswapping is turned OFF! Next, open an elevated command prompt (“run as administrator”). Next, navigate to the server/bin folder which contains the axbuild.exe program. There are several parameters you need, one optional one controls the number of parallel processes it uses (aka “workers”). By default axbuild will create roughly 1.4 workers per core you have: I have 4 cores in my laptop (2 physical, total 4 virtual), so axbuild will start up 6 worker processes. I found that reducing that to 4 workers on my machine gained me about 10 minutes. So play with this a bit to find the optimal setting. Additionally you need to specify the AOS you are compiling for. This uses the AOS “instance number” on the machine you are on. On my laptop I’m testing this on my primary AOS which is “01” (yes you need the leading zero). Finally, as mentioned earlier, you need to provide the path to a binary folder containing client-side DLLs, so pointing it to the client/bin folder works great. My final command ends up being:

cd \Program Files\Microsoft Dynamics AX\60\Server\<YOUR AOS NAME>\bin axbuild.exe xppcompileall /aos=01 /altbin=”C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin” /workers=4

I’m currently running a laptop with 3GHZ i7 (2 physical cores / 4 virtual) with 16GB memory and an SSD drive, running AOS and SQL on this machine (and SQL constrained to 4GB memory). I consistently hit 15 minutes compile time.

As mentioned earlier, if you are planning to use this on CU6 or earlier, you will have to cleanup some COM calls. You will see these as compile errors in your compile log output, which will be located in C:\Program Files\Microsoft Dynamics AX\60\Server\<YOUR AOS NAME>\Log (you can specify an alternative path in the command line).

For more details on command line arguments and architecture, check out these two Microsoft links: AX Tools Blog about CU7 Parallel compiler MSDN Article on AXBuild

  Read more...

Sep 26, 2013 - Linq to AX Example using WPF

Filed under: #daxmusings #bizapps

Today I decided to investigate and blog about a feature I haven’t tried since the beta of AX 2012 R2: the Linq connector. I do a quite a bit of C# work regularly, not related to AX, and Linq (along with WPF) is one of my favorite frameworks. So combining that with AX, seems like a perfect match to me.

For this example, I decided to do something basic. WPF allows easy binding, Linq allows querying and returns an IQueryable that you can bind to. In the past I’ve blogged about using WPF to bind to AIF services in my 10-minute app series. This time however, we’ll call our code from within AX and stay inside the AX client process and connection, which is where the Linq connector is working. You could use this over the business connector as well, just keep in mind that the BC technology is announced to be deprecated in the next release.

Unfortunately, there are several limitations and issues using Linq, and I’ll talk about those here.

So, let’s dive right into it. Open Visual Studio 2010 (with the VS extensions for AX 2012 R2 installed). Again, this ONLY works on R2 and higher. In Visual Studio, create a new class library project, I’ve called my example “AX62Linq”. Once it has opened, add the project to the AOT as shown below.

Once it’s added to the AOT, right-click on the project and select properties. In the properties window, make sure to set the “Deploy to Client” property to “Yes”. If you want to run and debug directly from Visual Studio, set the “Debug Target” to “Client”.

Next, we’ll create a WPF window control which we’ll call from within AX. On the project, right-click and click Add > New Item. Select WPF and create a new User Control (WPF) - don’t take the Windows Forms user control! I named my user control “CustomerSearch”.

Instead of a user control though, we’ll make this a full-on window. In the code CustomerSearch.xaml.cs code (expand the CustomerSearch.xaml in your solution explorer and double-click the CustomerSearch.xaml.cs file), change the inheritance from UserControl to inherit from Window instead.

Original:<pre>public partial class CustomerSearch : UserControl { public CustomerSearch() { InitializeComponent(); } }</pre> New:<pre>public partial class CustomerSearch : Window { public CustomerSearch() { InitializeComponent(); } }</pre>

To support this, you also need to add a reference to System.Xaml.

In the designer of the CustomerSearch.xaml, let’s change the “UserControl” tag to “Window”.

Original:<pre><UserControl x:Class="AX62Linq.CustomerSearch" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <Grid>

&lt;/Grid&gt; &lt;/UserControl&gt;</pre></code> <code>New:<pre>&lt;Window x:Class="AX62Linq.CustomerSearch"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300"&gt;
&lt;Grid&gt;
        
&lt;/Grid&gt; &lt;/Window&gt;</pre></code>

Next, we’ll add the references to the AX Linq libraries. Right-click references again and click “add a reference”, but this time we’ll have to browse to the DLLs we need. These DLLs are in the AX client’s bin directory, which by default on an 64-bit system is in c:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin. Add the files Microsoft.Dynamics.AX.Framework.Linq*.dll On my current test system which is AX 2012 R2 CU6 I have three files, other versions may have different files or different number of files. The reason I say this is because the code example on MSDN seems to show a difference with my system.

Alright, that’s a lot of “blah” for the little tiny bit of code we’re going to write, but here goes. First, we need to instantiate a query provider for AX. then, we create a query collection object for the table we want to query. That table should be a proxy to the table we’re interested in, so first, open the Application Explorer toolbar (from the Visual Studio menu: View > Application Explorer). Expand Data Dictionary / Tables and find CustTable. Right-click on CustTable and select “Add to project”. That create the proxy for you. You’ll need to declare some using statements at the top. Again I have a difference on my system versus the code example from MSDN referenced above. On my 2012 R2 CU6, here’s the using statements I added:

<pre>using Microsoft.Dynamics.AX.Framework.Linq.Data; using Microsoft.Dynamics.AX.Framework.Linq.Data.Common; using Microsoft.Dynamics.AX.Framework.Linq.Data.ManagedInteropLayer; using Microsoft.Dynamics.AX.ManagedInterop;</pre>

That allows us to declare the query provider and query collection:

<pre>QueryProvider provider = new AXQueryProvider(null); QueryCollection<CustTable> custTableCollection = new QueryCollection<CustTable>(provider);</pre>

Next, we can perform our query. If your familiar with Linq, it’s pretty much regular Linq, but there are a few restrictions. But, at least the basics work. For example, here I’m querying for all customers in a given customer group (specified in a string variable named “customerGroup”):

<pre>var customers = from c in custTableCollection where c.CustGroup == customerGroup select c;</pre>

This is where it gets a little bad. There’s an issue when both a method and a field have the same name. For example, the CustTable table we are using has both a field named “Blocked” and a method named “Blocked”. Now, the proxy generator for AX avoids this issue by naming the field “Blocked_” with an underscore. However, the Linq provider seems to not pick up on this correctly. So as soon as you try to use the customers list from the Linq query, you will receive an exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. —> System.ArgumentException: The supplied method arguments are not valid. If you look through the stack, you’ll see it go down to the interop layer and linq libraries, starting with Microsoft.Dynamics.AX.ManagedInterop.Record.createFieldExpressionNode(String fieldName). Luckily, C# features anonymous types, and as handy as they are with Linq in general, they are a must to solve our problem here. So, let’s change our Linq query to not return CustTable types, but rather a new anonymous types containing only the fields we want (and… only fields that don’t have a method with the same name). As for the ugly part: if anyone ever decides to add a method with the same name as one of your fields, I guess you’re done. Below I create a new anonymous type containing the account number and the delivery mode fields).

<pre>var customers = from c in custTableCollection where c.CustGroup == customerGroup select new { c.AccountNum, c.DlvMode };</pre>

An option to this whole mess would be to remove the methods that are causing the conflicts from the generated proxy file. Unfortunately, by default a rebuild of your project will regenerate the proxy code. If you want to try that out anyway, right click on a declaration of “CustTable” in your code (for example in the QueryCollection declaration we have) and click "go to definition".

Anyway, back to the good! Let’s create our XAML window and try this out, shall we? Open the CustomerSearch.xaml file by double clicking on it. In the XAML code, inside our <Grid> tag which is currently empty, we’ll define some rows and columns for layout, but most importantly we’ll then add a TextBox named “CustGroup” and a button to perform the search. Finally, we add a ListView named “CustomerList” with a GridView inside of it. The GridView we bind to “AccountNum” and “DlvMode” which are the two fields we are returning from our Linq query. The final xaml code looks like this (note that if you named your project and XAML control differently, the x:Class declaration at the top in the Window tag should be preserved as you have it!):

<pre><Window x:Class="AX62Linq.CustomerSearch" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" Title="Customer Search" Width="300" Height="300" d:DesignHeight="300" d:DesignWidth="300"> <Grid Margin="5"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions>

    &lt;TextBlock Grid.Row="0" Grid.Column="0" Text="Customer Group Search: " /&gt;
    &lt;TextBox Grid.Row="0" Grid.Column="1" Name="CustGroup" /&gt;
    &lt;Button Grid.Row="0" Grid.Column="2" Content="Search" Name="SearchBtn" Click="SearchBtn_Click" /&gt;

    &lt;ListView Name="CustomerList" Grid.Row="1" Grid.ColumnSpan="3"&gt;
        &lt;ListView.View&gt;
            &lt;GridView&gt;
                &lt;GridViewColumn Header="Account Number" DisplayMemberBinding="{Binding AccountNum}" /&gt;
                &lt;GridViewColumn Header="Delivery Mode" DisplayMemberBinding="{Binding DlvMode}" /&gt;
            &lt;/GridView&gt;
        &lt;/ListView.View&gt;
    &lt;/ListView&gt;
&lt;/Grid&gt; &lt;/Window&gt;</pre></code>

Now, note that the SearchBtn button has a click event handler called SearchBtn_Click, which we need to create. So let’s move on to the code. Open up the CustomerSearch.xaml.cs again. First, we’ll add a method that takes a customer group string, performs the Linq query and sets the result set as the item source for our list view (the control we named “CustomerList”). <pre>public void LoadCustomers(string customerGroup) { QueryProvider provider = new AXQueryProvider(null); QueryCollection<CustTable> custTableCollection = new QueryCollection<CustTable>(provider);

var customers = from c in custTableCollection where c.CustGroup == customerGroup select new { c.AccountNum, c.DlvMode };

CustomerList.ItemsSource = customers; }</pre></code>

Finally, we create another method for the button click, which we have to name the same as what we put in the XAML, “SearchBtn_Click” in the example from above. All we’ll do is grab the text from the “CustGroup” textbox and pass it into our method for Linq:

<pre>private void SearchBtn_Click(object sender, RoutedEventArgs e) { LoadCustomers(CustGroup.Text); }</pre>

At this point, we’re all done. You can do Build > Rebuild Solution and Build > Deploy Solution and we can go into AX to call our code. For the purpose of speeding this up, I just created a quick and dirty job:

<pre>static void Job1(Args _args) { AX62Linq.CustomerSearch myWindow;

myWindow = new AX62Linq.CustomerSearch();
myWindow.ShowDialog(); // this waits for exit }</pre></code>

If all went well, here’s what you should get. I entered “10” as a filter which should give you some customers in the standard CU6 demo data. If you want to go advanced you can go back to Visual Studio and from your Application Explorer make the job you create your “Startup Object” (and make sure you have debug target “client” set on your project, as explained in the beginning). This will allow you to just hit F5 from within Visual Studio which will start AX and run the code (and allow you to debug easily without needing to manually attach the debugger to the ax client). You can go back to my Developer Resources page and find some of the Visual Studio articles if you want to know more about those features.

Ok, so what have we learned today.

The good: Getting the linq queries to work is pretty easy, just add the reference, a proxy and get started. Basic linq queries work well and perform as expected. Since it’s IQueryable you can use the linq results as datasources for binding etc. Although I didn’t demonstrate that here, joins between tables work just as well. Look at the MSDN code example if you’d like to see that.

The bad: Some linq query syntax is not available. For example, looking for customer account numbers that contain “abc” doesn’t work. In Linq you would filter in the where clause: where c.AccountNum.Contains(“abc”) but that won’t fly for the AX proxies. Normal filters where a field equals a string, or where a number is larger or smaller than another number work just fine.

The ugly: A lot of standard tables have fields and methods with the same names. This causes major issues and you won’t be able to query these fields at all unless you copy/paste the proxy code into your own CS file (and remove the proxy) and remove all the methods that cause the issues. Of course then you’re disabling the benefit of the rebuild refreshing your proxy with new code and fields, so you’re on your own for maintaining the proxy code.

  Read more...

Aug 20, 2013 - XLNT - A Most "Excellent" Framework for X++

Filed under: #daxmusings #bizapps

Although demonstrated and praised quite a few times by Master Obi-Wan Villadsen and his team, the XLNT framework is still a little-known framework that contains a lot of power. So much so that the static code analysis (aka “Customization Analysis”) in the Dynamics Lifecycle Services almost solely based on the XLNT framework.

So what is the XLNT framework exactly? XLNT basically allows us to hook into the X++ compiler to get the parsed code and run analysis and diagnostics. How do you get XLNT? There’s no XLNT download in itself, but the XLNT framework is used for the Code Upgrade Service. You can download the Code Upgrade Service from InformationSource (you need to be a Dynamics Customer or Partner) and browse to the SERVICES page. Or you can just click this link. When you’ve download the package, you can just open the EXEcutable with your favorite compression utility (or you can run the installer) and the only files you really need for this article are:

Microsoft.Dynamics.AX.Framework.Xlnt.XppParser.dll Microsoft.Dynamics.AX.Framework.Xlnt.XppParser.Pass2.dll

Feel free to play with the rest, but it requires models to be installed etc to do the code upgrade service. So for the purpose of what we’re talking here, those two DLLs are all we need.

So, to make both the coding and this article a bit easier, I’m going to stay entirely in Visual Studio. XLNT does need the AX “context” so if we won’t be running this in AX but entirely in .NET from Visual Studio, we’ll use a business connector session to provide the context we need. I love XAML as a UI for test apps, but considering this is a blog and not everyone is familiar with XAML, I will just use a console app so avoid distracting from what we’re trying to do with XLNT. Once you create your project, right-click on the project node in the solution explorer and select properties.

Next, we need to change the Target framework to .NET Framework 4 (default is client profile 4.0).

Finally, the AX assemblies are built against .NET Framework 2.0. If you would try to run it as-is, you’ll get an error saying that “Mixed mode assembly is built against version v2.0.50727 of the runtime and cannot be loaded in the 4.0 runtime without additional configuration information”. The “additional configuration information” has to happen in the app.config of your console app. If you’re making a class library for use inside of the AX process, this shouldn’t be an issue at all. But in a standalone app like this console app, open the app.config and add the attribute useLegacyV2RuntimeActivationPolicy=”true” to the startup node, so your app.config xml file should look like this:

<pre><?xml version="1.0"?> <configuration> <startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> </startup> </configuration> </pre>

Let’s get down to it. I’m working in VS 2010 for this example. This would allow me to add any code to the AOT later if I wanted to use this code from within AX. Create a new C# console application, I’m calling it XLNTConsoleApp. In the solution explorer, right-click references and first add references to the XLNT dlls mentioned before. It doesn’t matter where you have them stored, Visual Studio will copy them into your solution by default.

Additionally, we’ll need to reference the business connector so that we can provide the AX session context needed for XLNT to work. Assuming you have the business connector installed (if not, please do so :-)) you can find the DLLs in your client/bin folder (default is C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin). The files you need are: Microsoft.Dynamics.AX.ManagedInterop.dll Microsoft.Dynamics.BusinessConnectorNet.dll

After that, we’re ready to write some code. First, we’ll load up a connection to AX using the business connector. Alternatively to all of this, you could just create a class library project, add it to the AOT, and then call the code from an X++ job or so. So, to account for both, let’s try and detect if we have an AX session context already (if we’re running from within AX) and if not, we’ll create one (uses BC.Net). In your Program.cs locate the static Main method and add the following code, which looks for a runtime context and checks if you’re logged in, if not it makes a BC connection. Of course, we do some basic exception handling.

<pre>static void Main(string[] args) { Microsoft.Dynamics.BusinessConnectorNet.Axapta ax = null;

try
{
    // Check for a RunTimeContext.Current, which will exist if we're running this
    // from within the AX process
    if (Microsoft.Dynamics.AX.ManagedInterop.RuntimeContext.Current == null
        || Microsoft.Dynamics.AX.ManagedInterop.RuntimeContext.Current.isLoggedOn() == false)
    {
        ax = new Microsoft.Dynamics.BusinessConnectorNet.Axapta();
        // Supply all nulls to use the Client Configuration settings for Business Connector
        ax.Logon(null, null, null, null);
        // alternatively, use the line below to specify a config file
        //ax.Logon(null, null, null, @"C:\path_to_my_config\configfile.axc");
    }
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message); // output the exception message
    Console.ReadLine(); // pause execution before we quit the console app
    return;
}

try
{
    // [CODE WILL GO HERE]
    Program.BasicTest("void test() { if (true) { info('hello, world'); } }");
}
catch(Exception ex)
{
    Console.WriteLine(ex.Message); // output the exception message
}

if (ax != null)
{
    ax.Logoff();
    ax.Dispose();
}

Console.ReadLine(); }</pre></code>

Note the line that says // [CODE WILL GO HERE]. That is where we will be introducing our experiments with XLNT. For now, let’s create one basic method for you to experiment with until the next article :-)

<pre>static void BasicTest(string sourceCode) { ProxyMetadataProvider metaData = new Microsoft.Dynamics.AX.Framework.Xlnt.XppParser.Pass2.ProxyMetadataProvider(); MultipassAdministrator multipassAdmin = new MultipassAdministrator(metaData);

Method method = multipassAdmin.CompileSingleMethod(sourceCode) as Method;

if (method != null)
{
    foreach (Statement statement in method.Statements)
    {
        Console.WriteLine(statement.ToString());
    }
} }</pre></code>

This will create a metadata provider and a “multipass administrator” which basically let’s you compile things. We’ll give it some source code to compile which we pass in as a string. Note that you can point it to AOT objects etc. Finally, we’ll loop over the statements in the method. Note that statements can be nested. The Statement class is a base class for different types of statements, which will all have different properties for sub-statements etc (eg the if statement (“IfStmt” class) has an expression statement and a “consequent” property).

You can explore the statements by adjusting the sourceCode input and putting a breakpoint inside the foreach loop, for example. Of course, you still need to actually call this new method, so in the [CODE WILL GO HERE] section you can put the following:

<pre>Program.BasicTest("void test() { if (true) { info('hello, world'); } }");</pre>

Note that a compile error will not result in an exception but rather it will just return null instead of an actual method instance.

Alright, now you’re ready to do something interesting with XLNT! We’ll explore more in the next article, but if you’re doing some exploratory work of your own, please post in the comments!

  Read more...

Aug 1, 2013 - Auto-Deploying DLLs and Other Resources - Part 2

Filed under: #daxmusings #bizapps

In the first post of this series on deploying resources, I discussed the framework and some of its issues we’ll have to deal with. In this article, we’ll actually write the code to support that article.

Note that I also posted an “intermission” to that blog post based on some reader feedback. The article explains how to use a manually edited (aka hack :-)) Visual Studio project to have AX deploy resources through the AOT VS project framework. It works great, but there’s always the possibility that an undocumented “feature” like that may be killed in an update.

So, back to the file deployer framework. We’ll create a new class called “CodeCribDeploy” and we’ll extend “SysFileDeployment”.

As soon as you save the code, you’ll notice 4 compile errors complaining you need to implement a few of the abstract methods:

  • filename
  • parmClientVersion
  • parmServerVersion
  • destinationPath

You can right-click the class and override each of these. They will still error out on the super() call since that would be calling an abstract method. Just get rid of the super() call for now if (like me) the errors bother you. Let’s start with the method “destinationPath”. This indicates where you will store the files you’re deploying. This requires some consideration. Users may not be local admins on the machine and may not have enough privileges to put the files anywhere. On the other hand, for DLLs you want to make sure they are in a path where AX will look to load assemblies from. As an alternative to client/bin I like to use the same folder that AX uses to deploy AOT VS project artifacts too, which is in the user’s appdata folder as explained in this article. Feel free to change, but for this example that’s where we’ll put it. So ultimately, my destinationPath method looks like this:

<pre>protected FilenameSave destinationPath() { return strFmt(@'%1\%2', j CLRInterop::getAnyTypeForObject(System.Environment::GetEnvironmentVariable('localappdata')), @'Microsoft\Dynamics Ax\VSAssemblies\'); }</pre>

I ask .NET for the System Environment variable “localappdata” and append the folder for the VSAssemblies. Interestingly, the sourcePath() method is not abstract and doesn’t need to be overridden. Unfortunately, although it returns the path to the include folder, it runs the code on the client tier and so it returns the wrong value. So, we’ll need to write a method to grab the server include folder on the server tier, then change the sourcePath method to return that value. Note I’m using the server include folder (default location is C:\Program Files\Microsoft Dynamics AX\60\Server[YOURAOSNAME]\bin\Application\Share\Include) because I think that makes sense, but feel free to change this. So this is what we’re adding to our CodeCribDeploy class:

<pre>protected static server FilenameOpen serverIncludePath() { return xInfo::directory(DirectoryType::Include); }

protected FilenameOpen sourcePath() { return CodeCribDeploy::serverIncludePath(); } </pre></code>

Next, the filename. Since there’s only one filename, this implies you need a class for each file you wish to deploy. I’ve personally just created a base class with all the overrides, and then just inherit from that for each file, just changing the filename method’s return value. So, we’ll just enter the filename. In this case I’ll deploy “MyDLL.dll”.

<pre>public Filename filename() { return 'MyDLL.dll'; }</pre>

The next two methods to override are “parmClientVersion” and “parmServerVersion”. Interestingly these don’t seem to be used much by the framework at all. In fact, the only reference is from the base class SysFileDeployment.getClientVersion() and SysFileDeployment.getServerVersion() who seem to just get the version from their parm method. Interestingly, the framework calls the isClientUpdated() method which by default only checks to see if the file exists on the client side. Not helpful. So, let’s implement these methods to actually return some useful information on the versions, then we’ll fix isClientUpdated to actually use these versions properly. There are different things you can do, including using the .NET framework to get actual assembly version numbers from your DLL, but we’ll go with the cheap version and just check timestamps of the files. Note that we need to run these checks on their respective tiers, ie we need to get the server version by running code on the server tier and the client version by running a check on the client tier. since we’re just check file properties (timestamp), we can use the WinAPIServer class to check stuff on the server. Unfortunately, that class demands the FileIOPermission, which means we have the assert that permission on the server tier prior to the calls to WinAPIServer. Since our class will be running client-side, we’ll have to create a static server method which we can call from the parmServerVersion.

<pre>protected server static anytype ServerFileVersion(str filename) { date serverDate; TimeOfDay serverTime;

new FileIOPermission(filename, 'r').assert();

if (WinAPIServer::fileExists(filename))
{
    serverDate = WinAPIServer::getFileModifiedDate(filename);
    serverTime = WinAPIServer::getFileModifiedTime(filename);
}

return strFmt('%1T%2', serverDate, serverTime); }

public anytype parmServerVersion() { str filename = strFmt(@’%1\%2’, this.sourcePath(), this.filename());

return CodeCribDeploy::ServerFileVersion(filename); }

public anytype parmClientVersion() { str filename = strFmt(@’%1\%2’, this.destinationPath(), this.filename()); date clientDate; TimeOfDay clientTime;

if (WinAPI::fileExists(filename))
{
    clientDate = WinAPI::getFileModifiedDate(filename);
    clientTime = WinAPI::getFileModifiedTime(filename);
}

return strFmt('%1T%2', clientDate, clientTime); }</pre></code>

So now we’ll override the “isClientUpdated” method to actually perform a version check:

<pre>public boolean isClientUpdated() { return this.parmClientVersion() == this.parmServerVersion(); }</pre>

Note that here I’m checking if the client and server versions are equal. So if the server version if older, it will return false here and prompt the client to download the older version. That may or may not be what you want.

We also need to make sure the framework picks up on our file to be “checked”. It unfortunately doesn’t look at subclasses of the base class to determine that automatically. You’re supposed to add your classNum as part of a return value of the filesToDeploy() method. If you’re reading this and wanting to implement this for AX 2009, you need to over-layer this method and add your class. If you’re on 2012, you have a better option: events! Right-click on your CodeCribDeploy class and click New > Pre- or post-event handler. Let’s rename this method to “filesToDeployHandler”. We’ll get the method’s return value, add our class ID to the container, and set the return value back.

<pre>public static void filesToDeployHandler(XppPrePostArgs _args) { container filesToDeploy = _args.getReturnValue();

filesToDeploy += classNum(CodeCribDeploy);

_args.setReturnValue(filesToDeploy); }</pre></code>

Finally, we just drag&drop this new method onto the filesToDeploy method of the SysFileDeployer class. Make sure to give the new subscription a meaningful and unique name (or otherwise you’ll defeat the clean purpose of using events in the first place). Also make sure to set the properties of the event subscription (right-click your new subscription node, select properties) to “Post” event.

Great, all set, right?! Well, there’s one more “fix” we have to perform, as discussed, to make sure our file versions are always checked. To do this, either change the code in the “parmUpToDate” method to always return false, or if you’re on AX 2012, again you can use events. By making parmUpToDate return false we force AX to check the versions, as it should. This can be as easy as adding another pre/post handler as we did before, and changing the return value to false.

<pre>public static void parmUpToDateHandler(XppPrePostArgs _args) { _args.setReturnValue(false); }</pre>

And obviously we need to drag&drop this onto the parmUpToDate method of the SysfileDeployer class, and set the CalledWhen property to Post.

Make sure to save the whole lot. Now, when you open a new AX client, you should get the following dialog:

If you don’t see it, make sure you put your DLL to be deployed in the right folder, for the right AOS. Yeah, that’s what I did.

  Read more...

Jul 31, 2013 - Custom Query Range Functions using SysQueryRangeUtil

Filed under: #daxmusings #bizapps

You’ve probably seen these requests before. Users want to submit some report or other functionality to batch, and the query should always be run for “yesterday”. It’s a typical example where, as a user, it would be handy to be able to use functions in your query range. Well, you can. And in fact, you can make your own, very easily!

Enter the class “SysQueryRangeUtil”. All it contains is a bunch of static public methods that return query range values. For example, there is a method called “day” which accepts an optional integer called “relative days”. So, in our example of needing a range value of “tomorrow”, regardless of when the query is executed, you could use day(-1) as a function. How this works in a range? Just open the advanced query window and enter your function call within parentheses.

Let’s make our own method as an example. Add a new method to the SysQueryRangeUtil class, and enter the following, most interesting code you’ve ever encountered.

<pre>public static str customerTest(int _choice = 1) { AccountNum accountNum;

switch(_choice)
{
    case 1:
        accountNum = '1101';
        break;
    case 2:
        accountNum = '1102';
        break;
}

return accountNum; }</pre></code>

So, this accepts an options parameter for choice. If choice is one (or choice is not specified), the function returns 1101, if 2 it returns 1102. Save this method and open a table browser window on the CustTable table. Type CTRL+G to open the grid filters. In the filter field for the AccountNum field, enter: (customerTest(1)).

So, the string returned from the method is directly put in the range. So, you could do all sort of interesting things with this, of course. Check out some of the methods in the SysQueryRangeUtil as examples.

  Read more...

Jul 19, 2013 - Auto-Deploying DLLs and Other Resources - Intermission

Filed under: #daxmusings #bizapps

I posted part 1 of the auto-deploying DLLs and other resources article last month. Although I will finish the part 2 article as promised, an interesting comment and subsequent email discussion / testing has prompted me to include this “intermission”.

The deployment framework has existed throughout quite a few versions of AX. when AX 2012 was released and we were all drooling over the Visual Studio projects in the AOT, one thing became clear: referenced DLLs within the project are not deployed like the DLLs built from the project. I tried quite a few options in the properties of the references to get the DLLs copied to the output folder etc, but nothing worked. Additionally, deploying other files from your project (images etc) doesn’t work either. But, one attentive reader of this blog, Kyle Wascher, pointed out a way to edit your Visual Studio project file to have it deploy files to the output folder. Interestingly, AX honors these settings as opposed to honoring the regular properties in the VS project file. So, here’s how you do it!

First, let’s create a new project in Visual Studio 2010. I’m choosing the Class Library project type, and I’m naming it “DeploymentProject”.

Once created, right-click the new project and select “Add DeploymentProject to AOT”.

Right-click on your project and select “Properties”. Make sure to select “Deploy to client” (or deploy to server or client or EP or all of them, depending on your scenario). For this test I’ll just set Deploy to client to YES.

Of course, we need a DLL to deploy. I’m going to create a new project/solution but of course that is NOT a requirement, you can pick any DLL you have created anywhere or downloaded from third parties. Shift-click on Visual Studio in your Windows taskbar to start another instance of Visual Studio. Create new project,again I pick the Class Library project type, and I’m naming it MyDLL. After this, my project looks like this. Again, creating this new project is just an illustration of a DLL we’ll deploy, it’s not needed to make this work. As an illustration for later, MyDLL contains a public class MyClass with a public static method “Message” that returns the string “Hello, world!”. Since the code is irrelevant I’m just putting a screenshot up here. On a side note, it seems if you create another project within the solution where you create the AX VS project, the new project will also be added to the AOT, which of course defeats what we are trying to do here.

Make sure to build this DLL so we can use it.

Ok, so now there are two ways to have the DLL be part of your project. One, you add it as an actual reference. Or two, you just add it to your project as a regular file. In this example, I will add the DLL as a reference in the project. This will allow me to actually also use the DLL in the project itself, which I will use further down as an example. This is also the most common scenario where one needs to deploy an extra DLL. So, go back to your AX VS Project “DeploymentProject”, right click the references node in your deployment project, and click “Add reference”. On the “Add Reference” dialog click the Browse tab and navigate to the MyDLL.dll we built in the other project. You’ll find that DLL file in your project’s folder under bin/debug or bin/release depending on which configuration you used to build.

Ok, open the File menu and select “Save all” to make sure we’ve saved our project. Time to get our hands dirty and “hack” the Visual Studio project :-) Right-click on your project and select “Open folder in windows explorer” (or manually browse to your project folder). Find your .csproj file (in my case it’s DeploymentProject.csproj) and open it in notepad or your favorite text editor (depending on your OS you may or may not have a “Open with” option, you may have to right-click or shift-right-click, it all depends… if all else fails, just open notepad and open the file from within notepad). Find the XML nodes called ItemGroup and add your own ItemGroup as follows:

A few things there. By using $(TargetDir) as the path, we’re telling Visual Studio/AX to find our extra DLL in the folder where this CURRENT project’s DLL is being built. This is important, since it will make sure that wherever the project is compiled, we’ll always find MyDLL.DLL correctly. By default when you add a reference, VS will set the “copy local” flag to yes, which will make sure the referenced DLL is available. Save the csproj file and switch back to Visual Studio. You should get the following dialog:

This is exactly what we need. Click “Reload”. Now, we’ve edited the VS project on disk but it’s not in the AOT yet. Normally, VS updates the AOT any time you click save. Unfortunately, in this case you just reloaded the project from disk so VS doesn’t actually do anything when you hit save, as it doesn’t think you’ve changed anything. So, the easiest thing you can do is just click the “Add DeploymentProject to AOT” option again, as we did in the beginning. This will force an entire update of the project to the AOT. Ok, so now (remember I had “Deploy to Client” set to yes) I will just open the AX client. And as explained in my article on VS Project DLL deployment, you should see both the DeploymentProject.dll and the MyDLL.dll files appearing in your Users\youruser\AppData\Local\Microsoft\Dynamics Ax\VSAssemblies.

Now, as for using the code of MyDLL. Remember we added a class and method there. However, the DLL is referenced in the VS project, but not in the AOT. So, your code inside DeploymentProject.dll can use that class, but your X++ can only use the code from DeploymentProject.dll. If you need access to the MyDLL.dll code from X++, you will need to manually add a reference to that DLL in the AOT still. Now, at that point you point it to the location of the DLL, but at runtime (or after you deploy the code to a test or production environment using models) AX will just try to load the DLL from its known search paths, which will include the VSASSemblies folder in your appdata. So as long as you include the AOT reference as part of your model, this trick will work everywhere.

As a final note, you can use this to deploy ANY file. You can right-click your project and select “add / existing item” and select a JPG file for example. In the properties of the file, make sure to set the “Copy to Output Directory” flag to “Copy always”. Then, just add another VSProjectOutputFiles node in your csproj file.

  Read more...

Jun 29, 2013 - R2 Hotfix for Compile Time needs Schema Update

Filed under: #daxmusings #bizapps

Just a quick note on the hotfix that was released quite a while ago to improve compile times on R2. Many blogs including the official Microsoft one linked directly to the hotfix, and many people have installed it immediately with no result. What many people don’t seem to know (and honestly in my own haste to try it out I did the same thing at first) is that you need to update your model store schema to benefit from the improvements which included new indexes in the model store. So, if you have installed the hotfix (KB2844240), make sure to run “axutil schema” on the model store to actually make the changes take effect!

  Read more...

Jun 20, 2013 - Dynamics AX Admin Tools - CodeCrib.AX.Config

Filed under: #daxmusings #bizapps

Yesterday I released code libraries and a wrapper PowerShell cmdlet libraries to automate installs of AX and maintain client and server configurations for AX. I also blogged an example of an installer script to have an automated install of an AX 2012 AOS. The download links for both libraries are:

CodeCrib.AX.Setup CodeCrib.AX.Config

Today I will give you an example of the Config library and how we’re using it. You can find reference documentation of the commands for the Config library here.

The point of the config library is to create and maintain configuration files. For example, when we auto-deploy an AOS for development, we can run a script that will change the AOS to have debugging and hot-swapping enabled. For the client side, we can generate client configuration files to log into the development workspace and in the correct layer. Both server and client config objects expose all the properties that you see on the configuration utilities. Before anyone comments, the big missing piece here is the “Refresh configuration” that exists on the client configuration utility. I’m working on finding out how to get that configuration easily.

So this one script takes care of both AOS and client configs. The first part of this script gets parameters in for the PowerShell script and loads the library. Next, it gets the active configuration (after initial install this is the “original” configuration). It changes the configuration name to equal the AOS name (I like this as a convention on our VMs), sets breakpoints on server, sets hotswapping, and save the configuration back (by piping the config object into the Save-ServerConfiguration cmdlet). Next, it uses Set-ServerConfiguration to set that new configuration as the active one for our AOS instance. <pre>Param( [parameter(mandatory=$true)][string]$instancename, [parameter(mandatory=$true)][string]$VARcde, [parameter(mandatory=$true)][string]$CUScode, [parameter(mandatory=$true)][string]$configfolder ) import-module ((Get-Location).Path + "\CodeCrib.AX.Config.PowerShell.dll")

$config = Get-ServerConfiguration -aosname $instancename -active $config.Configuration=$instancename $config.BreakpointsOnServer=1 $config.HotSwapping=1 $config | Save-ServerConfiguration -aosname $instancename Set-ServerConfiguration -aosname $instancename -config $config.Configuration</pre></code>

Next, we move on to the client configuration. Just like the server configuration, initially you are stuck with the “original” configuration. We just retrieve that one (it’s the active one), set user and global breakpoints, and save out the config three times (for three layers: USR, VAR, CUS). After that we repeat the process but we add the -Development startup command and create config files for each layer to log into the development workspace. <pre>$config = Get-ClientConfiguration -active $config.UserBreakPoints=1 $config.GlobalBreakPoints=1

$config.Layer=”usr” $config.LayerCode=”” $config | Save-ClientConfiguration -filename ($configfolder + “" + $instancename + “_usr.axc”)

$config.Layer=”var” $config.LayerCode=$VARcode $config | Save-ClientConfiguration -filename ($configfolder + “" + $instancename + “_var.axc”)

$config.Layer=”cus” $config.LayerCode=$CUScode $config | Save-ClientConfiguration -filename ($configfolder + “" + $instancename + “_cus.axc”)

$config.KernelStartupCommand = “-Development”

$config.Layer=”usr” $config.LayerCode=”” $config | Save-ClientConfiguration -filename ($configfolder + “" + $instancename + “_usr_Development.axc”)

$config.Layer=”var” $config.LayerCode=$VARcode $config | Save-ClientConfiguration -filename ($configfolder + “" + $instancename + “_var_Development.axc”)

$config.Layer=”cus” $config.LayerCode=$CUScode $config | Save-ClientConfiguration -filename ($configfolder + “" + $instancename + “_cus_Development.axc”)</pre></code>

We can probably shorten this into a loop of sorts, but this is easy to read and understand at this point.

Bonus round:

You could ask, how about actually creating a shortcut to start AX and pass the config file? I haven’t worked out that code yet (I’ll leave it as “an exercise for the reader” :-) but basically you can use WScript.Shell for that. I haven’t gotten past one issue with this (just haven’t had the time) where the target path validates the path’s existence. If you add the configuration file as a parameter in there, it basically fails to validate that whole string (including config file) as a valid target path. Either way, you can play with this but the following PowerShell script is where I left it last time I considered it: <pre>$shell = New-Object -COM WScript.Shell $shortcut = $shell.CreateShortcut("C:\Users\Public\Desktop\Powershell Link.lnk") #$shortcut.TargetPath=('"c:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin\Ax32.exe" "' + $configfolder + "\" + $instancename + '_cus_Development.axc"') $shortcut.TargetPath="c:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin\Ax32.exe" $shortcut.WorkingDirectory="c:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin\" $shortcut.Description="AX link with config file" $shortcut.Save()</pre> Note how the commented line causes the error. So this will now create a shortcut to AX without the config file. I’ll let you know when I figure this out :-)

For now, that’s it on the admin tools. I’m actively working on this code base, so expect more updates in the next weeks!

  Read more...

Jun 19, 2013 - Dynamics AX Admin Tools - CodeCrib.AX.Setup

Filed under: #daxmusings #bizapps

Long overdue for release, I’m glad to announce the first beta of my admin tools. These tools are still a work in progress, but you can start taking advantage of these right away. As you probably know, we have open sourced our TFS build scripts for Dynamics AX, and ever since these were released I’ve received quite a few emails and messages from people asking how to automate deployment etc outside of TFS. Obviously we do some of that already inside the build scripts, and there’s some code sharing that can be done. Additionally, we’ve been exploring SCVMM (System Center Virtual Machine Manager) for which we would like to automate a lot of things (such as installing AX, deploying code, etc). So, in an effort to refactor and support TFS builds as well as automated scripts or even your own tools (UI?), I embarked on a mission to create a set of admin tools. This first beta release features less than half of the final product, but it’s a good start and it’s what we’ve been using for SCVMM so far (more on that in another post).

So, today’s release includes a code library (which you can use to create your own tools) and a wrapper PowerShell cmdlet library to automate installs of AX and maintain client and server configurations for AX. The downloads are:

CodeCrib.AX.Setup CodeCrib.AX.Config

Today I will give you an example of the Setup library and how we’re using it. You can find reference documentation of the commands for the Setup library here.

Dynamics AX has always allowed silent installs using parameter files which you can pass to the setup executable of AX. For our VMM setup I wanted to make this even more generic and needed some good tools to support parameterized, automated installs. Additionally, a log file generated after an install of AX actually leaves you with most of the parameters you actually used (the exceptions are passwords are not stored in the log file). All of this is captured in the library CodeCrib.AX.Setup and the PowerShell CmdLets CodeCrib.AX.Setup.PowerShell . The download also contains a sample UI which lets you load a log file and write it out as a parameter file, or load a parameter file and manipulate it. Note that the UI is just an example of how to use the class library in your own projects, I’m not planning on maintaining that much but will instead focus on the library and PowerShell cmdlets instead. The following is an example of the PowerShell script we currently have in use for installing an AOS: <pre>Param( [parameter(mandatory=$true)][string]$setuppath, [parameter(mandatory=$true)][string]$databaseserver, [parameter(mandatory=$true)][string]$instancename, [parameter(mandatory=$true)][string]$aosaccount, [parameter(mandatory=$true)][string]$aosaccountpw ) import-module ((Get-Location).Path + "\CodeCrib.AX.Setup.PowerShell.dll")

$env:instance = $instancename

$setupparams = get-parameters -filename ((Get-Location).Path + “\AX2012 Server.txt”) $setupparams | set-parameter -environmentvariables

$setupparams set-parameter -parameter “DbSqlServer” -value $databaseserver
$setupparams set-parameter -parameter “AosAccount” -value $aosaccount
$setupparams set-parameter -parameter “AosAccountPassword” -value $aosaccountpw

$setupparams | start-axsetup -setuppath $setuppath </pre></code>

Basically, the PowerShell script accepts some basic information such as the path to the setup executable, the SQL server name, a name for a new AOS server (and it will reuse that as the name of the database assuming you follow convention and what to keep those the same), account and password to use for the AOS service. Obviously this is abbreviated and it’s specific to just installing an AOS. I will post more examples in future posts. But basically, this loads the PowerShell cmdlets, loads the parameter file (AX2012 Server.txt) and then 1) replaces the %instance% environment variable, and sets the db / aos / password in the parameter object and starts the AX setup.

Tomorrow I will show you an example PowerShell script for the CodeCrib.AX.Config.PowerShell library, to create some standard configuration files to get into layers, development workspace, etc. Enjoy!

  Read more...

May 28, 2013 - Auto-Deploying DLLs and Other Resources - Part 1

Filed under: #daxmusings #bizapps

In my article on .NET Assembly Deployment in AX 2012 we reviewed how assemblies are deployed for Visual Studio projects as well as the CIL generate for X++. However, there are several scenarios one can think of where you want to deploy files outside of that. For example, you are releasing a DLL but don’t want to provide the source, in which case you can’t add the VS Project to the AOT. Other scenarios are files not related to code execution, for example icons or other resource files. In this article we’ll look at a framework in AX that supports doing this, and it has actually existed for multiple versions of AX already: SysFileDeployer.

Let’s start with a scenario. We have a .NET assembly (DLL) we need for use on the client side. We could optionally copy this DLL file into every user’s client/bin folder, but that’s not very convenient. If we need to make an update, we’ll need to update all the clients as well. So, we want to auto-deploy these files to the client. Additionally, the question is WHERE do we put the files on the client side? Putting it in the client/bin would be one option, but there’s a few potential issues. For example, what if the user doesn’t have write privileges to that folder? (it’s in program files after all). For auto-deploying VS projects, AX has created a VSAssemblies folder in each user’s directory, and AX actually looks there to load DLL files. So we can exploit that and put our DLLs there as well. I’ll go with that in this example, but of course you’re free to do what you want. Second decision is, where do we put the files to begin with? The best way in my opinion is the share/include folder on the AOS. Each AOS bin directory has an Application\Share\Include folder which already contains some images and other things to be shared. For example, my default AOS “AX60” has those files in C:\Program Files\Microsoft Dynamics AX\60\Server\AX60\bin\Application\Share\Include . We’ll have the AOS load the files from there, and transfer them to the user’s AppData\Local\Microsoft\Dynamics AX\VSAssemblies folder.

To start off, I’ll create a new X++ project called FileDeployer and add some of the existing AX classes in there. I’ll add classes SysFileDeployment, SysFileDeploymentDLL, SysFileDeploymentFile and SysFileDeployer.

Now, if we debug this framework (for example, put a breakpoint in the MAIN method of the SysFileDeployer class and restart your AX client) we can figure out how this works. Unfortunately, you’ll soon figure out that this framework has an issue right from the start - but of course nothing we can’t fix. Anyway, the SysFileDeploy class a static method called “filesAndVersions” which will get a list of classes (that have to inherit from SysFileDeployment) that will tell this framework which files we wish to deploy. Obviously that will be the first thing we need to customize. Next, it will loop over that list of classes, instantiate each class and call the “getServerVersion” method. The end result is it returns the list of classes with the version on the server side. This method will be called from the “isUpTodate” method on the file deployer class, then it creates an instance of each class again - this time on the client side, sets the server version it got earlier, then calls the “isClientUpdated” method. The idea is that the isClientUpdated method actually checks the version on the client, and compares it with the server version that was retrieved earlier. It all makes sense. Then from the main method in the file deployer it will call the run method on each file deployment class if it determind one file was out of date. So a few issues here. One, if one file needs to be updated, it seems to be downloading all of them. I don’t think that’s a big issue considering these files are typically not large (and if they are, you may need to reconsider how you’re deploying these). The biggest issue though is the check for the parmUpdate() method in that main method. It’s basically checking a stored version from SysLastValue. So any time files are updated, that flag is set to true and stored for next time. Unfortunately, the check for that flag in the main() method is at the beginning of the IF statement, meaning this thing will only run once in its lifetime, to then never run again. Without customizing this framework, the easiest thing I could think of to get around this (in AX 2012 anyway, you’re stuck with customizing in AX 2009) is to add our “isUpdated” logic as handlers to the parmUpToDate method and change the return value if we need to update. If anyone has any better ideas or solutions to this issue, please let me know (put in comments or contact me).

Alright, in the next article we’ll start the code.

  Read more...

 

Page: 6 of 16

Blog Links

Blog Post Collections

Recent Posts