Run dynamic class based on field value - axapta

I have 3 classes each with a method which run some calculation and write the values in different fields, this method also writes the classname into a field from where the method runs from.
This works fine.
I recently created a button to re-run the method, from the class the method originally run from.
For example:
Class1 RunMethod
Class2 RunMethod
Class3 RunMethod
I am now creating the method for the action button when clicked, but I have no clue how to run a specific method from the class where it originally ran from. The class name is in a field.
I think I can accomplish this with SysDictClass, but I have no clue how to start, how can I best start with this method?

This should get you the idea. I wrote it in AX 2009 but it should probably work in AX 2012 as well.
public static client void SysDictClassJob()
{
ClassId classId;
Object obj;
SysDictClass sysDictClass;
;
// Create instance (if you are going to call a member method)
classId = className2Id('SomeClass');
obj = classFactory.createClass(classId);
// Invoke member method
sysDictClass = new SysDictClass(classId);
sysDictClass.callObject('yourMemberMethod', obj);
// Invoke static method
sysDictClass.callStatic('yourStaticMethod');
}

Related

How does CtMethod affects classloading? CtMethod.insertBefore() is ineffective

Please read along. I have a Lion class that has a method stayLion.
public class Lion {
//class Variables and methods
public void stayLion(String temp,int temp2,double temp3, boolean temp4,Food food) throws InterruptedException {
//method Body
}
}
The method takes in a Food instance as one of its parameter. Food class has cook method
public class Food{
public void cook(){
}
}
I am trying to instrument both these methods, stayLion and cook. The transform method is called for every class that is loaded by JVM.(Interesed to know, where and who calls it. Correct me if I am wrong) If classname matches with my desired list of classnames, I extract the list of methods, and verify the methodName and its parameter, I find the CtMethod instance for the method I wish to instrument (stayLion and cook).
public byte[] transform(ClassLoader loader, final String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
//verifying className(fully qualified), if same then,
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.getCtClass(className.replace("/","."));
CtMethod[] methods = ctClass.getDeclaredMethods();
for (CtMethod method : methods) {
//verifying method (name and parameter)
method.insertBefore( //some code );
}
}
However, in the process of verifying parameters for stayLion, the Food class gets loaded and transform method is not called, (I don't know the reason for this. The class loader is same. Why is transform method not called for Food class as it gets loaded?). To overcome this, I explicitly call transform method for Food class (in general, I call transform method everytime a class whose method I wish to instrument is in the parameter list of any other method I also wish to instrument).
transform(loader,"com/here/debugHelper/Food",null,protectionDomain,null);
But this explicit calling of transform method is ineffective. To be precise,
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.getCtClass(className.replace("/","."));
CtMethod[] methods = ctClass.getDeclaredMethods();
Till here, methods contains list of all methods of Food class. After verifying cook method with no parameter
for (CtMethod method : methods) {
//verifying method (name and parameter)
method.insertBefore( //some code );
}
But at this point, method.insertBefore() is ineffective and is not adding any code to the desired method.
The transform method is invoked by the JVM during class loading or redefinition. It does not make much sense for you to call the method directly unless you are testing.
If you are loading a class during a code manipulation, no class being loaded will be transformed. This is to avoid circularities in your instrumentation where instrumenting Aloads B and instrumenting B loads A. If you attempt to load the instrumented class itself, this will result in a ClassCircularityError.
I assume that when you instrument Lion, you are loading Food when processing the parameters. By using Javassist, you normally avoid this as Javassist loads class files directly but I figure that you are referencing the Lion class as a constant somewhere and therefore load it somwhere in your transformer.

What is a proper way of using reflection in AX?

Sometimes, when I need to do more complicated stuff than change one value in datasource, I would like some method on caller. For example I have a form A with overview. Form A has method setName() (I define). I open a related detail (form B). I change something and I want to call setName on caller.
Nowdays I am doing it in following way
element.args().caller().setName();
but I am looking for more idiomatic way. So what is proper way of calling method on caller in AX 2012 R3?
It sounds like you need to change your development methodology if you referencing that many caller-form methods. You would mostly do that for calling doRefresh or calling updateDesign, which are both typically created methods on forms. Beyond updating the design and refeshing, you should use a class form handler.
If you must do a caller-callback, you can validate by doing:
if (formHasMethod(element.args().caller(), identifierstr(updateDesign)))
{
element.args().caller().updateDesign();
}
You can pass your class as the caller. Here is a simple sample set of code:
\Forms\Form1\Designs\Design\[Group:Group]\Button:Button\Methods\clicked:
void clicked()
{
FormRun formRun;
Args args = new Args(formstr(Form2));
TestClass testLocal = new TestClass();
testLocal.parmTestVar('ZZZ');
args.caller(testLocal);
formRun = classfactory.formRunClass(args);
formRun.init();
formRun.run();
formRun.wait(true);
}
\Forms\Form2\Methods\init:
public void init()
{
TestClass testClass;
super();
testClass = element.args().caller() as testClass;
info(strFmt("%1", testClass.parmTestVar()));
}
Look at \Forms\SalesTable and \Classes\SalesTableForm or \Classes\SysCompare\startCompareOfContextProvider and the init method
identifierStr is not depreciated. It's a normal intrinsic function, but you will get a best practice warning if you use the identifierStr function. This is because no existence checking is carried out for identifierStr. Try to use a more specific intrinsic function if one is available. See http://msdn.microsoft.com/en-us/library/aa626893.aspx
You could take a look at Calling methods on a caller form, but identifierStr is deprecated in AX 2012. As far as I know there is no way to check for form methods at compile time.
But I would suggest avoiding methods on forms. Place them in a form handler class instead, you can then use the methodStr function to check for a method.

WF4 Business Rules

I want to pass a object to workflow as input parameter, without creating instance of it, as this object is already filled up with lot of sub-objects, and I want to run lot of rules on that object. I want to use
WorkflowInvoker.Invoker(this); Is this possible in Windows Workflow Foundation 4.0, If so, what should I keep in InArguments as the type?
Thanks In Advance,
Thanks for your input....
Sorry, I think I haven't kept the question correctly, I had a business class used by bank customers, class is like this,
public partial class BankName.DTOS.clsCardDetails : BaseClass
{
public string _mBankStatusCode;
protected override string IsCreditCard(HttpContext ctx)
{
Status = ctx.Request.Form["Trans_Status"];
_mBankStatusCode = Status.ToString();
}
}
Now, I have to pass this class object to workflow foundation, I should not create new instance of this class in workflow again. Because in workflow, I want to get the status, so ideally what I thought of passing to workflow is object of "BankName.DTOS.clsCardDetails". That is my question.
InArguments<> to workflow is object of BankName.DTOS.clsCardDetails
Arguments passed into a workflow need to be put into a dictionary and the dictionary is passed into the workflow.
var dictionary = new Dictionary<string, object> { { "BankName", _bankNameObject } };
WorkflowInvoker.Invoke(this, dictionary);
The key must have the same name as your InArgument and your object must have the same type as your InArgument.
You can pass as many arguments as you like into the workflow via the dictionary.
See http://blogs.msdn.com/b/rjacobs/archive/2011/05/26/passing-arguments-to-workflow-activities-again.aspx

Passing data between views and models in Flex MVC framework

I have never used Flex/Actionscript before so excuse me if I'm asking anything obvious but I have been working on this for 3 days (includes searching google and stackoverflow) before writing this post.
I have been asked to modify a game written in Flex 4 made with a mini-IoC-based MVC framework. Two mods I have been asked to make is a game over screen, which displays the final score and a difficulty selection on the introduction screen. I have made the game over screen, and successfully managed to get the controller to bring it up when the game timer runs out.
So for now, the game structure goes:
Intro View -> Game View -> Game Over View
^ |
|__________ (retry?) ________|
The first problem is getting the score to be passed from the mainGame ActionScript file to the game over screen.
Things I have tried:
Importing mainGame in the gameOverViewBase and calling the mainGame.score variable in the gameOverView mxml file.
-EDIT!!! = the above method works if I change the score variable in mainGame to a constant, but if it remains a variable, the controller won't load the gameOverView and the game sits at an empty mainGame view.
Making a function that adds to a new score variable in the gameOverViewBase whenever the player scores during the game.
Passing the score as a parameter to the GameOverView when the MainGame ends
controller.loadGameOver(score);
This seemed like the most logical way to go about it. So I followed the function through the other components of the game setting the loadGameOver to take an integer as a parameter until I got to the main game actionscript file:
public function loadGameOver(score:int) : void
{
loadView(GameOverView);
}
The loadView function (shown below) is where I get stuck because I can't see where to pass the 'score' parameter. It looks like this:
private function loadView(viewClass:Class, modelClass:Class = null) : void
{
var view:View = new viewClass();
if(!view) throw new Error("Could not load view");
viewContainer.removeAllElements();
viewContainer.addElement(view);
view.controller = this;
}
The second problem is the difficulty selection on the introduction screen. I have done this with 3 buttons (easy, normal, hard) in the mxml file and for every button in the ActionScript:
protected function onEasyButtonClick() : void
{
set = "easy"
controller.loadMainGame(set);
}
Once again, I end up at the above loadView function.
To sum up: I need to know how to pass the data between the views and models. If that's not the ideal method, I am open to any other methods that you think are better.
Thank You!
Ben
P.S. I can send my source code to anyone who would like to help :)
You don't specify which MVC framework you're using which would be helpful. However, score should definitely be a property of a model and the model data should be accessible to the view either directly, perhaps via binding (thanks weltraumpirat), or via some intermediary class.
I would suggest you have a look at some of the existing view classes and try to figure out how they are fed the model data. You can use this approach to get the data you need for your view.
[EDIT]:
The mainGame property is not being set on your GameOverView instance so you're unable to access its score property either through binding or through trace. The loadView method of your controller class accepts a Model class reference which it uses to construct a new Model instance to be used by the new View. Unfortunately this is no use to you as your GameOverView needs the instance of MainGame which was created for the MainGameView (and which contains the current score).
I don't know if the following fits into the philosophy of the framework you're using. However, I would change the loadView method to accept an instance of a Model rather than a class reference, and create and cache a reference to an instance of MainGame when your controller is instantiated. That way you can pass the same Model reference to both the MainGameView and GameOverView when these are created.
public class WhackAMoleBase extends Application implements IGameController
{
public var viewContainer:Group;
private var mainGame:MainGame
public function WhackAMoleBase() : void
{
super();
// Create and cache an instance of the main game Model
mainGame = new MainGame();
addEventListener(FlexEvent.CREATION_COMPLETE, onCreationComplete);
}
public function loadIntroduction() : void
{
loadView(IntroductionView);
}
public function loadMainGame() : void
{
loadView(MainGameView, mainGame);
}
public function loadGameOver() : void
{
// Use the same instance of MainGame which the MainGameView
// has been using as the Model for the GameOverView
loadView(GameOverView, mainGame);
}
// Accept a Model instance rather than a class Reference
private function loadView(viewClass:Class, model:Model = null) : void
{
//Create a new instance of the supplied view
var view:View = new viewClass();
if(!view) throw new Error("Could not load view");
//Clear any previous views in the container and add
viewContainer.removeAllElements();
viewContainer.addElement(view);
//Property based dependency injection
view.controller = this;
//There may or may not be a model required for the
//requested view; check and instantiate appropriately
if(model)
{
//Give model reference to the controller
//and link the view up to the model
model.controller = this;
view.model = model;
}
}
private function onCreationComplete(event:FlexEvent) : void
{
//When the application is first created, we want to show the introductory view
loadView(IntroductionView);
}
}

Setting parameter invoked Method

I have a question about .Net CF 3.5 Reflection.I am invoking expected something method like this,
object n= instance.Type.GetMethod("DoSome"
).Invoke(instance.Instance,
new object[] { commandKeys }
);
commandKey is my parameter list, instance.Type and instance.Instance is not null.
Methods invokes normally but, when I set any property(belongs to which I am calling class) in "DoSome" method, property dont have a value(default value).
public class CSet:ITask
{
public void DoSome
{
SomeProperty=true;
OnTaskCompleted(this);
}
}
I am catching "OnTaskCompleted" delegate which contains "ITask" interface but like i said ITask SomeProperty has not my given value
Thx your answers.
You say that the "Methods invokes normally", but at the same time you claim that the code inside the method is not executed. How do you know that the method is invoked at all?
Are you sure that instance.Instace is the same instance you check SomeProperty on, and catch OnTaskCompleted from?

Resources