Monday, September 19, 2011

AX 2012 .NET Assembly Deployment

Compared to AX 2009, assemblies in AX 2012 work a bit differently. One of the obvious differences is the introduction of visual studio projects. However, deploying the assemblies works differently, in our favor (of course!). There are a few things we need to keep in mind though, and definitely a few bad habits from AX 2009 to get rid of!

AX 2012 improves assembly deployment and referencing. For the client side of things, this means the assemblies no longer need to be copied to the client/bin folder. The development manuals for AX 2012 INCORRECTLY state that the assembly automatically gets downloaded by the client to the client bin folder. This is incorrect. The assemblies get downloaded to %USERPROFILE%\AppData\Local\Microsoft\Dynamics AX\VSAssemblies . The %USERPROFILE% by default on Windows 7 should be C:\Users\username so if your user name is DAXMUSINGS, the assemblies will be in C:\Users\DAXMUSINGS\AppData\Local\Microsoft\Dynamics AX\VSAssemblies . Make a correction in your 2012 development manual!
Since the assemblies are kept by user, that means in a multi-user environment such as terminal services or Citrix, there is no need for all users to log out (ie all client executables closed so no more assembly files are in use) before a new assembly can be deployed. First, you don't have to deploy, AX does it for you. Secondly, any time a user restarts AX, it will download the new assemblies where and if needed. It also means different users could have different versions of your code in use. This could happen traditionally with X++ as well, so it's always a good idea to schedule downtime to deploy new code, managed or otherwise.

So, when you build a Visual Studio project and click "deploy" in Visual Studio (or, in fact, if you just build the project and then restart your AX client), your AX client will, the next time it needs the assembly, detect a newer version of the assembly and copy it into the VSAssemblies directory. Also note that you can in fact build the project from the AOT. When you right-click/compile on a Visual Studio project, AX builds the project and thus a new assembly.

What happens on the server side? A similar thing, except here the assemblies are deployed to the server bin/VSAssemblies . By default, the AOS runs in 1 AppDomain which means you need to restart the AOS server to get the new assembly. But, in a developer environment you may be recompiling your assembly a lot, so to avoid having to restart your AOS constantly, hotswapping was enabled for the AOS. Check this MSDN page for more information. You should definitely never enable hotswapping in a production environment!

So, for anyone who has dealt with VS projects in previous versions, please don't copy assembly DLLs into your BIN directories. It will deploy automatically, refresh automatically, and load automatically. Also remember you don't have to explicitly add your Visual Studio's project as a reference in the AOT. The fact that it is an AOT VS project makes it available as an implicit reference anywhere in X++ . If you missed it previously, you may want to check out the managed code series for a look on how to create managed code projects.

Then there is the X++ compiled to IL. Those are assemblies too! Well, X++ is kept in the model store now. This includes the X++ source, the X++ p-code, and the X++ compiled IL. These compiled pieces are versioned, and the AOS will download newer versions of the X++ assemblies to it's server bin/XppIL folder, including the PDB files so you can debug that X++ running in the CLR using Visual Studio.

36 comments:

  1. Hi Joris,
    This article was very useful. However I'm facing the following problem:
    I created a class in DAX which uses a .NET class, which in turn is a client library that works with a WCF service. My client library has an application config. From this article I found out that on the client the library is deployed per user and on the server it is in the bin\VSAssemblies folder. So the problem is that when I set the DAX class parameter RunOn : Server, I keep receiving an error that the config file was not found.

    Unable to find the application configuaration file C:\Users\username\AppData\Local\Microsoft\Dynamics Ax\04\VSAssemblies\ClientLibrary.DLL.config. Ensure that the assembly containing the specific service client type has an application configuration source file present.

    From the error message you can see that it looks for the config file in the user profile of the AOS server account and not in the bin\VSAssemblies folder, which doesn't quite make sense because the dll and the config file are there. Did you face such issues?

    P.S. sorry for my english, please let me know if I should elaborate

    ReplyDelete
    Replies
    1. Hi Sergiu,
      Did you find a solution to your problem of loading config files when running code server side?

      Delete
    2. If you use the AifUtil class to instantiate your service client the config file should work.
      I believe the error seems as if it is looking in the wrong folder, but I'm guessing it is looking in several folders to try and find it, and eventually errors out and lists one.

      Aside from using AifUtil, you could also add the configuration data to the Ax32Serv.exe.config file, but that's a lot of manual work (and needs an AOS restart) so I would not advise that.

      I have an example of using AifUtil for consuming a WCF service here:
      http://daxmusings.blogspot.com/2011/10/consuming-external-webservices-in-ax.html

      Delete
    3. I don't understand where the dll's are saved on deploy time. Is it in the database? From where does the client get the dll? How does the client check if there is a new version? I hope you can give me some answers about these questions.

      Delete
    4. Technically the DLLs of CIL compiled code as well as the Visual Studio Projects in the AOT are stored in the database (model store). However, each AOS server will download the latest DLLs (not sure if it's always at startup or if there are rules for downloading when necessary). The DLLs get downloaded into the AOS server bin directory somewhere (should be easy to find, but somewhat irrelevant for this discussion).

      The client will receive the DLLs from the AOS server it connects to. Not sure if the AOS streams it from the database so the client can save the file, or how this works exactly, but that is somewhat irrelevant again for this discussion.

      HOW the client detects that there is a new version is an interesting question, and I don't know the answer. My guess would be that the model store contains some type of a timestamp or build number, which can be checked against the client's files when it starts.

      Client will get new files at startup only. The AOS has that hot-swapping feature which will allow it to load different versions at runtime for every client, without restarting the AOS necessarily.

      Delete
    5. I found out the following interesting information:

      Prerequisites:

      1. AOS-Server has open the DLL
      2. AX-Client is open
      3. The Project has the following settings
      3.1 Deploy Client = Yes
      3.2 Deploy Server = Yes

      Testing:

      1. Deploy the C# Project in Visual Studio

      - it will not update the DLL in %USERPROFILE% (AX Client is open)
      - it will not update the DLL in \bin\VSAssemblies (it is in use)
      - it will update the CONFIG in \bin\VSAssemblies
      - The Record for the DLL and the CONFIG in Table SysModelElementData is updated (createdDateTime is the same, modifiedDateTim has changed)

      2. Exit Visual Studio
      3. Exit AX Client
      4. Delete manually the Files in %USERPROFILE%\AppData\Local\Microsoft\Dynamics AX\VSAssemblies
      5. Start AX Client
      6. Compile C# Visual Studio Project in AOT

      - DLL %USERPROFILE%\AppData\Local\Microsoft\Dynamics AX\VSAssemblies is correctly updated
      - DLL on Server is NOT updated! Config is updated
      - DLL and Config in SysModelElementData is updated

      7. Stop AX Client
      8. Delete manually the Files in %USERPROFILE%\AppData\Local\Microsoft\Dynamics AX\VSAssemblies
      9. Start AX Client
      10. Go to Class where Assembly is used
      11. Open Method where Assembly is used

      - All DLLs (also that ones that are not used in the class method (!) are updated in %USERPROFILE%\AppData\Local\Microsoft\Dynamics AX\VSAssemblies. BUT (!) I now have the file from the Server bin folder which is the old one (!)

      So for that i have to restart the AOS server before i deploy the project and before (!) the assembly is used.

      Delete
    6. Which version of AX2012 are you using?

      Also, do you have "hotswapping" enabled on the AOS? If that is not enabled you cannot get the server to pick up the new DLL without restarting it.

      Delete
    7. Yes, hotswapping is enabled on the AOS.

      Kernel-Version: 6.0.1108.3061
      Application-Version: 6.0.947.61

      AOS Server: Windows Server 2008 R2 Standard SP1
      Development Server: Windows Server 2008 R2 Standard SP1
      Database Server: DBMS_VER 11.00.2100

      Delete
    8. I'd have to look at how/where the files are downloaded with hotswapping. In that case each client connection takes its own appdomain, so multiple DLLs could be loaded at the same time. A new client session should get the newer DLLs, existing connections use their existing. Since there are multiple versions in play at the same time, i'm not entirely sure where they are stored, i'd have to investigate a bit.

      Delete
  2. Hello Joris,
    Thanks for your articles, I learn each time new things.

    I've read your point of view about that point "A little downtime should be possible. I always compare AX code updates to windows updates. You are updating a critical system in your organization".

    But let's consider a world wide compagny with 'very ofen' little critical updates ... and no way to changes this.

    - "Hot-swapping" is not recommended for production environement.
    - It's possible to kill all open sessions.

    Do you think it is possible to flush and load VSassemblies without restarting AOS (if no session open) ? (like a partially restart)

    Thanks

    ReplyDelete
    Replies
    1. No, there is no partial restart. But again, in AX 2012 the downtime should be a few minutes at most (basically, the time it takes to restart the AOS and synchronize the data dictionary). Compile can be done on a side-system and then move the model store.

      There's no good way to end all session; but the easiest way to "kill" sessions is just to stop the AOS.

      Delete
  3. Hi Joris

    Deployment and editing of most .NET projects works nearly perfect. Just don't try to open a project outside Ax, add resources and then deploy the project. The resources will be added, but somehow the auto generated Resource class files fails to deploy :-) (Didn't try to reproduce the error as we are pretty busy upgrading from our 3.0, so maybe that was just a glitch).

    There is a problem regarding usercontrols though. Such things have to reside in the client bin and must be added to references. This makes deployment extremely painfull, because any open client will lock the assembly file, so you can't just replace it on the fly, and in no way can you do it from an Ax client. That is NOT very usefull in an environment where we are not allowed to use the explorer on the installation path. And it is certainly not funny trying to deploy in a Citrix environment where everybody and his sister can have an open client.
    Why, oh Why, couldn't the people that gave us the client VSAssemblies deployment have communicated this great idea to the people in the UI department!?

    We are not amused...

    ReplyDelete
    Replies
    1. Yes, the WPF control thing is annoying. You can use SysFileDeploy* classes for that, but it's hardly perfect (those could use a refactor to use attributes or events, currently you have to customize them).

      I'm unfamiliar with the resources issue. There's one other glitch though. The VsAssemblies on the client is for your CLIENT and not per environment. So if you have multiple environments with different versions of the code and you use the client on the same machine, good luck. In fact, it sometimes tries to update the DLL and you get an error that it is in use :-)

      We can complain, but this is a huge step forward for AX though. Let's just hope Microsoft fixes the glitches and we'll be in heaven. Coming from a 3.0 environment I'm sure you can appreciate! :-)

      Delete
  4. I created a WPF Control in Visual Studio 2010. Build it successfully. Then added it to AOT from VS and set the property 'Deploy To Client' to 'Yes'.
    Then I deployed the WPFControl.

    The DLL was copied successfully in > C:\Users\\AppData\Local\Microsoft\Dynamics Ax\VSAssemblies

    I created a job in Dynamics AX 2012 and I can reference the DLL.

    However, it is not listed in Dynamics AX 2012 in the Managed Control Selector window (when trying to add a Managed Host to a form).

    Am I missing a step? Thanks.

    ReplyDelete
  5. There is a "bug" in the managed control selection screens. Funny you mention this because I am actually actively working with Microsoft on this right now.

    To work around it you need to copy the DLL into the client/bin folder manually. The code will run fine without it, but the selection screen as well as the event hookup dialog don't work properly since they try to read the DLL "manually" and don't check in the vsassemblies folder. Very annoying but I'm hoping to get this fixed in a hotfix.

    ReplyDelete
  6. Hi Joris,

    I can't take this approach as when I go to new environment it generates new DLLs which in-validate all my testing done in previous environment.

    I need a way to copy DLLs directly from one environment (dev) to others (Test, prod). Do you know what happens behind the scene when we deploy a service reference from VS to AOT.

    Regards,
    Deepak

    ReplyDelete
    Replies
    1. From a logical standpoint, having new DLLs doesn't matter. You also recompile the X++ code and generate CIL, so that CIL will generate new DLLs as well that AX will use. I see your point though, but that is just the AX architecture.

      Now, if this is because of auditors or some other very compelling reason to move the DLLs manually, you could use the framework called SysFileDeployer. I've been meaning to blog about this just haven't gotten around to it, so maybe I'll make that a priority. Look at the classes in the AOT, you should be able to figure it out from looking at it.

      Delete
    2. Also, if you move a full model store, it will move all the compiled X++ code and the compiled DLLs for your Visual Studio projects.

      Delete
  7. Hi Joris,

    I have created one VS project and deployed the code on server and client. When I am calling a method in manged code, I get error saying "Assembly containing type XXXX is not referenced. Object 'CLRObject' could not be created Clr Interop Marshal: Unsupported type." when Class property run on is set as 'Server' Same code runs without any error when run on is set as 'Client'. I need to run this code on Server. Any help will be appreciated. Thanks in Advance.

    Regards,
    Vinit

    ReplyDelete
    Replies
    1. Hi Vinit,

      Is your server set to enable hot swapping? Otherwise try to restart the AOS so it picks up the new DLLs.

      Delete
    2. I am having a similar issue, and hot-swapping is enabled and the server has been re-started multiple times. It runs on functions marked as "client", but not on ones that are marked as "server."

      Delete
  8. My VS project references a couple other assemblies that I need to also deploy to the clients. However, when I deploy the project, only the primary output gets deployed. Is there a way to include the other required assemblies in the client deployment?

    Take care.

    --Mark

    ReplyDelete
    Replies
    1. Hi Mark,

      Unfortunately there is no easy way of doing this. Microsoft has (on purpose) restricted what gets deployed. Even config files or other resources don't seem to get deployed from within an AOT VS project (regardless of your setting of the file in the VS project).
      You have to either deploy the files manually, or follow my article on auto-deploying any files, including third party DLLs. I've only posted the first article, I still owe the second one but that should be online this week or next. Either way it's not pretty.

      Delete
  9. Hi Mark,

    we are facing the same issues with deploying referenced assemblies. Did you find a solution yet?

    cheers,
    Marijn

    ReplyDelete
  10. Stay tuned, I will have a blog post on Monday addressing this issue! :-)

    ReplyDelete
  11. Hi,

    Can anyone point me to a tutorial or give me step by step help how to run your own .NET application from within AX 2009. By that I mean that a custom AX Form has a button that launches an application created with C# and then the user does his work and the application saves the data to AX database.

    I have done the following steps while trying to get this to work:
    1. Created .dll from the C# application
    2. Imported the .dll to the References in AX 2009 AOT
    3. Tried to use the class in the .dll in AX 2009, but it does not work. I cannot use my imported assembly in X++

    I have developed external application that uses the BusinessConnector to communicate with AX 2009, but I'd like my custom component to be launched from a button on a Form, inside AX 2009.

    Thanks for the answers beforehand!


    ReplyDelete
    Replies
    1. For AX 2009, besides adding the reference you need to make sure the DLL is available in a path the AX client can find. The easiest place for AX 2009 would be to put it in your client/bin directory. Once it's there, the reference you created should work and you should be able to call your class.

      Delete
  12. Hi there,

    We're using TFS Build server to build and export our .axmodel files to our clients. We also have some C# Visual Studio Projects in the AOT that we want to be part of our .axmodel files. The problem is, sometimes the output .dll and .config files are included in our .axmodel, sometimes not...

    We found out that when the account used to run the build compilation is configured to start ax32.exe with the VAR Model by default, the output of any Visual Studio Projects is generated in that model rather than our model, even if the project itself is in our model. How can you explain that? And what else can be done so the build can generate Visual Studio Projects output in the right model??

    ReplyDelete
    Replies
    1. Two things.

      Yes, it's weird but somewhat makes sense. When you compile a VS project in the AOT, AX will generate the new DLLs etc and put them back in the AOT. Therefor if you run the compile in some model/layer, the new files being generated will be added in the model/layer you're in. It makes sense to some degree, but it is very confusing and even a bit nasty because different people doing the compile may cause the binaries to be in different layer/models/etc.

      However for your specific issue, there is no reason the binaries should be in your .axmodel. The model doesn't contain any compiled code for any X++ code, and binaries for VS projects are just there because they are stored as actual AOT artifacts. But as soon as your clients compile your axmodel, your VS project files will be re-generated anyway, so it's a moot point whether the DLL/config file outputs are in the model or not.

      But if you really want it in there (again, there's no real reason to), you can just make sure the compilation step logs into AX in the right model and layer.....

      Delete
    2. In fact, our clients must import our .axmodel and then compile it, you're right about that one... But, the thing is, our clients compile in the USR layer... When doing so (see for yourself!), even though they might compile C# Visual Studio Projects, the output will NEVER get generated... cause they're in the USR layer. So, we need to export them those precious files.

      Delete
    3. Hmm, that is interesting. I will have to try that indeed!
      Well, i guess you just need to make sure you start your compile in the right layer and model then :-)

      Delete
    4. That's for sure! :) That's what i'm doing as a test tonight on the build server... We'll see tomorrow how things are going! :)

      Delete
  13. Ok it's time for news update! I dont know if you had time to try something on your side. But we had to make some changes to the SysStartupCmd code in order to force the Visual Studio Projects type to generate the output in their own model rather than the active model. It worked great and now we can export the precious needed .dll and .config files to our own respective models.

    ReplyDelete
  14. Joris,
    I've referenced this in the past to help me... however today I've come across something rather frustrating. Running AX2012R3 CU9.

    I've built up a library of c# code from a legacy .net application to help run older code in AX for various purposes. However when running server-side I get the "Assembly containing type MyAssembly is not referenced." error. Everything I know and read says an AOS restart/full CIL would fix it... but it isn't working. The project PlatformTarget is AnyCPU since I am deploying to client AND server. Running code against the dll from client works fine, but in an SSRS report or Batch job (server side) it fails with the error. I've tried adding FullControl permissions to the DLL for the AOS account, restarting AOS service, clearing XppIL folder, running full CIL... pretty much everything. Any ideas why I would still be getting the error?

    ReplyDelete
    Replies
    1. I would look at the event log on the server to see if you can find more information. If your DLL has a dependency (reference) on other libraries that for some reason aren't on the server, that would explain it. Also check .NET versions and make sure they are available on the server side... but check the event log, it typically has more detailed info.

      Delete
    2. Thanks for the tips. Event log on the server didn't give me anything. Also this is an "all in one" dev box, so .NET would be available to client & server.

      So I dug through my c# project to find dependency issues. Turns out that was it! There were several referenced dlls for .NET controls which were part of what was brought over. Those dlls were added to AOT which apparently doesn't work for the AOS server side. Since we obviously don't care about user controls on server side, I just split all that into its own controls project.

      Delete