Sunday, December 26, 2021

X++ | Technique | Conditional select statement

This post is inspired (or in other word, re-written) from the nice post of Ramesh Singh https://stoneridgesoftware.com/conditional-where-clauses-in-select-statements-in-dynamics-ax.


Background

In many situations, we need a conditional select statement. To do so, the most practical way is to use the thing I called it the Query pattern (Query, QueryBuildDataSource, QueryRun, …).

The Query pattern absolultely works well, but there is an alternative technique.


Example

Let's check a standard method \Data Dictionary\Tables\CustTable\Methods\find.

 static CustTable find(CustAccount  _custAccount,  
                       boolean      _forUpdate = false)  
 {  
   CustTable custTable;  
   ;  
   if (_custAccount)  
   {  
     if (_forUpdate)  
           custTable.selectForUpdate(_forUpdate);  
     select firstonly custTable  
           index hint AccountIdx  
           where custTable.AccountNum == _custAccount;  
   }  
   return custTable;  
 }  

Let assume we add a new custom flag field IsActive, then we might need to extend the above implementation. There are several way to implement it. For example, adding a new method and name it like findActive or replacing the above statement with an if-else clause.


However, regarding Ramesh technique, we can make it a bit more simpler by the following code.

 static CustTable find(CustAccount  _custAccount,  
                       boolean      _isActiveCheck = false,)  
                       boolean      _forUpdate = false)  
 {  
   CustTable custTable;  
   ;  
   if (_custAccount)  
   {  
     if (_forUpdate)  
           custTable.selectForUpdate(_forUpdate);  
     select firstonly custTable  
           index hint AccountIdx  
           where custTable.AccountNum == _custAccount  
              && (!_isActiveCheck || custTable.isActive);  
   }  
   return custTable;  
 }  

The above technique is similar to the conventional QueryBuildDataSource.addRange we usually did. 


My opinion

In general, to build a dynamic and complex select statement (or query), the Query pattern is still my preferred choice.

But when adding a small customization as the above example, this technique is quite useful. It avoids the unnecessary redundant and safe a couple line of codes, even sacrifice a little bit self-described characteristic.


Thanks for reading. Until the next post!



Sunday, February 14, 2021

OData | 401 Unauthorized issue

Background        

Did you ever find this kind of issue? You have an external app which wants to exchange data with D365FO via OData. You create a Azure application in Azure as usual and whitelist it in D365FO.

It works well when testing a token requesting. But when trying an API call, you get this following error in Postman (or other REST API tools).


401 Unauthorized issue


This post will guide things you can check when you got above error message.


Instructions

There are steps I would recommend you to check it respectively.


  1. [On D365FO AOS machine] Check "Event Viewer" on "Applications and Service Logs -> Microsoft -> Dynamics -> AX-WebApi/Operational" path.

     Thanks Matej  https://stackoverflow.com/questions/58544679/calling-customer-service-results-in-401-unauthorized

     

    



     

     From the above message, you can see obviously that the problem is token validating.

     

  2. [On Azure] You can verify these follows..

      2.1 Tetant ID - Go overview -> Tenant information i.e. abcabcab-1111-2222-3333-abc123456789

      2.2 Primary domain - Go overview -> Tenant information i.e. d365abc.onmicrosoft.com

      


      

      

      2.3 Login account - See the top right side of the page - The account you use to login Azure and create the "App registrations" (the app to get the token) i.e. mrMillionProblems@d365abc.onmicrosoft.com

     

     If you're not sure the existing app created correctly, you can create a new App in App registrations as well.

     

      


     

  3. [On D365FO] Make sure account mrMillionProblems@d365abc.onmicrosoft.com can connect to D365FO. If not, add the account in D365FO users.

  

      


  

  4. [On D365FO] Make sure the app in Azure Active Directory applications is configured correctly. Verify Client Id and User ID. 

  

      User ID can be Admin or other users. However, that user (Admin or whatever) should set its email as mrMillionProblems@d365abc.onmicrosoft.com.

      

   



      


Conclusion

That's all! I hope it might help when you find the similar cases.


Until the next post! 

Sunday, February 7, 2021

X++ | Reread, refresh, research, and executeQuery

This is my personal memo for these datasource methods which was described very well by this following url. https://community.dynamics.com/365/supply-chain-management/b/axvanyakashperuk/posts/tutorial-58-refresh-reread-research-executequery-which-one-to-use-63

I rewrite it here because I found it's quite difficult to memorize the above concept. The reason is probably these methods are sharing somethings in common, then difficult to distinguish. 


Getting a latest value







Reread     Get the current record value from database to form datasource cache.

Refresh    Get the current record value from form datasource cache to form control.

Commonly used: reread() and then refresh()


Rerunning a form datasource query

Research            Rerun the form datasource query against the database with base query + user interface filters.

ExecuteQuery    Rerun the form datasource query against the database with base query + dev code.

In other words (below are copied directly from above url):

    1) Research - when the research method is called, a new instance of the queryRun is created, using the formDataSource.queryRun().query() as the basis. Therefore, if the user has set up some filters on the displayed data, those will be preserved. 

    2) ExecuteQuery - on the other hand, will use the original query formDataSource.query() as the basis, therefore removing any user filters.