I wanted to write today about why I utilize .NET projects for certain scenarios when developing X++ solutions for D365FO.
How Is This Possible?
How is it possible that you can have two different project types under one solution? I actually wrote about the changes that were happening to the X++ compiler way back in 2017, the change we are most interested in is that X++ code now compiles and generates a .NET CIL (so no p-code intermediary). This allows us to create both X++ and .NET projects within the same solution because they both compile to the same CIL.
How Do We Create a .NET Project in Our X++ Solution (and get it referenced correctly)?
1) On a current X++ solution, right click -> Add -> New Project
2) Select Class Library (.NET Framework)
3) Enter a Project Name and Location, then select .NET Framework 4.7.2 – the reason for this is that if we want to reference any Microsoft DLLs we need to be sure our solution matches the project type and version that the DLLs are built against (which in the case of Microsoft DLLs is .NET Framework 4.7.2)
4) Next we need to create a reference between our X++ project and our .NET project, right click on the References and ‘Add Reference’
5) Next select your .NET project from the Projects tab
6) Now you will see your .NET project is referenced by your X++ project
So Now I Can Reference Microsoft DLLs from the .NET Class?
Yes!
1) Right click on the References tab on your .NET project -> Add Reference
2) Click on Browse -> Then the Browse button
3) Navigate to the PackagesLocalDirectory folder (on my development onebox this is located at K:\AosService\PackagesLocalDirectory) then look for the bin folder. This folder contains the built DLLs that are included with your D365FO instance.
In the example scenario I came up with below you can see I’ve added the Microsoft.Dynamics.AX.Xpp.Support library to my project and then added a ‘using’ statement for Microsoft.Dynamics.Ax.Xpp. This allows me to use the MetadataSupport class in my project to get a listing of all menu item display names and then perform a LINQ statement to find menu items that contain a search term, I then use the JsonConvert class from Newtonsoft.Json nuget package to serialize the object before returning it.
One thing to note here is that by default X++ projects are built against .NET version 4.6, so we need to update this to 4.7.2. To do this we need to find the .rnrproj file which is the X++ project file, open it in a text editor and change the TargetFrameworkVersion from v4.6 -> v4.7.2:
And How Do I Call This .NET Method From X++?
Since we are referencing our .NET project in our X++ solution we can add a using statement to our X++ class to the FpTestNet project and then reference the .NET class and method within our X++ code. Note here that you have to follow the syntax of: <.NetClass>::<.NetMethod>. When we execute the code you can see the correct menu item displays are returned in a JSON format that we can then utilize within our X++ project or return to a service operation endpoint to an external solution.
Why Would We Do This?
There are a couple different reasons I do this within our Fastpath customizations (and advocate others to do this as well):
- Normally companies have many more .NET developers than X++ developers
- It is just a simple fact that there are more .NET developers available than X++ developers, now I am not saying that X++ developers aren’t needed (they absolutely are) but for the team I work on I am the sole X++ developer and the rest of the team is .NET developers. Therefore it makes sense for me to try and make this solution as easy as possible for them to develop on as well as debug / troubleshoot so I am not the only one who can perform new development / support if it is needed.
- NET intellisense seems to always work, X++ intellisense works when it wants to
- While the X++ intellisense has gotten better over the years, there are still times when I load a solution and no matter what I do intellisense just doesn’t work, I’ve never had that issue in any .Net project I’ve worked on.
- When using .NET you get all of the ‘goodies’ that go along with it (Nuget packages, LINQ statements on collections, natively supported by DevOps source control)
- Want to utilize Newtonsoft to serialize objects to JSON before storing them in the database or returning it to a service operation endpoint? Go for it! Want to run a LINQ statement on a collection to filter your results? Go ahead! Want to be able to actually look at lines of code added/removed from a solution instead of looking at XML tags? You got it! All of these options and more are available in .NET and not X++.
- NET does a much better job with object collections than X++ (especially when you are returning those objects to an external .NET solution).
- Ok, this might be a personal preference and the fact that I am a .NET dev at heart but I would much rather deal with .NET collections than X++ collections. Microsoft has an entire page dedicated to showing the differences between the collection options between the two. Some key differences include:
- For object lists, being able to explicitly set the object type (ex: List<myObject> = new List<myObject>)
- Iterating a list in .NET is a simple ForEach loop, iterating a list in X++ requires you create and maintain a collection iterator
- Can perform LINQ statements against the collection in .NET
- Ok, this might be a personal preference and the fact that I am a .NET dev at heart but I would much rather deal with .NET collections than X++ collections. Microsoft has an entire page dedicated to showing the differences between the collection options between the two. Some key differences include:
Does this work in every scenario?
No, unfortunately not. There are still cases where you have to write X++ code that cannot be done in .NET. Things like writing SQL statements, creating extensions, and obviously anything that has to be done in the Visual Studio D365FO extension (creating objects, modifying object parameters, etc).
So When Do You Recommend to Use .NET?
For me personally, I try and perform actions in .NET whenever possible for the reasons I listed above.
- Dealing with large object collections and are
- Looking to perform business logic on those collection objects
- Have a need for a .NET nuget package to help with your business logic.
Conclusion
Are there ways to get X++ to do a lot of the things I listed above, absolutely. Are they as simple and straightforward as they are within .NET, I would personally say no. While it may not be right for every scenario, using .NET projects in your X++ solutions may help speed up your time to develop, support, and maintain your D365FO solutions.
Hey Alex,
Cool article, I can see that you put a lot of effort into it, sharing your thoughts and including the screenshots. Most articles don’t got that much in depth so thank you for that!
You mentioned that you’d “recommend using .NET when you need a NuGet package to help with business logic,” but in the article, you only use a package that’s already available in X++. Have you ever tried using external `.dll`s, specifically from NuGet?
Would really appreciate any insights!
Best,
Karel
Karel,
Yes, I frequently use external Nuget packages. I have an example of this using the populate Newtonsoft package in this screenshot:
Hey Alex,
Thank you for the quick reply!
I read somewhere that you can see all the packages already available when you run debugging and then go to Debug > Windows > Modules and there Newtonsoft.Json is already there. And so is System.Linq… So Microsoft includes those already.
But I’ve tried installing something that isn’t natively there – by adding it to the references of my C# project from Nuget (as I described here https://community.dynamics.com/forums/thread/details/?threadid=4a106985-2ad3-ef11-8eea-6045bdebfed9) without success…
Karel,
You can absolutely reference external as I have a number of custom Nuget packages that I have created that I reference from an X++ solution. These libraries are not shipped / built with D365FO as they are custom.
The two things you have to ensure is that:
1) All dependent packages are included (if a top level package has any dependency packages those also need to be included in the resulting build)
2) Your DLL packages are copied into the resulting X++ model bin folder
The way I have this scenario set up is that I have a post-build event that ensures all DLLs are copied over:
