AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX Blogs
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 28.05.2014, 10:11   #1  
Blog bot is offline
Blog bot
Участник
 
25,617 / 848 (80) +++++++
Регистрация: 28.10.2006
littleax: Part1. Dynamics ax 2012. Split construct method by models.
Источник: http://littleax.blogspot.com/2014/05...ct-method.html
==============

Split construct method by models. Part1.

Using construct method is a common technique in AX, for example SalesFormLetter::construct(...)


Corresponding child class of SalesFormLetter class is constructed based on DocumentStatus parameter.
This is good. However let’s try to implement one simple task.
Step 1.

We need to print message BackOrder is #SalesId #CustomerName for all sales orders.
Implementation.
Create a new class
X++:
class TestBaseClass
{
   SalesTable   salesTable;
}

public SalesTable parmSalesTable(SalesTable _salesTable = salesTable)
{   
    return salesTable;
}
 public void showInfo()
{   
info(strFmt("Backorder is %1 %2", this.parmSalesTable().SalesId, this.parmSalesTable().customerName()));
}

 public static TestBaseClass construct()
{   
 return new TestBaseClass();
}
and job for testing this class
X++:
static void TestBaseClass1(Args _args)
{   
 SalesTable        salesTable;         // cursor   
 TestBaseClass     baseClass;    
 setPrefix("SalesInfo1");   
 while select salesTable
        {
               baseClass = TestBaseClass::construct(salesTable.SalesStatus);
               baseClass.parmSalesTable(salesTable);
               baseClass.showInfo();
        }
        info("done");
}
Result is

Step 2.

Consultant has changed the requirement to print Invoice if SalesStatus== Invoice. - ok - let's implement the change.
Create a new class
X++:
class TestBaseClass01_Model01 extends TestBaseClass
{
}
overwrite the method
X++:
public void showInfo()
{
    info(strFmt("Invoiced is %1 %2", this.parmSalesTable().SalesId, this.parmSalesTable().customerName()));
}
and change construct method of TestBaseClass
X++:
public static TestBaseClass construct(SalesStatus _salesStatus)
{
    TestBaseClass   testBaseClass;
  
    switch (_salesStatus)
    {
        case SalesStatus::Invoiced:
            testBaseClass = new TestBaseClass01_Model01();
            break;
        default :           
            testBaseClass = new TestBaseClass();

    return testBaseClass;
}
Update job and run it

OK - we have done this!
All the changes were done in one layer and one model (for example layer cus, model A01).
Step 3.

Our consultant wants to create another modification in cus layer, but in model A02. The goal of the modification is to print Delivered for sales order with SalesStatus==Delivered. Why do we need to implement it in different model (we ask our consultant)?
Because we have many clients. Some clients want to get additional information, some - don't. Base logic is in one model and additional logic is in another model which allows us to have only one application and to develop without any problem in the future.
Ok, We have a new task!!!
Excellent. Let's do it!

*Note - if you create an object in wrong model - don't worry, you can always change current model of the object (method, event handler ...) - right click on the object and select - Move to model ...

Create a new model and select it as current. In new model create a new class
X++:
class TestBaseClass02_Model02 extends TestBaseClass
{
}
overload method
X++:
public void showInfo()
{
    info(strFmt("Delivered is %1 %2", this.parmSalesTable().SalesId, this.parmSalesTable().customerName()));
}
than slightly modify method TestBaseClass::construct()

X++:
public static TestBaseClass construct(SalesStatus _salesStatus)
{
    TestBaseClass   testBaseClass;

    switch (_salesStatus) 
    {
        case SalesStatus::Invoiced:
            testBaseClass = new TestBaseClass01_Model01();
            break;
        case SalesStatus::Delivered:
            testBaseClass = new TestBaseClass02_Model02();
            break;
        default :
            testBaseClass = new TestBaseClass();

    }
    return testBaseClass;
}
and run our job without any modification


Everything is good and we can summarize our results.
1. We implemented the requirements - good.
2. We created correct models by AX standard - good.
3. We have one model A01 for one type of customers - good.
4. We have one model A02 - this model allows us to extend logic for other customers. - good.
Everything is almost good!

Let's see our solution split by models

TestBaseClass - in model A01
TestBaseClass01_Model01 in model A01
TestBaseClass02_Model02 in model A02

If we try to install model A01 to another application - we will get compile error, because system could not find class TestBaseClass02_Model02 - this is not good.
If we move TestBaseClass::construct to A02 model and try to install A01 to another application - we will lose construct method - this is not good.

How we can re-factoring our small solution to resolve this problem?
In AX 2012 developers have interesting mechanism - delegates. Let's try to use it to resolve the conflict.
Ok, We have a new Interesting task!

Step 4.

4.1 Change TestBaseClass::construct

X++:
public static TestBaseClass construct()
{
    return new TestBaseClass();
}
4.2. create a new method TestBaseClass. findDetermination() (model A01)
X++:
public boolean findDetermination(SalesTable _salesTable)
{
    boolean ret;

    if (_salesTable.SalesStatus == SalesStatus::Backorder)
    {
        ret = true;
        this.parmSalesTable(_salesTable);
    }

    return ret;
}
4.3. overwrite method \Classes\TestBaseClass01_Model01\findDetermination (model A01)
X++:
public boolean findDetermination(SalesTable _salesTable)
{
    boolean ret;

    if (_salesTable.SalesStatus == SalesStatus::Invoiced)
    {
        ret = true;
        this.parmSalesTable(_salesTable);
    }

    return ret;
}
4.4. overwrite method \Classes\TestBaseClass02_Model02\findDetermination (model A02)
X++:
public boolean findDetermination(SalesTable _salesTable)
{
boolean ret;

    if (_salesTable.SalesStatus == SalesStatus::Delivered)
    {
        ret = true;
        this.parmSalesTable(_salesTable);
    }

    return ret;
}
4.5. Let's create a new class - this class helps us to resolve the problems (model 01)
X++:
// Class fabric. This class can generate right class updater
class TestBaseClassFabricaConstructor
{
    SalesTable  salesTable;             // salesTable for updating
    List        potentialConstructors;  // save all potential updaters
}

public void new()
{
    potentialConstructors = new List(Types::Class)
}


public SalesTable parmSalesTable(SalesTable _salesTable = salesTable)
{
    salesTable = _salesTable;

    return salesTable;
}


// add updater to some list to save public void subscribe(TestBaseClass _subscriber)
{
    potentialConstructors.addEnd(_subscriber);
}


public static TestBaseClassFabricaConstructor construct()
{
    return new TestBaseClassFabricaConstructor();
}

and create a new blank but very important method
X++:
delegate void constructorCollect(TestBaseClassFabricaConstructor _fabricaCollector)
{

}
plus another method to call delegate
X++:
// call all subscribers and get subscribers list. Subscriber add itselt manualy to our collections
public void collect(TestBaseClassFabricaConstructor _fabricaCollector = this)
{
    this.constructorCollect(_fabricaCollector);
}
It look's good, but not finished. We will return to this class a little bit later.

4.6. create a new method \Classes\TestBaseClass\subscribe (model A01)
X++:
public static void subscribe(TestBaseClassFabricaConstructor _fabricaConstructor)
{
    TestBaseClass   baseClass = TestBaseClass::construct();
    _fabricaConstructor.subscribe(baseClass);

}
4.7. Create a new method \Classes\TestBaseClass01_Model01\subscribe (model A01)
X++:
public static void subscribe(TestBaseClassFabricaConstructor _fabricaConstructor)
{
    TestBaseClass01_Model01   baseClass = TestBaseClass01_Model01::construct();
    _fabricaConstructor.subscribe(baseClass);

}
4.8. Create anew method \Classes\TestBaseClass02_Model02\subscribe (model A02)
X++:
public static void subscribe(TestBaseClassFabricaConstructor _fabricaConstructor)

    TestBaseClass02_Model02   baseClass = TestBaseClass02_Model02::construct();
    _fabricaConstructor.subscribe(baseClass);
}
4.9. Return to class TestBaseClassFabricaConstructor.
method constructorCollect - right click "New Event Handler Subscription"


and do like shown bellow 3 times - for all three classes

Each event handler can be placed in own model.
TestBaseClass and TestBaseClass01_Model01 is placed in model A01, TestBaseClass02_Model02 is placed in A02 model.
4.10 - create the last method in TestBaseClassFabricaConstructor.softConstruct
X++:
// determine what is class want be updater for salesTable
public TestBaseClass softConstruct(SalesTable _salesTable = this.parmSalesTable())
{
    ListEnumerator  le = potentialConstructors.getEnumerator();
    TestBaseClass   ret;

    while (le.moveNext())
    {
        ret = le.current();
        if (ret.findDetermination(_salesTable)) // check if updater want update SalesTable - welcome
        {
            break;
        }
    }
    return ret;

}
Very good. Now we need to run it!

Create a new job

X++:
// TODO - Example for everyone. How me can split method with multimodel declarations
static void TestBaseClassFabricaConstructorJob(Args _args)
{
    TestBaseClassFabricaConstructor fabricaConstructor; // constructor for our class updater
    SalesTable                      salesTable;         // cursor
    TestBaseClass                   baseClass;          // abstract updater

    setPrefix("SalesInfo");
    while select salesTable
    {
        fabricaConstructor = TestBaseClassFabricaConstructor::construct();  // init
        fabricaConstructor.parmSalesTable(salesTable);                      // init
        fabricaConstructor.collect();                                       // call method collect. This method call all subscribers and subscribers add itself to update candidate list
        baseClass = fabricaConstructor.softConstruct();                     // loop by all updaters and find the best updater
        if (baseClass)                                                      // if updater find - show info about it
        {
            baseClass.showInfo();
        }
    }
    info("done");
    // If we need to create new updater
    // 1. Create new class and extends it from base class (TestBaseBaseClass_Model01)
    // 2. Create static method subscribe
    // 3. Create subscription to \Classes\TestBaseClassFabricaConstructor\constructorCollect
    // 4. Create method findDetermination
    // 5. Create method showInfo
    //
    //
}
and results


Well done!

Let's see what will happen if we decide to publish model A01 for clients - it will work - because we move model with out one event handler.

Next time we will try to resolve our task with another approach.
Have a nice day!



Источник: http://littleax.blogspot.com/2014/05...ct-method.html
__________________
Расскажите о новых и интересных блогах по Microsoft Dynamics, напишите личное сообщение администратору.

Последний раз редактировалось mazzy; 28.05.2014 в 13:19.
 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
axsa: MDM Adapter - Extending Dynamics AX 2012 R3 Master Data Management Blog bot DAX Blogs 0 22.05.2014 03:28
crminthefield: Podcast and Overview: Microsoft Dynamics CRM 2011 Update Rollup 16 Blog bot Dynamics CRM: Blogs 0 23.01.2014 03:15
DAX: How to gain additional value from the Microsoft application platform with Microsoft Dynamics AX 2012 R2 Blog bot DAX Blogs 3 21.06.2013 15:16
ax-erp: Debug BP errors in Dynamics AX 2012 Blog bot DAX Blogs 0 14.12.2012 23:11
rumicrosofterp: Dynamics AX на Convergence 2012 Blog bot Microsoft и системы Microsoft Dynamics 0 13.01.2012 11:11

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 15:13.