Microsoft Dynamics 365 / Power Platform - page 12
Aug 17, 2011 - AX 2012 Manuals and Training
Filed under: #daxmusings #bizappsBefore continuing on the Business Operation Framework post, I wanted to share that you can now find some official AX 2012 manuals on PartnerSource, if your organization has access to the learning content on PartnerSource.
Aug 17, 2011 - From RunBase to SysOperation : Business Operation Framework
Filed under: #daxmusings #bizappsThere’s been a lot of talk about the Business Operation Framework (or SysOperation), how it is the next iteration and future replacement of the RunBase and RunBaseBatch framework. I say “future replacement” since there is plenty of RunBase to go around in the standard application. But we’d all be fools not to take advantage of this new framework in AX. There is a bit of information to be found about this new framework online already. Rather than just a straightforward walkthrough, I will make the comparison between RunBase and SysOperation and you will see making the transition is not that difficult.
The SysOperation framework is basically an implementation of the MVC pattern. The isolation of the parameters (model), the dialog (view) and the code that runs (controller) is the essence of the pattern. If you think about RunBase and these three elements, you will realize how they are intertwined within the RunBase framework:
Model: • Member variables • Pack/unpack (aka serialization)
View: • Dialog() • GetFromDialog() • PutToDialog()
Controller: • Prompt() • Run()
The framework leverages the services framework for this. A service in AX has a data contract. This is basically an AX class with special attributes. The class specifies accessor methods (“parm” methods in AX-speak), again decorated with attributes, which let you set and get member variables. This specifies the data that can go into your process (aka “operation”), and can also specify a return value for it. The operation itself is just a method that takes a data contract, and returns a data contract. This basically classifies it as a custom service. And indeed, if you use this framework, your operation can in fact be used as a service over AIF. You can see the SOA emerge, a hint of where AX is going. Anyway, distractions aside, we now have a model, we have a process, now we need a controller and a view… Easily enough, the view is automatically created based on your data contract (although you can create your own). The controller is simply an inherited class that doesn’t even need to do much. So, what I decided to do is show you a RunBaseBatch class, and then help you convert the class into a SysOperation class. This class lets you input a title and two integer numbers. The run() method will use the title as an infolog prefix, then output the numbers, and the sum of the two numbers. Exciting stuff indeed. So let’s dissect this class.
The inputs of this class are the title and the two numbers. My ClassDeclaration of this class looks as follows:
Also note the DialogFields in here (the “View”), and the macros #CurrentList and #CurrentVersion which are used in the pack() and unpack() methods, basically the “serialization” of the input data. To convert this into a BOF pattern, we will create a Data Contract with our three arguments.
For each member variable (since in AX they are always private), we will create an accessor method. Again here we need to decorate with attributes.
So, we have now created our input Data Contract. Next, we want to create the operation, which is basically the run() method of the RunBase. Compared to RunBase, the operation will not have the member variables for our inputs, but rather it will take the data contract as an argument. Below shows the original run() method, and the new operation method. We’ll also make the operation class RunOn = Server. Other than that, it’s a regular class with a method, no special inheritance, interface implementation, or anything.
Ok, so we now have the input Data Contract, we have the operation that takes the data contract and runs the functionality. How about the dialog? Well, that will get auto-created by the controller. So, how about that controller? Another class!
Ok, here’s where it gets interesting. Technically, we wouldn’t even need our own controller class. The base controller class which we’re extending here (SysOperationServiceController) has some extra logic in its base method, that reads parameters from the Args argument. This will allow you to just create your own menu item (see MSDN article on BOF usage), point it to the class. The parameters field needs to be set to your class and method name. In our case it would be DAXMusingsService.CoolOperation (notice the period in between the class and method), the enumtype to SysOperationExecutionMode and then pick your enum value, default is Synchronous (more about this later) - see an example here. So why am I telling you all this when we were creating our own class? Well, the SysOperationServiceController’s constructor (the “New” method in AX) takes the service class name, operation method name and the SysOperationExecutionMode enum as arguments. Since we specifically created our controller class to run the specific operation, we can “hardcode” the class and method name (using best practices of course: pre-compiler methods classStr and methodStr). To accomplish this, let’s just create a static construct method. We’ll still leave the option of passing in an execution mode so you can do some testing on your own later.
Ok, so we created a handy static construct method. Are we there yet? Yup, pretty much, all we need is a static Main method to execute our controller class.
One more thing left to do. Since services, including BOF services, run X++ in the CLR, we need to generate CIL. Rather than doing a full compile (which you should have done when you installed AX!), we can just generate incremental CIL.
Alright, ready to rock and roll. Run the class!
Cool, that worked! So, no thanks to the length of this article so far, that wasn’t bad at all, was it?
Of course, the AX-dialog aficionado in you is already complaining about the auto-generated dialog . For one, in this example the two integers with the same data type have the same label. Secondly, RunBase(Batch) classes need to deal with queries, so how does that work?
Well, since this article has turned out longer than expected, I will have to politely ask you to tune in again tomorrow for the next article. (If you can’t wait here’s a tip: more attributes).
Aug 10, 2011 - AX2012 Managed Code - Part 3: Unfortunate Limitations
Filed under: #daxmusings #bizappsPart 1: Get Set Up Part 2: Interop Bliss Part 3: Unfortunate Limitations
After all the glory in the last article, I’ll have to end by showing you one unfortunate feature that does not work. In the previous article, we have created some managed code to which we were able to pass AX objects. Those objects translated perfectly into the proxies we were using in managed code. All this worked seamlessly. We also used a managed handler, accepting the XppPrePostArgs class, also a proxy. The event fired and the XppPrePostArgs instance was passed into our managed handler perfectly.
As you have probably read or at least heard mentioned before, there are pre/post events, and there are delegates (aka “coded events”). The pre/post can be handled in two ways. First, as in the previous example, by creating a handler that accepts the XppPrePostArgs class. The second way to handle pre/post events is by using a “lightweight handler”. The lightweight event handler just needs to match the argument signature of the method it subscribes to, and return void. These handlers are called “lightweight”, because they effectively give you no access to the event sender object, no access to the return value, and depending on the type of arguments (certain arguments are by default passed in as references) no means of changing the arguments to the method in case of a pre-handler. The advantage however, is that you don’t have to access the arguments by means of index or name of variable, but rather get compile-time checking of your matching method arguments. The other type of event, called a delegate, is also named a coded event because it is the original coder’s intent to tell the outside world that something has happened, and allow it to react to that event. A regular convention in the .NET world, which is also recommended by the AX compiler team, is to have two arguments to the delegate: the sender object and an arguments class. The reason you want to pass an arguments class is simple. It allows the original developer of the delegate to extend the arguments being presented to event subscribers, without changing the signature of the delegate. This way existing subscribers don’t break, and newer subscribers can use the extensions on the arguments class. For this purpose, AX now has a class named “XppEventArgs”, which you are encouraged to inherit from to create your own event arguments.
So back to the code. Recall that in Part 1 of this series, you created a delegate on an X++ class that looked like this:
Also recall, that in part 2, you created the following managed event handler in Visual Studio:
coincidentally, they have the same signature. So, as per the definition of a delegate handler, you expect you can hook up this handler to the delegate. Well, unfortunately this is NOT supported. If we try to set this up (right-click the X++ delegate, select “New Event Handler Subscription”) as a managed handler, the method dropdown is empty (it doesn’t find a matching handler in our HandlerClass):
If we try to actually type in the method name of our handler manually (“ComplexHandler”… remember, ANYTHING related to managed code is case sensitive, class names, namespaces, method names, etc), you will get a compile error:
The error message actually reads “The method ComplexHandler in class -snip- has parameters of X++ complex types. It cannot be used as an event handler”. I find this an interesting message, since it seems to imply that it’s the handler’s fault for having the wrong types, whereas it’s really the X++ delegate’s complex type arguments that make it incompatible with any managed handler. When I asked Microsoft about this, the simple answer was “it’s not supported”. Although I love the managed code interop, and I love the eventing story, apparently the two cannot be fully reconciled…
But WAIT! Did we not see in the previous article that we were able to call this ComplexHandler method from X++ code? Well yes, we did see that. And don’t delegates work with X++ code in any shape or form? Well yes, they do. So what if… we create an X++ handler, that calls the managed code method ComplexHandler and just “forwards” the arguments? Let’s add a new static method to our DAXMusings class which we can use for this, let’s call it “proxyHandler”, and let’s call the managed code within it… should work?
If you try to save this… you will get a compile error “Syntax error”. Ok, I did this on purpose of course. This is a minor “glitch” I would say. The semi-colon you no longer need in AX 2012 code? Well, you still need it if your first statement is managed code (basically all of the “rules” of when it was needed in AX 2009 X++, still apply for the managed code statements in AX 2012). So, let’s add the semi-colon and save the method, which will compile at this time.
Next, add the handler to the delegate. You can simply drag&drop the “proxyHandler” method onto your delegate in the AOT. All the properties will be setup correctly that way. DO NOT FORGET to rename the handler. If you want to know why this is important, check out previous articles here and here.
Ok, so this saves and compiles! What if we execute it?
Yup, our managed handler now also gets called from the delegate! So, this is quite interesting. Hooking up the managed handler directly does not work, but using this X++ “proxy” handler, yup, that works… As I stated at the beginning of this series, the AX compiler team are real smart people, so I’m sure there are good reasons for this limitation. However, we have to admit that we are missing this one feature to really call .NET a first class citizen in AX 2012. As for us, are we going to be using managed code in our implementation solutions? Absolutely.
I’m still absolutely thrilled about the managed code interop! Perhaps we can’t call .NET a first class citizen, but we can sure call it a first class green card holder.
Aug 9, 2011 - AX2012 Managed Code - Part 2: Interop Bliss
Filed under: #daxmusings #bizappsPart 1: Get Set Up Part 2: Interop Bliss Part 3: Unfortunate Limitations
In part 1 of this series, we setup some base code in AX, and set up Visual Studio with a project containing some AX proxy classes, ready to deploy and run code from Visual Studio. This article (part 2) builds on top of that code.
Ready for some action? I thought so! First, we’ll create a pre/post event handler and hook it up to our Run() method as a managed pre handler. So, with the code from the previous article in place, let’s add some code in Visual Studio. If you didn’t save a solution you can re-open, you can open the Visual Studio project form the AOT. Open AX, and in the AOT go to Visual Studio Projects > C Sharp Projects and find the DAXMusings project. Right-click the project and select “edit”. This will open the project in Visual Studio for you. So, in the HandlerClass.cs file, we will add a new method to the HandlerClass. Handler methods (whether they are X++ or managed) that you will add in the AOT, have to be static and public of course, so they can be called from the event sender. We will use a “standard” pre/post handler, not a lightweight handler, so the only argument we are expecting is XppPrePostArgs. For more information on XppPrePostArgs and how to use it, also see my models and events series (specifically the second part). Just to see our code working I retrieve the calling object (getThis()) from the XppPrePostArgs class and output the type to the infolog using our Global class proxy. The interesting piece here is that getThis() returns an “object”. We need to get this into our proxy. Remember the proxy is a “wrapper” around the AX object, and that means we can’t CAST the getThis() returned object to our DAXMusingsEvents class, but we need to create a new proxy class and pass it the AX object. It’s important you understand why and how this works, because it can lead to some confusion if you’re not careful.
Let’s build and deploy this to our client so we can add it as a handler in the AOT. Just press F5 in Visual Studio. In AX, open the AOT and find our DAXMusingsEvents class. Right-click on the Run() method and select “New Event Handler Subscription”. Open the properties for the handler. Give it a proper name (this is important, check this post so see why, also check the models and events series), and set the “EventHandlerType” property to “Managed”. For the class name, we need to include the full namespace handle to the class. So the class property will be set to “DAXMusings.ManagedHandlers.HandlerClass”. As soon as you do that, the dropdown for the “Method” property will show you the “compatible” methods on the class. Just select the “PrePostHandler” method and save and compile the class.
If you now run your class, you should see the follow infolog:
If you started AX using F5 in Visual Studio, you can put a breakpoint in your C# method and run the class in AX again, to see how the debugging works. So, this works perfectly. You could re-implement the code from the models and events articles to use a managed handler for the validateField instead of the X++ handler.
Let’s try some regular interop. How about we make a C# method which we will call from X++ code? We’ll create one method here which we can later re-use for other purposes. Close the AX client and go back into Visual Studio. In the HandlerClass, we will add the following method:
Hit F5 to build, deploy and start AX. Find your DAXMusingsEvents class in the AOT, and right-click > New > Method. We’ll call the method “CallManagedCode”. Inside, we will create an instance of an XppPrePostArgs class, set an argument “string”, and call the static C# method we created previously.
Next, we’ll call this method from the existing Run() method. Open up Run() and add the call to “CallManagedCode”. When you now run the class, you should see the PrePost handler fire, but also our call to our method.
Let’s review what this means. The “CallManagedCode” method, passes “this” (DAXMusingsEvents instance) and an instance of the XppPrePostArgs. Our C# managed method, accepts our PROXY classes for DAXMusingsEvents, and our proxy for XppPrePostArgs. As you can see from the output in the infolog, this works perfectly! You get the proxies in your C# code as expected. I encourage you to play around with this. Debug, try to create a new instance of an AX class in C# and return it from a method, etc. Interop is great. These were basically the missing features in previous versions of AX. Instances of CLR objects and/or AX objects crossing the boundaries back and forth.
As cool as this is, we’ll have to end on a sad note in our part 3 of the series, showing you the unfortunate limitations of these new features.
Aug 8, 2011 - AX2012 Managed Code - Part 1: Get Set Up
Filed under: #daxmusings #bizappsPart 1 : Get Set Up Part 2 : Interop Bliss Part 3 : Unfortunate Limitations
One of the new features Microsoft is praising, is .NET languages being a “first class citizen” in the AX development eco system. That sure is a big deal, and compared to previous releases of AX the major barriers for effectively using .NET code in AX have been overcome. I won’t be doing Peter Villadsen’s work justice if I tried to give you an overview of the underlying changes that had to happen to make the interop between X++ and .NET work the way it does today. But suffice it to say, Peter’s team has done a great job, and there is plenty to talk about. However, there is one sore point (in my opinion) in this model: managed code handlers for X++ events do not support complex types!
What? Yes, you heard me right. If events (be it pre/post events or delegate (“coded”) events) have complex argument types to their methods (anything that’s not a base type), they will not allow managed handlers. I’m sure there are good reasons, knowing how savvy the compiler team is. I have not been able to get any explanation for this, but I did get official word that it is indeed not supported. Interestingly, when calling .NET code from X++, you can pass complex types back and forth just fine. On top of that, the Pre/Post handler type using the XppPrePostArgs class as an argument (a complex type…!) does work with managed handlers. But when subscribing a lightweight handler, or subscribing to a delegate, the managed code handlers CANNOT take complex types. If you put two and two together, does that mean if you would create an X++ handler, which on its turn calls the .NET handler “manually”, that does work? Yes, that does work. Strange goings-on, but again, I’m sure there’s some reason.
So, without further delay, I will show you exactly how to create these managed code artifacts, and show you what is and what isn’t supported, and how to “get around it”.
First, let’s create a new class in Dynamics AX 2012 called “DAXMusingsEvents”. We will have a Run method that calls a delegate, we’ll need to create that delegate, and a main method to run the class. To create the delegate, right-click your new class and select New > Delegate, or you can just create a new blank method and type in the method manually as shown below.
I use the default event convention as used in C#. You do not necessarily have to follow this convention, however, there is good reason to do it this way. I have not seen the latest best practice guide for AX 2012 and whether it mentions delegate best practices, but I do know the Microsoft AX compiler team at least recommends using this convention. There is an abstract “XppEventArgs” class which you can inherit from. I’m using the XppPrePostArgs class (which extends XppEventArgs) to avoid creating a new class for this article. Anyway, next we create a run method which calls the delegate
Finally, we need a Main method to run our class, which will instantiate the class and call Run().
This is enough setup in AX. Next, we’ll go into Visual Studio 2010. Create a new project and select the “Class Library” project type. I called my project “DAXMusings.ManagedHandlers”.
When the project is created, in your solution explorer window, right-click the project and select “Add DAXMusings.ManagedHandlers to AOT”.
Next, rename the “Class1.cs” file in your project to “HandlerClass.cs”. Visual Studio will ask you if you want to rename the class itself and any references to it. Click YES.
We will also need access to some AX classes, including the DAXMusingsEvents class we created, the XppPrePostArgs class we are using, and also the “Global” class since that contains the info, error, warning functions we can use to put messages in the infolog. To do this, open the “View” menu and open the “Application Explorer”. This will open an AOT-type view in Visual Studio. Browse to Classes, and find the DAXMusingsEvents, the XppPrePostArgs and the Global class. For each one, either right-click on the class and select “Add To Project”, or drag the class onto your project node. When you have all three, your project should look like the project below.
Almost through the setup. In the application explorer, right-click on the DAXMusingsEvents class and select “Set as startup element”.
On your project in the solution explorer, right-click and select properties. In the properties, set the “Debug Target” to “Client”, and set “Deploy to Client” to “Yes”. Also notice that the property called “Startup Element” is set to your DAXMusingsEvents class due to our previous action.
The “deploy to client” flags that this assembly we’re building will run on clients, and consequently needs to be deployed to clients. If your code needs to run on server, or both server and client, make sure to set these flags correctly. Also remember event subscribers need to run on the same tier as the event itself. The “debug target” will allow you to hit “F5” (or menu Debug / Start Debugging”), which will build the code, deploy it, and run the AX Client and attach Visual Studio to the client you’ve started, allowing you to debug the C# code in Visual Studio when it gets run from AX. You can also just close your client, run build and deploy in Visual Studio, then run AX client manually. If you want to debug then, you can click Debug / Attach to Process from Visual Studio and attach to the AX client, which would give you the same result. The “startup element” will make AX run that element (in this case: our class - note that we didn’t need a menu item) as soon as it started up. This allows for a full build-deploy-run client-run code scenario with the F5 command.
At this point, the code is not doing anything. Feel free to run F5 and test things out. Next article, we will start testing!
Aug 3, 2011 - Follow-up on the Models/Events Articles
Filed under: #daxmusings #bizappsI’d like to do a little follow-up on the models&events article series I’ve posted. It’s nice to get the feedback from you, these long posts take some work but from the feedback I’ve received it seems people found the series very useful so that is awesome to hear. There was also some other feedback, and the main one I’d like to touch on comes from MFP, which I thought I needed to share with you.
Obviously what I’ve dubbed the “Power Combo” is a very powerful concept and I hope people will embrace the new thinking to make the most of this concept. MFP agrees wholeheartedly, but wanted to make sure I mention the following three other concepts that make this powerful combo possible.
-
Fine grained metadata: I actually explain this one in the first post on models, but it’s important to know the power combo would not work without forms, parts and other AOT artifacts(such as VS projects) storing their components more granular than in previous versions of AX. I did get an email from a reader called Thomas who asked how I could state that an object can only exist in 1 model within a layer, but yet I imported my “customization” to the custTable table and form twice in two different models. Well, the granular metadata is exactly the answer to that!
-
Installation specific IDs: Object IDs have always been an issue in AX. In a strict development environment, with code ONLY moving from DEV to TEST to UAT to PROD, object IDs “should” never be an issue. The issue with object IDs is very easy to illustrate. Assume I create a class in DEV, which gets ID #50000. Someone else meanwhile, is actually creating a new class in TEST as well, which of course will also get ID #50000. Other scenario could be we create two classes in DEV, 50000 and 50001. Now we want to move the second one to TEST, but not the first one (let’s say it’s not done yet). If we move the class into TEST without IDs, it will generate a new ID on the fly (which will be 50000). Again, object IDs are out-of-sync. For classes, although it requires manual intervention, this can be resolved. However, for Table IDs, field IDs and anything that has security tied to it, changing IDs means dropping data from the database, security losing connections, etc. There were different attempts in the past to solve this problem. When source control was introduced in AX 4.0, the recommended setup was every developer having his own AOS and AX instance to work in. To remedy the issue there, a “team server” (poor naming, this has nothing to do with Team Foundation Server) was introduced, to keep track of IDs of objects across environments. In AX 2009, the export and import for XPO code was changed so that “export with IDs” was enabled by default. Which actually caused more issues for some clients who were not paying attention. In any case, as MFP explains on his blog, object IDs are no longer a concern in AX. I suggest reading that blog to get more background information. Short of it is, the IDs are still used by the compiler and the runtime, but they are assigned when a model is installed. To uniquely identify objects across environments, they are assigned GUIDs in the background, when and where you create the object. I’m butchering the details here, so I suggest you read the full explanation here. There are some things to keep in mind, for example, storing element IDs in data is a definite no-no, since they are most likely different between environments (making data export/import impossible). Also, Microsoft had to fix BLOB types (containers) to use element names instead of IDs for that very same reason.
-
SQL-based model store: I’m sure you’ve heard by now, the AOD layer files are gone, and all code and metadata is stored in SQL. There may not be much in your daily life as a developer that will make this change apparent, but it has enabled a lot of the goodies we get in AX 2012.
That was it. I have another big blog post coming up, so stay tuned. Please use the “contact me” link and keep sending me your comments and questions!
Aug 1, 2011 - General Availability of AX 2012: It's here!
Filed under: #daxmusings #bizappsTwitter is buzzing like crazy. AX 2012 has been released. I’ve not been able to find partner and customer licenses yet, so I guess they’re still setting up VOICE with the licensing, but partners can already download the product installer image here.
Make sure to check out the public launch page, and the PartnerSource launch page with all sorts of links to documentation, training, etc.
Check out my June blog post with a Documentation roundup and links to what’s available. There are a few new things released since then, which you can find if you search the Microsoft Download Center.
Jul 22, 2011 - AX 2012 RecIds and UnitOfWork Foreign-Key Magic
Filed under: #daxmusings #bizappsAX 2012 features the new UnitOfWork class. One of the reasons this class exists, is to handle new issues that have come up because of one major shift in thinking in AX.
We now should try to link records together based on RecIds.
I can hear you scream WHAT all the way to Denver. Yes, you heard me right, it’s now considered GOOD practice to link records together based on RecIds. Most developers I know that have any kind of experience with AX hate tables that link on RecId (although in some cases you have to), and usually consider it bad practice.
Well, get with the program! :-) It is now considered a good practice. Key reasons are performance and avoiding data duplication. Lots of tables in AX use(d) long strings as primary keys, and in some cases you need to start linking on more than one field to keep linking unique, etc. Well, since RecId is always unique within a table, and it’s “just” a number, the duplication of multiple field values, and the performance on long string primary keys is now “solved”. Yes, I agree, this will not be pretty when doing data migrations and company copies (which for all intents and purposes are near impossible in 2012 - more on that later). But I’m sure somehow we will all get over our preconceptions and embrace this new paradigm (I’m using it, but I’m not a big fan just yet).
In any case, it is what it is. There are some new things around this RecId linking, to facilitate both developer as well as end-user (can you imagine a user having to pick a RecId from a dropdown instead of a Customer number? Before you freak out - customers still have account numbers and that’s still the primary key).
Let’s create a new table called AX2012Features, which uses RecId as its primary key, and a Name field. This name field will be contained in a unique index as well, and be designated as the “Alternate Key” (more on this later). On the table’s properties, set this key as the “ReplacementKey”.
Next, we create a new extended data type for our RecId (yes!). So this data type we’ll call AX2012FeatureRecId, and it has to be an Int64. As it so happens, this data type has a new property called “ReferenceTable”, where we fill in our AX2012Features table.
I’m skipping the creation of a form for this table. Well just add some records using the table browser, we’re amongst developers here, right? So, on to the next table. We’ll add an AX2012FavoriteFeatures table, where we can add an integer field “Ranking” for our favorites features from the other table. Also, we will drag and drop our new AX2012FeatureRecId extended data type on the “Fields” node of the table, which will then release some magic. I will rename this field to “FeatureRecId”.
When you now open this table’s table browser, nothing appears out of the ordinary (besides the fact the table browser in 2012 is now finally no longer an MDI child). There is an integer ranking field, and a FeatureRecId field with a dropdown with recids. Let’s create a form for this, shall we? Now, for the fields, either drag them from the datasource onto the design, OR, when you add a control for the FeatureRecId, select the “ReferenceGroup” control instead and set the referencefield property to “FeatureRecId”. If you drag & drop, it will take referencegroup automatically.
A picture says more than a thousand words, so open up the form, which now has the FeatureRecId field (an Int64) in a ReferenceGroup control.
Yes indeed. The dropdown features your alternate key with your name. If you select a value, the supposed RecId field actually shows the Name, not the RecId. Please try this out with more than one field in the AlternateKey, instead of just one like we did here. Ok, so this is pretty cool actually. We only store one RecId field, but the user sees and picks from a list of values from the alternatekey (however many fields there may be in that key). This begs the question, what do we do in code? Traditionally, I know going in, what my name will be, and so I can add that name when I insert a record in the favorites. Now with this, I cannot get my inserts going, until I have added the favorites, then I need to get that recid and put it in the favorite.
Blast! Just when you started getting used to using RecordInsertList, you need to stop using it because you need the auto-generated value to insert another record. Well, have no fear, the UnitOfWork class is here. And even better, if you have complex structures, it will figure out the sequencing of inserts for you! How you say? Let’s create a class, sporting our two tables and a UnitOfWork class. We create an instance of the UnitOfWork class, and we set the name of our empty feature to “UnitOfWork”. Instead of calling insert, we call unitOfWork’s “InsertOnSaveChanges”, to indicate we want to insert this record when we call “savechanges” on unitofwork. We use a class to test this, and not a job, because UnitOfWork always needs to run on server. So, when you create a main method to run the class, make sure to declare it SERVER!
Next, the magic happens. Go back to your FavoriteFeatures table, and check the relations. The RecId data type prompted you to auto-create the relation. If you open the relation, you’ll see the FeatureRecId field linked to the RecId of the Features table. Open the properties of the relation and set the “CreateNavigationPropertyMethods” to YES.
In the code, we’ll say our number one feature (at least for this blog post)is the one we’re trying to insert. Because of the ForeignKey relationship, and setting the navigation property methods to YES, AX 2012 has added a method to the favorites table, carrying the name of your relationship, in the case of this example, AX2012Features. We pass in the (not yet created!) record buffer of feature. Next, we add the favorites buffer to the unitOfWork as well.
Alright. So now, the unit of work class knows we want to insert both records, and it also knows which one depends on which other one, so it will figure out in what sequence the inserts need to happen (granted, in this example there’s not much to it… but feel free to experiment with more records, or changing up the order of calling “insertonsavechanges”).
Since UnitOfWork also takes care of transactions, we don’t really need to do anything more than call the SaveChanges method (because of this transaction model, if you want to use savechanges to make updates, make sure to select your records for optimistic concurrency!).
So, your full code should now look like this:
So… ready to run it? GO! And yes, if you open the favorites form, you’ll see your unitofwork ranking number 1 in there, properly linked on RecId (but of course sporting the alternatekey “Name” field on the form instead!).
Jul 20, 2011 - AX 2012 Models & Events Part3: The Power Combo!
Filed under: #daxmusings #bizappsIn the conclusion of this 3-part blog article, I will finally get to my point. We will walk through the event code and models and create an example where we have two models that both touch the same table, the same form AND the same method (by means of handlers). In the end, we will import both those and they will install themselves, without any merging necessary. They can be used independently, or together. Hopefully you will understand the needed change in thinking and designing code to make this possible.
Part 1: Model Architecture Part 2: Events Part 3: The Power Combo
So, let’s get down to business. We will create two models (separately, one at a time), each will add a new field to the CustTable table, each will add that field to the customer details screen, and each will add code to the validatefield method on the custtable. We will create one model, do the customization, remove the model, create the other model, do the customization, then import the old model back in. If the promise of models and events holds, and if we create our customizations the right way, we can add and remove these models in whatever sequence we want, as many times as we want, and there will be no merging necessary at all. Just a plain old install, as if you would install an app on your phone.
If you are attempting this walkthrough on the public beta, there are potential issues. Remember that is a pre-release which is 5~6 months older than the version I have created this exercise on. If there are issues with the beta, please let me know in the comments. And be patient and try again when RTM is released (we’re getting close!).
Each layer in AX 2012 has a default model. For VAR, this would be the “VAR Model”, CUS is the “CUS Model”, etc. So first thing we need to do, regardless of what layer you are test this in, is create a new model so we can separate our code. Open AX in the developer workspace (add “-development” in your client configuration or use shortcut CTRL+SHIFT+W in the regular mode to open the developer workspace). From the Tools menu, select Model Management / Create Model.
Enter the information of the model. You don’t have to copy my settings exactly, just make it unique enough, our next model will need a different name (and remember what is what). I decided to name this first model DAXMusings after my blog, and the publisher is myself. Make sure to give it a display name as well, and set it as your current model. As opposed to layers, you can change “active” models on the fly (only within the layer you’re logged into, though).
You will get a nice infolog message indicating your model was created successfully. Other ways to create a model is from the command line using the AXUTIL command-line tool. If the tool is not in your path, it’s located on your server, in the 64-bit program files under “management utilities”. Remember though this is a separate item in the AX 2012 installer. Next up, we’ll create a new project and drag the CustTable table and CustTable form into it. We will also create a new class, which, to keep it easy, I will also call DAXMusings.
Ok, so here’s the story. We will add a field on the custtable to contain a project number (string extended data type “ProjId”). We will validate that field to make sure the project status is “In Process” (granted, this is a silly, useless example, but for the sake of the exercise, let’s pretend it’s business critical!). Traditionally, we would add code to the validateField method on the CustTable. This would be a one-liner calling a method somewhere else, or we would just add all the code in there, doesn’t matter. However, in AX 2012, we can segregate our customization easily by using a pre/post handler. In that case, come the next hotfix rollup or AX release, we need not be concerned about “upgrading” our over-layered method, because it won’t be over-layered at all. And, as we will see here, when we install another model that also needs to hook into the validateField, we can just install it without investigating both code sets and merging. So, let’s get down to it. First, we add our field. To avoid more conflict, we will prefix our customization objects with DM (for DAX Musings). Add the field DMProjId, and set the extended data type to “ProjId”.
We will add this field on the CustTable form, on the general tab. It doesn’t really matter where you put it, the point is we’re customizing the screen by adding it.
Next, we will add a pre/post handler method to attach to the CustTable.validateField method. We will use the standard pre/post handler, since that allows us to change the return value. In case of the validateField, changing the return value makes sense. By default, it will return true, and if one of the validations fail it will set the return value to false. So, if we (and consequent other handlers) don’t touch the return value unless we want to set it to false, it doesn’t matter what order handlers subscribe (let that sink in - I’m making sense, really). I will spare you the details on the code I’m writing. You can just either set the return to false or true to speed this up, I’m still trying to maintain the illusion of a useful mod here.
To hook up this method, drag&drop the method onto the validateField method of the CustTable, or right-click the validateField method and select “New Event Handler Subscription”. Make SURE to rename the subscription to a unique name, and set the “CalledWhen” property to “Post” (if you select pre, the return value will be reset to the default TRUE of the original validatefield method!).
Feel free to open the CustTable form and test the validation. Trust me, it works! :-) Great, so now we have our DAXMusings model. Let’s export this to an AxModel file, and remove it from our system. To do this, open up a command prompt or powershell (I prefer the latter after discovering the joys of powershell). We will use AXUTIL to export, and then remove the model.
The tool will ask you to restart the AOS (think: indexing of objects/layers). After you restart the AOS and fire up your AX client again, you will get a prompt asking you to synchronize and compile. Obviously, you should. In our case, the table needs to drop a field, and the validateField method of the CustTable table needs to be recompiled (remember the pre/post handler is compiled as if it were part of the original method, so a recompile is definitely needed here - weird and bad things happen otherwise).
So, now that we’re back to a virgin AX with foundation, let’s repeat the steps. This time, we’ll sneak in some clandestine advertising for Streamline Systems in our model name and description. As a field, we’ll add an item number (string extended data type “ItemId”) and validate that it does not have an alternate item number (field AltItemId should be blank on the InventTable). Otherwise, we’re pretty much repeating steps here (fast forward the screenshots!).
Add a field Set the Extended Data Type to “ItemId” Create pre/post handler method on the new class Subscribe the new method to the validateField on CustTable Add the new field to the CustTable form
Alright. Now that that’s over, think about what we are about to do. We have an installed model customizing the CustTable, adding a field to a form, and performing a validation in code by adding code, but by using pre/post handlers rather than actually modifying the base code. So, let’s put this new architecture to the test and install our old DAXMusings model back into this same environment.
Now, restart the service, and let’s fire up AX. Again, it will prompt about model changes. Synchronize and compile people (seriously, compiling is an absolute must here)! So now, before you test the mod, what do you think will happen?
YES! Success! (admittedly, I was sweating a little bit… all this typing, imagine if this hadn’t worked).
Open the CustTable form… Both the fields are there!
Try to pick an item with an alternative item (3004 in demo data). Try to put in a project not in process. (if you are getting unexpected results… did you compile?)
Enjoy the power of this new architecture. If you have doubts, feel free to import/export these models in other environments that haven’t been customized at all. You can also try to ACTUALLY customize the validateField method, you will notice it is independent of the handlers attached to it (proving the upgrade point, if standard AX modifies lower level code).
Hope you enjoyed the series. Please leave me a comment, or send me some feedback using the contact form!
Jul 19, 2011 - AX 2012 Models & Events Part2: Events
Filed under: #daxmusings #bizappsIn this 3-part series I will let you in on the power of the 2012 model architecture in combination with events. There is a paradigm shift in regards to customizations and even ISV code that will make life so much easier, provided everyone uses this new model. To that end, I will talk a bit about models, events, and then put it all together and SHOW you the advantage. The originally article kept getting bigger and bigger, so I decided to split it up in three parts, I will publish one every day.
Part 1: Model Architecture Part 2: Events Part 3: Full Walkthrough (with code and screenshots)
AX 2012 introduces the concept of events (whitepaper is available on Microsoft’s Download Center - for a list of all available 2012 documentation see my June Documentation round-up post). For those of you familiar with C# you will realize that delegates are a great concept, but they require the base functionality to have these delegates (for those unfamiliar, delegates are event “hooks” the application exposes). I counted the number of delegates in the RTM release of AX 2012, and there are about 100 (which is not a lot yet, considering the application has around 5 million lines of code). Chances are your customizations will want to subscribe to places, or make modifications to, code that doesn’t provide any delegates. Peter Villadsen and his team thought about this, and introduced a concept of pre and post-handlers. This basically allows you to “subscribe” your code to any method in the application, without having to modify the standard application code to make that call. For the sake of customizations, I will talk about these pre/post handlers, and not delegates. The principles are similar enough. I will have another article on delegates altogether, since there are some interesting details to them. In any case, pre-post handlers come in two flavors: the “lightweight” handlers, and the other handlers that don’t have an official name (yet?). Let’s call them original handlers (one could argue they should use some opposite term of lightweight, but let’s not go there). Let’s start with the original handlers. They are always static methods (since pre-post handlers can only be attached in the AOT, as opposed to delegate handlers that can be subscribed at runtime), obviously public (remember, new methods in AX 2012 will be explicitly declared “private” by default), and no return type (=void), and accept an object of type XppPrePostArgs.
The XppPrePostArgs object is the juice of the meat. This gives you access to the arguments passed into the method you’re subscribing to (using getArg() or args()), the return value being returned from the method you’re subscribing to (getReturnValue()), and it has a method getThis() which returns the instance of the object that fired the event. Of course, you can modify the arguments being passed in, and modify the return value of the original method. But here’s the thing! Technically, it is not guaranteed in what order the event subscribers will be called. This is because it depends on the order in which code is imported, models are installed, etc. So changing the return value (in post-handlers) or arguments (in pre-handlers) is possible, but since you are not guaranteed to be the last person to touch these values, it is not guaranteed that nobody will overwrite your values. Since changing the return value cannot be guaranteed, it becomes a little less useful. On top of that, accessing arguments through the args() or getArg() methods is not the safest thing. You either access the arguments through index or through name (ugh), which you won’t know works until runtime. Because of these limitations and potential issues, lightweight handlers were invented. They are lightweight in the sense that they do not provide a means to change the arguments (but you can read them - and remember certain types are passed by reference so technically you can change them), no means to change the return value, and no means to retrieve the object instance the event was thrown from. Unfortunately, the latter makes the lightweight handlers handicapped to a certain extent. All in all, depending on your situation, you will find that either one will support you in your endeavors. It’s a powerful feature. To finish the quick introduction (without spoiling the next article), to hook up the subscriber, you either drag your subscriber method and drop it on the method you wish to subscribe to. Or you right-click the method you wish to subscribe to and select “New Event Handler Subscription”.
Note that the “subscription” itself (the node in the AOT) carries a name property. Make sure to give this a unique name. Also in the properties, you can denote the subscription to be pre or post, and in case you wish to write the code in managed code, you can, by setting the EventHandlerType property to “Managed”. In that case, make sure, for the class name, to include the full assembly name as well (eg: MyCompany.MyProject.MyClass).
There are two major things you need to know to appreciate this pre/post handler feature even more: 1) The “subscription” (the link between the original method and your subscriber method) has an AOT node, and is stored in the layer/model separate from the original code (so giving it a unique name is of the essence!) 2) The compilation of the original method will “append” the call to your handler to itself - this means no “lookup” overhead at runtime (if you think about this, it will answer any questions you may have around database transactions, exceptions, etc… consider the pre/post handler as if they were calls to your method at the very beginning, or very end, of the method you’re subscribing to).
The key here is number 1 though, the fact that the subscription is stored separate from the original method. This is in fact the number one reason for the power of the events and models combo!
Next: Part 3: Full Walkthrough (with code and screenshots)
Blog Links
Blog Post Collections
- The LLM Blogs
- Dynamics 365 (AX7) Dev Resources
- Dynamics AX 2012 Dev Resources
- Dynamics AX 2012 ALM/TFS
Recent Posts
-
GPT4-o1 Test Results
Read more... -
Small Language Models
Read more... -
Orchestration and Function Calling
Read more... -
From Text Prediction to Action
Read more... -
The Killer App
Read more...