Pages

Wednesday, February 15, 2017

Unlimited private agents for Visual Studio Team Services!

In January Microsoft announced via e-mail good changes to Build & Release Private Agents. They introduced the concept of "Private Pipelines" in Visual Studio Team Services.



What are Agents?
An agent is basically a task-runner that you can pass build activities to, such as PowerShell scripts, executables, etc. The agent can either be Hosted (in the cloud on Azure) or on-premise (installed on your private servers).

When you run a build/release process from VSTS, it sends your tasks to an Agent to do your work.

What's new?
The concept of a "pipeline" is introduced, which is essentially the number of concurrent agents running tasks at the same time.  Previously, you were allowed one on-premise agent (for free) and if you wanted more agents on different servers you had to buy additional ones ($15/ea).

Now, you can have unlimited on-premise agents! You are given one private and hosted pipeline free with your VSTS account. So this means you can still only run operations on one agent at a time unless you buy additional pipelines.

Why is this a good thing?
If you have Dev/Test/Prod systems, you may want to place an agent on each machine where each has certain privileges and setup your build processes to communicate with each agent. And you may have no need for many concurrent build/release operations, but you want to have your agents (task runners) on different machines. This is VERY convenient.  Previously, the work around was to just figure out ways to have your agent run on a single machine and execute operations remotely to the other machines, which you can imagine is a hassle.

Here is the full contents of the email:
Starting later this month and running through the end of February, we will be gradually deploying a change to how we count Build & Release Private Pipelines (previously called Private Agents) in Visual Studio Team Services.  It does not change your bill.

Up to this point, you have needed a Private Pipeline for each separate private build agent or private release agent that you configure in your Team Services account.
Going forward, you can configure as many private agents as needed in your Team Services account without incurring extra charges.  You'll only need as many Private Pipelines as the number of concurrent builds you want to run or releases you want to deploy using your own machines. With this new way of counting Private Pipelines you may be able to purchase fewer Private Pipelines than you do today. 
Learn more about the impact of this change on your builds and releases.
If you have any questions, please contact us through the Visual Studio Developer Community (http://developercommunity.visualstudio.com) or via customer support (https://www.visualstudio.com/team-services/support/). 

Tuesday, November 22, 2016

Dynamics 365 for Operations (AX7) leverages Facebook libraries

So a little known thing about Microsoft Dynamics 365 for Operations (AX7) is that the user interface appears to be built on Facebook's React JavaScript libraries, specifically React with Addons.

Hearing Facebook and D365 in the same sentence initially makes me wince a little, but it makes perfect sense. Facebook is in the business of user interface on the web, so why would Microsoft reinvent the wheel for user interface when the engineers at Facebook are leading the way and devoting more resources towards it?

You can see the React JS files in various locations, such as C:\AOSService\webroot\Scripts\ext\react*.js on a demo VM.



If you've tried to create/debug a D3fo custom control using Chrome/FF developer tools and stepped into the JavaScript, you'll eventually see it in the min versions of the React libraries.

As of writing this, the current version embedded in the Nov 1611 VM is 15.1.0 while the available is v15.3.2.

So if you're experiencing bugs with some controls, there may be a fix on the horizon.

Dynamics 365 for Operations (AX7) leverages Facebook libraries

So a little known think about Microsoft Dynamics 365 for Operations (AX7) is that the user interface appears to be built on Facebook's React JavaScript libraries, specifically React with Addons.

Hearing Facebook and D365 in the same sentence initially makes me wince a little, but it makes perfect sense. Facebook is in the business of user interface on the web, so why would Microsoft reinvent the wheel for user interface when the engineers at Facebook are leading the way and devoting more resources towards it?

You can see the React JS files in various locations, such as C:\AOSService\webroot\Scripts\ext\react*.js on a demo VM.



If you've tried to create/debug a D3fo custom control using Chrome/FF developer tools and stepped into the JavaScript, you'll eventually see it in the min versions of the React libraries.

As of writing this, the current version embedded in the Nov 1611 VM is 15.1.0 while the available is v15.3.2.

So if you're experiencing bugs with some controls, there may be a fix on the horizon.

Wednesday, May 4, 2016

The better way to detect when multiple records are selected

There are some instances when you want to know when multiple records are marked on a form. An example would be if you wanted a button to be enabled only when more than 1 record was selected.

You can make a button disabled when more than 1 record is selected, but not the other way around.

As far as I can tell, the way the grid functions is it stores an Array object of the cursor positions of the marked records. Below are the two ways I've seen to solve this problem. Method #2 is more common, but Method #1 seems more efficient to me as you're only interacting with the Array and not traversing the datasource's table buffer.


    FormDataSource  fds = salesTable.dataSource();

    // Method 1
    // The grid stores an array of marked records, and this
    // will return only the index, which is all that is needed
    if (fds.recordsMarked().lastIndex() > 1)
        info("Multiple records selected");
    else
        info("1 or 0 records selected");
    
    // Method 2
    // This will traverse the datasources buffer
    fds.getFirst(true);    
    if (fds.getNext())
        info("Multiple records selected");
    else
        info("1 or 0 records selected");

    // Enabling/Disabling button in one line
    myButton.enabled(SalesTable_ds.recordsMarked().lastIndex() > 1);

And how to enable/disable a button in one line:

    // Enabling/Disabling button in one line
    myButton.enabled(SalesTable_ds.recordsMarked().lastIndex() > 1);

Thursday, December 3, 2015

Some notes on Dynamics AX 2012 and the registry


  • [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AOS60$01]
    • Windows service configurations
  • HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Dynamics Server
    • AOS Server(s) configurations
  • HKEY_CURRENT_USER\Software\Microsoft\Dynamics\
    • When a user opens the client config, this registry key is created and it stores their configurations which take priority over HKLM
  • HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Dynamics
    • These are the client config settings for the machine that are used when HKCU settings aren't present.

Tuesday, November 10, 2015

CacheDataMethod (AX2012 feature) property vs CacheAddMethod

A someone little known feature of AX 2012 is a new property called "CacheDataMethod".

Previously, when you wanted to improve performance by caching your display/edit methods, you would place a line of code like this after the super() in the datasource's init() method:

this.cacheAddMethod(tableMethodStr(CustTable, MyDisplayMethod));

In AX 2012, you can just change the "CacheDataMethod" property on the object without adding code.  I prefer this method where possible mainly because it keeps everything packaged together and I don't have to intermingle my code with base code.


Monday, November 9, 2015

The better way to pass containers between objects/forms using the Args() class, and not by converting to a string

If you need to pass a container between objects/forms using the Args class, don't convert it to a string and then back, use parmObject() and ContainerClass()!  I see many suggestions about converting it to a string, which can have much more unpredictable results and is not as versatile.

You wrap your container in the class ContainerClass() and then unwrap it at the other end.

Make your call like this:
args.parmObject(new ContainerClass(["Real container", 1234, "Not con2str container"]));
And retrieve it like this:
containerClass = element.args().parmObject() as ContainerClass;
myContainer = containerClass.value();

To test this, create a form (Form1) and overwrite the init method and put in this code:

public void init()
{
    ContainerClass      containerClass;
    container           conValue;
    
    if (!(element.args() && element.args().parmObject() && element.args().parmObject() is ContainerClass))
        throw error("@SYS22539");
    
    super();
    
    containerClass = element.args().parmObject() as ContainerClass;
    conValue = containerClass.value();
    
    info(strFmt("The container contains '%1'", con2Str(conValue)));
}

Then create a Job and put in this code:

static void JobForm1(Args _args)
{
    Args        args;
    FormRun     formRun;
    
    args = new Args();
    args.name(formStr(Form1));
    args.parmObject(new ContainerClass(['Real containers', 1234, 'Not con2str containers']));
    
    formRun = classFactory.formRunClass(args);
    formRun.init();
    formRun.run();
    formRun.wait();
}

And then run the job!