web
You’re offline. This is a read only version of the page.
close
Skip to main content
Community site session details

Community site session details

Session Id :
Finance | Project Operations, Human Resources, ...
Suggested answer

pass all records in grid from controller to service class (x++)

(4) ShareShare
ReportReport
Posted on by 1,819
Hi,

I have a form with grid, that shows certain records based on some conditions
 
i created a new action menu item, that when clicked it should update all records in grid in batch (no need to select records, it should update them all)
so i used sysOperationFramework to use batch processing
 
but how i'm going to pass all records in the grid, and loop them in the service class to update them?
 
what to do in controller class and in service class?
Categories:
I have the same question (0)
  • Martin Dráb Profile Picture
    235,796 Most Valuable Professional on at
    pass all records in grid from controller to service class (x++)
    Whether you get user filters or not has nothing to do with batches, MultiSelectionHelper or so. It depends on which query you use.
    The form data source can give you both queries, the original one and the one modified by the user, and it's your decision which one you can use. Then you can use the selected query for iteration, you can pack it and pass to a contract or do anything else.
    If you want to get users filters, you need to call queryRun() method of the form data source. That's what I suggested in my second reply; you've decided to do it differently and therefore you're getting a different result. You call query(), which gives you the original query. The modified is one queryRun().query().
     
    Also, if your goal is processing all records matching the query, then it's not important whether a particular record is currently displayed in the form. If you've decided that your requirements are different, OK, then you may need to change the implement. But we can't tell you what you want, especially since you've told us nothing about the business scenario.
     
    Regarding "is that if the Ids i added to the list, suddenly didn't meet the queryRange anymore, then it will make me update un-needed records": This is a decision you should have made before starting the implementation, as I told you in the first reply. And the decision needs to be based on business requirements.
     
    I have no idea what you mean by "if microsoft added a conditon/range later". You'll need to be more specific.
     
    In the first reply, I told you you need to decide what the code should do before writing. Your questions like "how to decide which one is better? what normal forms do?" suggest that you don't know it yet. At the moment, you should be thinking about how to design a solution that meets business requirements, not writing code. And if you have no idea, the thread should look differently. You should have shared the business requirements with us and ask how to implement them. You can still do that, just create a new thread; this one it already quite long and the title of your new question will be different.
  • .. Profile Picture
    1,819 on at
    pass all records in grid from controller to service class (x++)
    Hi @Martin Dráb,

    Ok i got your point now. I think what you mean is this:
     
        public static void main(Args _args)
        {
            Controller1 controller = Controller1::construct();
            
            Contract1 contract = controller.getDataContractObject() as Contract1;
    
            List             idsList    = new List(Types::String);
            FormDataSource   table1_ds  = FormDataUtil::getFormDataSource(_args.record());
            Query            query      = table1_ds.query();
    
            QueryRun queryRun = new QueryRun(query);
            while(queryRun.next())
            {
                Table1 table1   = queryRun.get(tablenum(Table1)) as Table1 ;
                idsList.addEnd(table1.Id);
            }
            contract.parmIds(idsList);
           
            controller.startOperation();
        }


    but now regarding which solution is better:


    In solution1 (Query) , if we use contract.SetQuery(), then regardless of current records in the form, even if we do manual filtration on any column, it will always take what the form query returned, and update records accordingly.
    A disadvantage i can see, is that if before i run the batch, a new record got created by another user that matches the current form query ranges, but i don't see it because i need to do small refresh on the form, it will still be updated even though i didn't see it in my current form. Correct?  This would also happen if we use the 2nd Solution (contract.ParmList()) as i loop through query form and add ranges to the Ids, right
    can this be avoided?

    but a disadvantage in solution2 (contract.ParmList()), is that if the Ids i added to the list, suddenly didn't meet the queryRange anymore, then it will make me update un-needed records, which is wrong? And to fix this, maybe i will need to add all the conditions in the query range again in the service class, but if microsoft added a conditon/range later, it means my service class won't handle this condition

    sth about solution 3( multi selection helper), which might appears as an advantage, is that after the form opens, if someone add extra manual filtration, by filtering on any column, then this will affect what I update, i mean if the form data source query returned 3 records, then i fitlered on the form on one record, then only one record will be updated


    how to decide which one is better? what normal forms do?
  • Martin Dráb Profile Picture
    235,796 Most Valuable Professional on at
    pass all records in grid from controller to service class (x++)
    I still keep repeating the same - take the query from the data source and iterate with with the QueryRun class. I already gave you an example and you're now also using QueryRun to iterate records in your own code: Service1.update(). I never suggested using MultiSelectionHelper; the only time when I mentioned it was when explaining that you shouldn't use it in your case. You may want to read my second reply once more to recall what I suggested.
  • .. Profile Picture
    1,819 on at
    pass all records in grid from controller to service class (x++)
    Hi @Martin Dráb,

    In the 2nd solution, you mentioned that i need to iterate all records and put them in a list in the contract class


    so how to iterate through records and pass them as a list?

    i selected all records in the grid using element.task(#taskSelectAll);
    then i used multiselectionHelperClass in the controller to fill the contract List with the Ids
    wasn't this what you meant?
    [ExtensionOf(formControlStr(Form1, updateButton))]
    final class From1UpdateButton_Extension
    {
    
        public void clicked()
        {
            #task
            element.task(#taskSelectAll);
    
            next clicked();
    
        }
    }
    
    [DataContract]
    public class Contract1
    {
        private List      ids;
    
        [DataMemberAttribute,
            AifCollectionTypeAttribute('_ids',Types::String),
        AifCollectionTypeAttribute('return',Types::String)]
        public List parmIds(List _ids = ids)
        {
            ids = _ids;
            return ids;
        }
    
    }
    
    
    public class Controller1 extends SysOperationServiceController
    {
    
        public void new()
        {
            super(classStr(Service1), methodStr(Service1, update), SysOperationExecutionMode::Synchronous);
        }
    
        public static Controller1 construct()
        {
            Controller1 controller = new Controller1();
            return controller;
        }
    
    
        public static void main(Args _args)
        {
            Controller1 controller = Controller1::construct();
            
            Contract1 contract = controller.getDataContractObject() as Contract1;
    
            List                    idsList              = new List(Types::String);
            MultiSelectionHelper    multiSelectionHelper = MultiSelectionHelper::createFromCaller(_args.caller());
            Table1                  table1               = multiSelectionHelper.getFirst();
    
            while(table1)
            {
                idsList.addEnd(table1.Id);
    
                table1 = multiSelectionHelper.getNext();
            }
    
            contract.parmIds(idsList);
    
            controller.startOperation();
           
        }
    
    }
    
    
    public class Service1
    {
    
        public void update(Contract1 _contract)
        {
    
            ListEnumerator listEnum = _contract.parmIds().getEnumerator();
            if(listEnum)
            {
                while (listEnum.moveNext())
                {
                      //logic
                }
            }
    
        }
    
    }
    
     
  • Martin Dráb Profile Picture
    235,796 Most Valuable Professional on at
    pass all records in grid from controller to service class (x++)
    Good to hear that the first approach works.
     
    Regarding the second, I don't understand your statement that you need all records and therefore you chose the solution for the selected ones, not all. It sounds like you chose a solution not matching your goal. Your attempt to select all sounds like a mere workaround for a wrong solution. Why exactly don't you want to simply use QueryRun class to iterate the query? Wouldn't it be simpler, more logical and more efficient?
  • .. Profile Picture
    1,819 on at
    pass all records in grid from controller to service class (x++)
    Hi Martin,

    For the first solution, do you mean like this (it worked but i want to see if this is what u meant)
    [DataContract]
    public class Contract1
    {
        private str      query;
    
        [DataMemberAttribute, AifQueryTypeAttribute('_query', '')]
        public str parmPackedQuery(str _query = query)
        {
            query = _query;
            return query;
        }
    
        public Query getQuery()
        {
            return new Query(SysOperationHelper::base64Decode(query));
        }
    
    
        public void setQuery(Query _query)
        {
            packedQuery = SysOperationHelper::base64Encode(_query.pack());
        }
    
    }
    
    
    public class Controller1 extends SysOperationServiceController
    {
    
        public void new()
        {
            super(classStr(Service1), methodStr(Service1, update), SysOperationExecutionMode::Synchronous);
        }
    
        public static Controller1 construct()
        {
            Controller1 controller = new Controller1();
            return controller;
        }
    
    
        public static void main(Args _args)
        {
            Controller1 controller = Controller1::construct();
            
            Contract1 contract = controller.getDataContractObject() as Contract1;
    
            FormDataSource   table1_ds  = FormDataUtil::getFormDataSource(_args.record());
            Query            query      = table1_ds.query();
    
            contract1.setQuery(query);
           
            controller.startOperation();
        }
    
    }
    
    
    
    public class Service1
    {
    
        public void update(Contract1 _contract)
        {
    
            Query query = _contract.getQuery();
    
            QueryRun queryRun = new QueryRun(query);
            while(queryRun.next())
            {
                //logic
            }
    
        }
    
    }
    
    


    For the 2nd solution, do you mean like this? i had to select all records, then use multi select
    i also added List as contract param
    (it worked but i want to see if this is what u meant)
     
    [ExtensionOf(formControlStr(Form1, updateButton))]
    final class From1UpdateButton_Extension
    {
    
        public void clicked()
        {
            #task
            element.task(#taskSelectAll);
    
            next clicked();
    
        }
    }
    
    [DataContract]
    public class Contract1
    {
        private List      ids;
    
        [DataMemberAttribute,
            AifCollectionTypeAttribute('_ids',Types::String),
        AifCollectionTypeAttribute('return',Types::String)]
        public List parmIds(List _ids = ids)
        {
            ids = _ids;
            return ids;
        }
    
    }
    
    
    public class Controller1 extends SysOperationServiceController
    {
    
        public void new()
        {
            super(classStr(Service1), methodStr(Service1, update), SysOperationExecutionMode::Synchronous);
        }
    
        public static Controller1 construct()
        {
            Controller1 controller = new Controller1();
            return controller;
        }
    
    
        public static void main(Args _args)
        {
            Controller1 controller = Controller1::construct();
            
            Contract1 contract = controller.getDataContractObject() as Contract1;
    
            List                    idsList              = new List(Types::String);
            MultiSelectionHelper    multiSelectionHelper = MultiSelectionHelper::createFromCaller(_args.caller());
            Table1                  table1               = multiSelectionHelper.getFirst();
    
            while(table1)
            {
                idsList.addEnd(table1.Id);
    
                table1 = multiSelectionHelper.getNext();
            }
    
            contract.parmIds(idsList);
    
            controller.startOperation();
           
        }
    
    }
    
    
    public class Service1
    {
    
        public void update(Contract1 _contract)
        {
    
            ListEnumerator listEnum = _contract.parmIds().getEnumerator();
            if(listEnum)
            {
                while (listEnum.moveNext())
                {
                      //logic
                }
            }
    
        }
    
    }
    
    



     
  • Martin Dráb Profile Picture
    235,796 Most Valuable Professional on at
    pass all records in grid from controller to service class (x++)
    No AOT query is required. You just got confused by the AIFQueryType attribute in TAMDeductionMassMatchContract class, but that's not relevant to your scenario. The thing you should have focused on there was the type of the variable (string) and the conversion between a Query object and a string. If you want to see an example without a reference to an AOT query, look at BusinessDocumentSubmissionContract.
     
    Regarding the iteration, I don't know what problem you have with it. You should already know how to get a query from a data source, create a QueryRun object from it and iterate the records. Please show us your current code and explain what problem you have with it.
     
    MultiSelectionHelper would be useful if we wanted selected records, which isn't the case here.
  • Sohaib Cheema Profile Picture
    47,602 User Group Leader on at
    pass all records in grid from controller to service class (x++)
    Hi @..
    You can take a look at several examples that exist in the standard system and can google as well.
     
    For example you can take inspiration from class named WHSOutboundShipConfirmController
     
    You can also use MultiSelectionHelper, here is another example https://www.schweda.net/blog_ax.php?bid=641&wdl=en
     
     
  • .. Profile Picture
    1,819 on at
    pass all records in grid from controller to service class (x++)
    Hi @Martin Dráb,

    but i don't have an existing AOT query to fill it in the contract  "AIFQueryType" attribute
    the query I have is from the formDataSource

    so what to do here?

    And for the 2nd way, i meant how to loop through records in form and pass them to the List in the contract class?
  • Suggested answer
    Martin Dráb Profile Picture
    235,796 Most Valuable Professional on at
    pass all records in grid from controller to service class (x++)
    Putting a Query object to the data contract isn't correct. Serialize it to a primitive data type; use SysOperationHelper class to serialize it to a text (and deserialize in your service class). For example, look at TAMDeductionMassMatchContract:
    [DataContractAttribute]
    internal final class TAMDeductionMassMatchContract
    {
        private str packedQuery;
    
        [
            DataMember,
            AifQueryType('_packedQuery', queryStr(TAMDeductionMassMatch))
        ]
        internal str parmQuery(str _packedQuery = packedQuery)
        {
            packedQuery = _packedQuery;
            return packedQuery;
        }
    
        [Hookable(false)]
        internal Query getQuery()
        {
            return new Query(SysOperationHelper::base64Decode(packedQuery));
        }
    
        [Hookable(false)]
        internal void setQuery(Query _query)
        {
            packedQuery = SysOperationHelper::base64Encode(_query.pack());
        }
    }
    Regarding the iteration, use the QueryRun class. You may either use the one returned from ds.queryRun() or create a new one based on the query. For example:
    QueryRun qr = new QueryRun(query);
    while (qr.next())
    {
        ...
    }​
    
    The contract will need a member with the List data type. Use its addEnd() method to add elements there.
    Don't forget to decorate the member with AifCollectionType attribute. As usual, there are plenty of examples in the standard application. This one is from DimensionValidationStatusContract class:
    [
        DataMemberAttribute,
        AifCollectionTypeAttribute('return', Types::String),
        AifCollectionTypeAttribute('return', Types::String)
    ]
    public List parmValidationMessages(List _validationMessages = validationMessages)
    {
        validationMessages = _validationMessages;
    
        this.determineValidationMessages();
    
        return validationMessages;
    }
    By the way, this is nothing original. You can easily find answers to these questions of yours on internet. Maybe you forgot to check the internet or you used wrong keywords.

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

Responsible AI policies

As AI tools become more common, we’re introducing a Responsible AI Use…

Mansi Soni – Community Spotlight

We are honored to recognize Mansi Soni as our August 2025 Community…

Congratulations to the July Top 10 Community Leaders!

These are the community rock stars!

Leaderboard > Finance | Project Operations, Human Resources, AX, GP, SL

#1
Sohaib Cheema Profile Picture

Sohaib Cheema 665 User Group Leader

#2
Martin Dráb Profile Picture

Martin Dráb 595 Most Valuable Professional

#3
Yng Lih Profile Picture

Yng Lih 558

Last 30 days Overall leaderboard

Product updates

Dynamics 365 release plans