<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
    <channel>
        <title> X++
  </title>
        <link>http://eduardomiranda.net/blogs/dynamicsax/category/9.aspx</link>
        <description> X++
  </description>
        <language>pt-BR</language>
        <copyright>Eduardo Miranda</copyright>
        <managingEditor>dynamicsax@eduardomiranda.net</managingEditor>
        <generator>Subtext Version 1.9.5.176</generator>
        <item>
            <title>Práticas e padrões: Pack e Unpack</title>
            <link>http://eduardomiranda.net/blogs/dynamicsax/archive/2008/02/09/239.aspx</link>
            <description>&lt;p&gt;A &lt;a href="http://en.wikipedia.org/wiki/Serialization"&gt;serialização&lt;/a&gt; de objetos possibilita a persistência e/ou a transmissão de um objeto entre módulos fisicamente separados de uma aplicação. É um processo parecido com transformar o leite em pó e depois conseguir recriar o leite adicionando água. &lt;/p&gt;
&lt;p&gt;Diversas linguagens oferecem funcionalidades de serialização, em X++ ela é feita através do padrão pack-unpack. Enquanto em .Net, por exemplo, o objeto pode ser serializado em xml ou outros formatos, no X++ o formato utilizado é o &lt;a href="http://www.eduardomiranda.net/blogs/dynamicsax/archive/2008/01/18/232.aspx"&gt;container&lt;/a&gt;. Vamos utilizar a classe Person para o nosso exemplo: &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; Person
{
    str     name;
    date    birthdate;
}&lt;/pre&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Mas em que consiste “empacotar” um objeto? Basicamente é guardar todas as variáveis deste objeto em um container. O processo inverso, “desempacotar” o objeto, é receber um container contendo os valores das variáveis e carregar as variáveis com estes valores. &lt;/p&gt;
&lt;p&gt;Uma versão simplista, mas funcional, do pack-unpack seria assim:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; container pack()
{
    &lt;span class="kwrd"&gt;return&lt;/span&gt; [name, birthdate];
}

&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; unpack(container _packed)
{;
    name        = conpeek(_packed, 1);
    birthdate   = conpeek(_packed, 2);
}&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
]]&gt;&lt;/style&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Veja a utilização do padrão. Primeiro crio um objeto pessoaA e coloco um nome e data de nascimento neste objeto. Utilizando o pack, guardo este objeto serializado em um container e, em seguida, destruo o objeto. Depois crio um novo Person, pessoaB, e, utilizando o unpack, restabeleço os valores do pessoaA neste novo objeto.&lt;/p&gt;
&lt;pre class="csharpcode"&gt;    Person pessoaA;
    Person pessoaB;
    container packed;

    pessoaA = &lt;span class="kwrd"&gt;new&lt;/span&gt; Person();
    pessoaA.parmName(&lt;span class="str"&gt;"Joao"&lt;/span&gt;);
    pessoaA.parmBirthdate(10\10\2000);

    packed = pessoaA.pack();

    pessoaA = &lt;span class="kwrd"&gt;null&lt;/span&gt;;
    
    pessoaB = &lt;span class="kwrd"&gt;new&lt;/span&gt; Person();
    
    pessoaB.unpack(packed);
    
    print pessoaB.parmName();
    print pessoaB.parmBirthdate();

    pause;&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
]]&gt;&lt;/style&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Esta versão funciona, mas poderia ficar um pouco melhor. Preciso repetir cada variável nos dois métodos, pack e unpack, além disso preciso acertar a ordem do container, senão posso colocar o valor de uma variável em outra durante o unpack. Utilizando uma macro, que declaro na classe, consigo resolver estes problemas. Esta nova implementação fica assim:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; Person
{
    str     name;
    date    birthdate;

    #LOCALMACRO.CurrentList
        name,
        birthdate
    #ENDMACRO
}

&lt;span class="kwrd"&gt;public&lt;/span&gt; container pack()
{
    &lt;span class="kwrd"&gt;return&lt;/span&gt; [#CurrentList];
}

&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; unpack(container _packed)
{;
    [#CurrentList] = _packed;
}&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
]]&gt;&lt;/style&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Em CurrentList declaro as variáveis que desejo empacotar para esta classe. Depois o pack-unpack utilizam a lista, eliminando a duplicação de código. &lt;/p&gt;
&lt;p&gt;  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Versionamento&lt;/strong&gt; &lt;/p&gt;
&lt;p&gt;Muitas vezes as classes evoluem, ganhando novas variáveis, dadas as necessidades do negócio. Neste caso teremos um problema, imagine que um container contendo um objeto serializado é persistido e depois de algum tempo a aplicação tenta “desempacotá-lo” na classe que agora contém novas variáveis. Não seria possivel, na nossa implementação. &lt;/p&gt;
&lt;p&gt;Portanto precisamos de uma pequena mudança. Digamos que a classe Person agora também contém o sexo da pessoa como variável. Então tenho uma variável e um método parm para sexo:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; Person
{
    str     name;
    date    birthdate;
    Gender  gender;

    #LOCALMACRO.CurrentList
        name,
        birthdate
    #ENDMACRO
}
&lt;span class="kwrd"&gt;public&lt;/span&gt; Gender parmGender(Gender _gender = gender)
{;
    gender = _gender;
    &lt;span class="kwrd"&gt;return&lt;/span&gt; gender;
}&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
]]&gt;&lt;/style&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Agora tenho que mudar a implementação do pack-unpack para que ele saiba diferenciar pacotes contendo um objeto da versão antiga, sem sexo, da nova, com sexo. Adiciono a variável da CurrentList, esta foi a parte fácil. Crio uma constante com a versão corrente, 2.&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; Person
{
    str     name;
    date    birthdate;
    Gender  gender;

    #DEFINE.CurrentVersion(2)
    #LOCALMACRO.CurrentList
        name,
        birthdate,
        gender
    #ENDMACRO
}&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
]]&gt;&lt;/style&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;No pack a versão passa a ser a primeira variável empacotada&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; container pack()
{
    &lt;span class="kwrd"&gt;return&lt;/span&gt; [#CurrentVersion, #CurrentList];
}&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
]]&gt;&lt;/style&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;No unpack, verifico qual a versão do pacote recebido, e implemento um swicth para cada versão que a classe já teve. O método RunBase::getVersion() é um helper que lê o container descobre sua versão.&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; unpack(container _packed)
{
    &lt;span class="kwrd"&gt;int&lt;/span&gt; version;
    ;
    
    version = RunBase::getVersion(_packed);

    &lt;span class="kwrd"&gt;switch&lt;/span&gt; (version)
    {
        &lt;span class="kwrd"&gt;case&lt;/span&gt; #CurrentVersion:
            [version, #CurrentList] = _packed;
            &lt;span class="kwrd"&gt;break&lt;/span&gt;;

        &lt;span class="kwrd"&gt;default&lt;/span&gt;:
            [name, birthdate] = _packed;
    }
}&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
]]&gt;&lt;/style&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Veja a utilização, consigo desempacotar tanto um container contendo somente nome e data de nascimento, quanto a versão mais nova. Lógico que a variável gender assume o valor defaul, Unknown, quando o container não a possui. &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;pre class="csharpcode"&gt;    Person pessoaA;
    Person pessoaB;
    container packed;
    container oldPacked = [&lt;span class="str"&gt;"Joao"&lt;/span&gt;, 10\10\2000];

    pessoaA = &lt;span class="kwrd"&gt;new&lt;/span&gt; Person();
    pessoaA.parmName(&lt;span class="str"&gt;"Joao"&lt;/span&gt;);
    pessoaA.parmBirthdate(10\10\2000);
    pessoaA.parmGender(Gender::Male);

    packed = pessoaA.pack();

    pessoaA = &lt;span class="kwrd"&gt;null&lt;/span&gt;;

    pessoaB = &lt;span class="kwrd"&gt;new&lt;/span&gt; Person();

    pessoaB.unpack(oldPacked);

    print pessoaB.parmName();
    print pessoaB.parmBirthdate();
    print pessoaB.parmGender();

    pessoaB.unpack(packed);

    print pessoaB.parmName();
    print pessoaB.parmBirthdate();
    print pessoaB.parmGender();

    pause;&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
]]&gt;&lt;/style&gt;
&lt;p&gt;  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Herança&lt;/strong&gt; &lt;/p&gt;
&lt;p&gt;Uma nova classe, Employee, é criada herdando da classe Person, empregado é uma pessoa, que também possui data de contratação. &lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;class&lt;/span&gt; Employee extends Person
{
    date hiringDate;
}
&lt;span class="kwrd"&gt;public&lt;/span&gt; date parmHiringDate(date _hiringDate = hiringDate)
{;
    hiringDate = _hiringDate;
    &lt;span class="kwrd"&gt;return&lt;/span&gt; hiringDate;
}&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
]]&gt;&lt;/style&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Como é Employee filha de Person, posso utilizar os métodos pack-unpack, no entanto, como era de se esperar, a variável hiringDate não será empacotada. Se eu simplesmente sobrescrever o pack-unpack e implementar minha própria versão, estarei duplicando código. Preciso sim, estender os métodos para que o pacote contenha também as variáveis da classe filha. &lt;/p&gt;
&lt;p&gt;O pack, portanto, vai pegar o container da classe mãe, e vai empacotar em um novo container, junto com suas próprias variáveis.&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; container pack()
{
    container &lt;span class="kwrd"&gt;base&lt;/span&gt;;

    &lt;span class="kwrd"&gt;base&lt;/span&gt; = super();

    &lt;span class="kwrd"&gt;return&lt;/span&gt; [#CurrentVersion, #CurrentList, &lt;span class="kwrd"&gt;base&lt;/span&gt;];
}&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
]]&gt;&lt;/style&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Já o unpack vai desempacotar o container em três partes: a versão, sempre a primeira variável, as variáveis da classe filha e um segundo container, contendo as variáveis da classe mãe. Este segundo container é passado para o unpack da classe mãe. &lt;/p&gt;
&lt;p&gt;Veja sua utilização, funcionando perfeitamente:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;    Employee    empregadoA;
    Employee    empregadoB;
    container   packed;
    ;
    
    empregadoA = &lt;span class="kwrd"&gt;new&lt;/span&gt; Employee();
    
    empregadoA.parmName(&lt;span class="str"&gt;"Jose"&lt;/span&gt;);
    empregadoA.parmBirthdate(1\5\1950);
    empregadoA.parmHiringDate(10\5\1980);
    packed = empregadoA.pack();
    empregadoA = &lt;span class="kwrd"&gt;null&lt;/span&gt;;
    
    empregadoB = &lt;span class="kwrd"&gt;new&lt;/span&gt; Employee();
    empregadoB.unpack(packed);

    print empregadoB.parmName();
    print empregadoB.parmBirthdate();
    print empregadoB.parmHiringDate();
    
    pause;&lt;/pre&gt;&lt;img src="http://eduardomiranda.net/blogs/dynamicsax/aggbug/239.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Eduardo Miranda</dc:creator>
            <guid>http://eduardomiranda.net/blogs/dynamicsax/archive/2008/02/09/239.aspx</guid>
            <pubDate>Sat, 09 Feb 2008 17:00:21 GMT</pubDate>
            <wfw:comment>http://eduardomiranda.net/blogs/dynamicsax/comments/239.aspx</wfw:comment>
            <comments>http://eduardomiranda.net/blogs/dynamicsax/archive/2008/02/09/239.aspx#feedback</comments>
            <wfw:commentRss>http://eduardomiranda.net/blogs/dynamicsax/comments/commentRss/239.aspx</wfw:commentRss>
            <trackback:ping>http://eduardomiranda.net/blogs/dynamicsax/services/trackbacks/239.aspx</trackback:ping>
        </item>
        <item>
            <title>Boas práticas para containers</title>
            <link>http://eduardomiranda.net/blogs/dynamicsax/archive/2008/01/18/232.aspx</link>
            <description>&lt;p&gt;SysDictCoder publicou um post sobre como utilizar melhor os containers: &lt;a title="Dealing with containers" target="_blank" href="http://sysdictcoder.com/blog/dealing-with-containers/"&gt;Dealing with containers&lt;/a&gt;. Como o próprio autor define "container é um poderoso, e um pouco estranho, tipo". Para mim pouco é gentileza dele, containers são muito estranhos. Um ponto que realmente me incomoda é a pouca legibilidade dos containers. É impossível saber o que tem dentro deles, quantos níveis, os tipos, nada. Você é obrigado a ler o código que criou um container para conseguir ler os dados dele. &lt;/p&gt;
&lt;p&gt;Existem diversas estrutura de dados no X++ e podem ser ótimas alternativas: &lt;a href="http://www.eduardomiranda.net/blogs/dynamicsax/archive/2007/03/19/cole-es-de-objetos-no-x-arrays.aspx"&gt;Arrays&lt;/a&gt;, &lt;a href="http://www.eduardomiranda.net/blogs/dynamicsax/archive/2007/03/19/cole-es-de-objetos-no-x-maps.aspx"&gt;Maps&lt;/a&gt;, &lt;a href="http://www.eduardomiranda.net/blogs/dynamicsax/archive/2007/03/22/cole-es-do-x-list.aspx"&gt;List&lt;/a&gt;, &lt;a href="http://www.eduardomiranda.net/blogs/dynamicsax/archive/2007/04/06/cole-es-do-x-set.aspx"&gt;Set&lt;/a&gt;, &lt;a href="http://www.eduardomiranda.net/blogs/dynamicsax/archive/2007/04/06/cole-es-do-x-recordsortedlist.aspx"&gt;RecordSortedList&lt;/a&gt;, dentre outros. Ou seja, estamos bem servidos de estrutura de dados. Se, ainda assim você precisar utilizar containers, sugiro a leitura acima. &lt;/p&gt;&lt;img src="http://eduardomiranda.net/blogs/dynamicsax/aggbug/232.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Eduardo Miranda</dc:creator>
            <guid>http://eduardomiranda.net/blogs/dynamicsax/archive/2008/01/18/232.aspx</guid>
            <pubDate>Fri, 18 Jan 2008 14:36:56 GMT</pubDate>
            <wfw:comment>http://eduardomiranda.net/blogs/dynamicsax/comments/232.aspx</wfw:comment>
            <comments>http://eduardomiranda.net/blogs/dynamicsax/archive/2008/01/18/232.aspx#feedback</comments>
            <wfw:commentRss>http://eduardomiranda.net/blogs/dynamicsax/comments/commentRss/232.aspx</wfw:commentRss>
            <trackback:ping>http://eduardomiranda.net/blogs/dynamicsax/services/trackbacks/232.aspx</trackback:ping>
        </item>
        <item>
            <title>Práticas e padrões: Métodos parmVariable()</title>
            <link>http://eduardomiranda.net/blogs/dynamicsax/archive/2008/01/18/231.aspx</link>
            <description>&lt;p&gt;Variáveis de classe no X++ não aceitam “access modifiers” e são sempre privadas. Ou seja, uma variável declarada no class declaration só acessivel dentro da própria classe. &lt;/p&gt;
&lt;p&gt;No C#, ou no VB, nós temos as propriedades para dar acesso externo as variáveis de classe. Já no Java é comum criar método GetVariavel e SetVariavel para isto. No X++ o padrão é criar um método parmVariavel que oferece ao mesmo tempo acesso de leitura e de escrita na variável. &lt;/p&gt;
&lt;p&gt;A classe Person abaixo, por exemplo, tem duas variáveis de classe: name e birthDate&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;class&lt;/span&gt; Person
{
    str     name;
    date    birthdate;
}&lt;/pre&gt;
&lt;style type="text/css"&gt;&lt;![CDATA[csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #eeeeee;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
]]&gt;&lt;/style&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Se preciso expor estas variáveis para o mundo externo, faço um método parm como este:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; str parmName(str _name = name)
{;
    name = _name;
    
    &lt;span class="kwrd"&gt;return&lt;/span&gt; name;
}&lt;/pre&gt;
&lt;p&gt;O método recebe como parâmetro opcional o nome. Cujo o valor default é o nome atual. Veja como é fácil, alterar e ler a variável:&lt;/p&gt;
&lt;pre class="csharpcode"&gt;    Person person;
    ;

    person = &lt;span class="kwrd"&gt;new&lt;/span&gt; Person(&lt;span class="str"&gt;'José'&lt;/span&gt;, 21\2\1998);
    person.parmName(&lt;span class="str"&gt;'João'&lt;/span&gt;);

    print person.parmName();
    pause;&lt;/pre&gt;
&lt;p&gt;Mesmo inicializando o objeto com o nome ‘José’, consigo alterar o nome para ‘João’. Agora em alguns casos não quero permitir alteração da variável, apenas leitura. Veja por exemplo o método parmAge, ele retorna a idade, baseado na data de nascimento. Só que não recebe parâmetro, pois esta é uma variável que não quero alterar.&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; parmAge()
{;
    &lt;span class="kwrd"&gt;return&lt;/span&gt; yearDiff(today(), birthDate);
}&lt;/pre&gt;
&lt;p&gt;A leitura do método continua normal.&lt;/p&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Job1(Args _args)
{
    Person person;
    ;

    person = &lt;span class="kwrd"&gt;new&lt;/span&gt; Person(&lt;span class="str"&gt;'José'&lt;/span&gt;, 21\2\1998);
    person.parmName(&lt;span class="str"&gt;'João'&lt;/span&gt;);

    print person.parmName();
    print person.parmAge();
    pause;
}&lt;/pre&gt;
&lt;p&gt;Como boa prática só devemos expor as variáveis somente quando realmente precisamos usar. Neste &lt;a href="http://www.eduardomiranda.net/blogs/dotnet/archive/2007/05/25/timidez-uma-otima-caracteristica-para-o-seu-codigo-fonte.aspx"&gt;post&lt;/a&gt; explico um pouco melhor o porque desta prática. &lt;/p&gt;&lt;img src="http://eduardomiranda.net/blogs/dynamicsax/aggbug/231.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Eduardo Miranda</dc:creator>
            <guid>http://eduardomiranda.net/blogs/dynamicsax/archive/2008/01/18/231.aspx</guid>
            <pubDate>Fri, 18 Jan 2008 14:03:44 GMT</pubDate>
            <wfw:comment>http://eduardomiranda.net/blogs/dynamicsax/comments/231.aspx</wfw:comment>
            <comments>http://eduardomiranda.net/blogs/dynamicsax/archive/2008/01/18/231.aspx#feedback</comments>
            <wfw:commentRss>http://eduardomiranda.net/blogs/dynamicsax/comments/commentRss/231.aspx</wfw:commentRss>
            <trackback:ping>http://eduardomiranda.net/blogs/dynamicsax/services/trackbacks/231.aspx</trackback:ping>
        </item>
        <item>
            <title>Acessando dados via X++</title>
            <link>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/10/16/193.aspx</link>
            <description>&lt;p&gt;Uma das grandes vantagens do X++ como linguagem para um ERP são suas funcionalidades de acesso a dados integradas a própria linguagem. Isto simplifica bastante o acesso à camada de dados. Mesmo assim existem muitas formas de alcançar os mesmos objetivos, umas mais adequadas que outras. Neste post vou analisar algumas destas alternativas. &lt;/p&gt;
&lt;p&gt;A estrutura mais utilizada de acesso aos dados de uma tabela é o while select. N exemplo abaixo seleciono e processo todos os clientes, contidos na tabela CustTable, que pertencem ao grupo “INTER-PR”. &lt;/p&gt;
&lt;div class="CodeFormatContainer"&gt;
&lt;pre class="csharpcode"&gt;    CustTable customer;
    ;

    &lt;span class="kwrd"&gt;while&lt;/span&gt; &lt;span class="kwrd"&gt;select&lt;/span&gt; customer
        &lt;span class="kwrd"&gt;where&lt;/span&gt; customer.CustGroup == &lt;span class="str"&gt;"INTER-PR"&lt;/span&gt;
    {
        print strfmt(&lt;span class="str"&gt;"Id: %1 - Name: %2"&lt;/span&gt;, customer.AccountNum, customer.Name);
    }

    pause;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Estendendo este código posso trazer as vendas abertas para estes clientes. O exemplo abaixo mostra isto, porém de uma forma pouco performática. Para cada cliente você faz um novo select na tabela de pedidos. Para uma base de 500 clientes, são 501 acessos ao banco de dados. &lt;/p&gt;
&lt;div class="CodeFormatContainer"&gt;
&lt;pre class="csharpcode"&gt;    CustTable customer;
    SalesTable sales;
    ;

    &lt;span class="kwrd"&gt;while&lt;/span&gt; &lt;span class="kwrd"&gt;select&lt;/span&gt; customer
        &lt;span class="kwrd"&gt;where&lt;/span&gt; customer.CustGroup == &lt;span class="str"&gt;"INTER-PR"&lt;/span&gt;
    {
        print strfmt(&lt;span class="str"&gt;"Id: %1 - Name: %2"&lt;/span&gt;, customer.AccountNum, customer.Name);

        &lt;span class="kwrd"&gt;while&lt;/span&gt; select sales
            &lt;span class="kwrd"&gt;where&lt;/span&gt; sales.CustAccount == customer.AccountNum
        {

            print strfmt(&lt;span class="str"&gt;"  Sales: %1 - amount to be invoiced: %2"&lt;/span&gt;, sales.SalesId, sales.amountOrderedNotInvoiced());
        }
    }

    pause;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;O código abaixo mostra uma solução usando um join entre as duas tabelas. O resultado é o mesmo, mas o acesso ao banco de dados é feito de uma vez. O que torna a solução mais performática. &lt;/p&gt;
&lt;div class="CodeFormatContainer"&gt;
&lt;pre class="csharpcode"&gt;    CustTable customer;
    SalesTable sales;
    CustAccount currentCustomer;
    ;

    &lt;span class="kwrd"&gt;while select&lt;/span&gt; customer
        &lt;span class="kwrd"&gt;where&lt;/span&gt; customer.CustGroup == &lt;span class="str"&gt;"INTER-PR"&lt;/span&gt;
        join sales
            &lt;span class="kwrd"&gt;where&lt;/span&gt; customer.AccountNum == sales.CustAccount
    {
        &lt;span class="kwrd"&gt;if&lt;/span&gt; (customer.AccountNum != currentCustomer)
        {
            print strfmt(&lt;span class="str"&gt;"Id: %1 - Name: %2"&lt;/span&gt;, customer.AccountNum, customer.Name);
            currentCustomer = customer.AccountNum;
        }

        print strfmt(&lt;span class="str"&gt;"  Sales: %1 - amount to be invoiced: %2"&lt;/span&gt;, sales.SalesId, sales.amountOrderedNotInvoiced());
    }

    pause;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Outro problema muito comum é listar todos os clientes que têm ordens de venda em aberto. Uma solução que pode parecer razoável é esta abaixo. Para cada cliente verifico se existe uma ordem aberta para ele. &lt;/p&gt;
&lt;div class="CodeFormatContainer"&gt;
&lt;pre class="csharpcode"&gt;    CustTable customer;
    ;

    print &lt;span class="str"&gt;"Clients with open orders:"&lt;/span&gt;;
    &lt;span class="kwrd"&gt;while select&lt;/span&gt; customer
    {
        &lt;span class="kwrd"&gt;if&lt;/span&gt; (SalesTable::existCustOpenOrder(customer.AccountNum))
        {
            print strfmt(&lt;span class="str"&gt;"  Id: %1 - Name: %2"&lt;/span&gt;, customer.AccountNum, customer.Name);
        }
    }

    pause;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Existem dois pontos fracos nesta solução: O primeiro select traz todos os clientes, sem filtrar nada. Depois disto, para cada cliente, um select é realizado na tabela de ordens de venda. Novamente um join é o mais adequado para este caso. Porém, neste caso, o ideal é fazer um tipo especial de join, que o exists join. &lt;/p&gt;
&lt;div class="CodeFormatContainer"&gt;
&lt;pre class="csharpcode"&gt;    CustTable customer;
    SalesTable sales;
    ;

    print &lt;span class="str"&gt;"Clients with open orders:"&lt;/span&gt;;
    &lt;span class="kwrd"&gt;while select&lt;/span&gt; customer
        exists join sales
        &lt;span class="kwrd"&gt;where&lt;/span&gt; customer.AccountNum == sales.CustAccount
    {
        print strfmt(&lt;span class="str"&gt;"  Id: %1 - Name: %2"&lt;/span&gt;, customer.AccountNum, customer.Name);
    }

    pause;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Neste caso, o exists join tem uma grande vantagem sobre um join comum: Não traz todas as ordens de vendas de cada cliente. Com isto o resultado do select é menor (leia-se: quantas linhas retornam). Além disto, a instância da tabela de ordens de compras não é carregada, economizando memória. &lt;/p&gt;
&lt;p&gt;Este tipo de join tem seu inverso, que é o notexists. O comando também é conhecido como “minus” em SQL, pois trazem as linhas da tabela de cliente MENOS os clientes que estão na tabela de ordens de venda. Este é o resultado do código abaixo. &lt;/p&gt;
&lt;div class="CodeFormatContainer"&gt;
&lt;pre class="csharpcode"&gt;    CustTable customer;
    SalesTable sales;
    ;

    print &lt;span class="str"&gt;"Clients without open orders:"&lt;/span&gt;;
    &lt;span class="kwrd"&gt;while select&lt;/span&gt; customer
        notexists join sales
        &lt;span class="kwrd"&gt;where&lt;/span&gt; customer.AccountNum == sales.CustAccount
    {
        print strfmt(&lt;span class="str"&gt;"  Id: %1 - Name: %2"&lt;/span&gt;, customer.AccountNum, customer.Name);
    }

    pause;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Utilizando o notexists temos que tomar o seguinte cuidado. As cláusulas where referentes à tabela de clientes devem ser escritas logo abaixo do select. Já as cláusulas referentes ao join entre as tabelas ficam no final do comando. Se eu quiser, por exemplo, filtrar os clientes que estão em São Paulo, tenho a query abaixo. Dois where no mesmo select não é comum em SQL, mas a leitura é bem intuitiva: Quero buscar os clientes que estão em São Paulo (primeiro select) e que não têm ordens de venda (o join). &lt;/p&gt;
&lt;div class="CodeFormatContainer"&gt;
&lt;pre class="csharpcode"&gt;    CustTable customer;
    SalesTable sales;
    ;

    print &lt;span class="str"&gt;"Clients without open orders:"&lt;/span&gt;;
    &lt;span class="kwrd"&gt;while&lt;/span&gt; select customer
        &lt;span class="kwrd"&gt;where&lt;/span&gt; customer.State == &lt;span class="str"&gt;"SP"&lt;/span&gt; &lt;span class="rem"&gt;//Neste where ficam as cláusulas referentes a customer&lt;/span&gt;
        notexists join sales
        &lt;span class="kwrd"&gt;where&lt;/span&gt; customer.AccountNum == sales.CustAccount &lt;span class="rem"&gt;//Neste ficam as cláusulas do join&lt;/span&gt;
    {
        print strfmt(&lt;span class="str"&gt;"  Id: %1 - Name: %2"&lt;/span&gt;, customer.AccountNum, customer.Name);
    }

    pause;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Se você tentar colocar a cláusula do cliente no final, junto com as outras, verá que o resultado é o inverso, a query trará os cliente que não estão em São Paulo, pois a cláusula está relacionada ao notexists. &lt;/p&gt;
&lt;p&gt;Como conclusão, seguem algumas dicas para identificar qual a melhor solução: &lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Se dentro do select while você está fazendo testes com campos da tabela ou buscando informações de outras tabelas, também usando informações da tabela, verifique se um join não pode resolver este problema. &lt;/li&gt;
    &lt;li&gt;Se você está fazendo um join, mas a segunda tabela só é utilizada como filtro para o select, verifique se é possível utilizar o comando exists ou notexists &lt;/li&gt;
&lt;/ul&gt;&lt;img src="http://eduardomiranda.net/blogs/dynamicsax/aggbug/193.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Eduardo Miranda</dc:creator>
            <guid>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/10/16/193.aspx</guid>
            <pubDate>Tue, 16 Oct 2007 15:53:13 GMT</pubDate>
            <wfw:comment>http://eduardomiranda.net/blogs/dynamicsax/comments/193.aspx</wfw:comment>
            <comments>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/10/16/193.aspx#feedback</comments>
            <wfw:commentRss>http://eduardomiranda.net/blogs/dynamicsax/comments/commentRss/193.aspx</wfw:commentRss>
            <trackback:ping>http://eduardomiranda.net/blogs/dynamicsax/services/trackbacks/193.aspx</trackback:ping>
        </item>
        <item>
            <title>Debugando aplica&amp;ccedil;&amp;otilde;es em AX</title>
            <link>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/07/02/163.aspx</link>
            <description>&lt;p&gt;Ótimas dicas de como debugar de forma mais eficiente no Dynamics AX neste &lt;a href="http://sysdictcoder.wordpress.com/2007/07/01/10-tips-for-debugging-in-dynamics-ax/" target="_blank"&gt;post&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://eduardomiranda.net/blogs/dynamicsax/aggbug/163.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Eduardo Miranda</dc:creator>
            <guid>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/07/02/163.aspx</guid>
            <pubDate>Mon, 02 Jul 2007 14:26:04 GMT</pubDate>
            <wfw:comment>http://eduardomiranda.net/blogs/dynamicsax/comments/163.aspx</wfw:comment>
            <comments>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/07/02/163.aspx#feedback</comments>
            <wfw:commentRss>http://eduardomiranda.net/blogs/dynamicsax/comments/commentRss/163.aspx</wfw:commentRss>
            <trackback:ping>http://eduardomiranda.net/blogs/dynamicsax/services/trackbacks/163.aspx</trackback:ping>
        </item>
        <item>
            <title>Estudando para a Certifica&amp;ccedil;&amp;atilde;o</title>
            <link>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/06/26/159.aspx</link>
            <description>&lt;p&gt;O Carlos Nobrega me enviou um email perguntando sobre material disponível para estudo para a segunda prova de certificação de desenvolvimento no Dynamics AX. Segue a minha resposta abaixo, caso alguém mais tenha interesse.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Carlos,&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;Não existe muito material disponível na internet sobre AX, como você deve ter percebido (se comparado com .Net ou Java). &lt;/p&gt;&lt;p&gt;Se a empresa que você trabalha tem acesso ao PartnerSource você pode pegar o material dos cursos. &lt;/p&gt;&lt;p&gt;Este link apresenta o que cairá na prova (provavelmente vc já conhece): &lt;/p&gt;&lt;p&gt;&lt;a href="https://mbs.microsoft.com/partnersource/communities/training/Certifications/exampreparation/AX4Morph_prep.htm"&gt;https://mbs.microsoft.com/partnersource/communities/training/Certifications/exampreparation/AX4Morph_prep.htm &lt;/a&gt; &lt;/p&gt;&lt;p&gt;Você pode pegar o material dos cursos 1 a 4 &lt;/p&gt;&lt;p&gt;&lt;a href="https://mbs.microsoft.com/partnersource/communities/training/trainingmaterials/student/course8623"&gt;https://mbs.microsoft.com/partnersource/communities/training/trainingmaterials/student/course8623&lt;/a&gt; &lt;/p&gt;&lt;p&gt;&lt;a href="https://mbs.microsoft.com/partnersource/communities/training/trainingmaterials/student/course8633"&gt;https://mbs.microsoft.com/partnersource/communities/training/trainingmaterials/student/course8633&lt;/a&gt; &lt;/p&gt;&lt;p&gt;&lt;a href="https://mbs.microsoft.com/partnersource/communities/training/trainingmaterials/student/course8645"&gt;https://mbs.microsoft.com/partnersource/communities/training/trainingmaterials/student/course8645&lt;/a&gt; &lt;/p&gt;&lt;p&gt;&lt;a href="https://mbs.microsoft.com/partnersource/communities/training/trainingmaterials/student/course8643"&gt;https://mbs.microsoft.com/partnersource/communities/training/trainingmaterials/student/course8643&lt;/a&gt; &lt;/p&gt;&lt;p&gt;  &lt;/p&gt;&lt;p&gt;Neste link você pode procurar e baixar o material de qq curso: &lt;/p&gt;&lt;p&gt;&lt;a href="https://mbs.microsoft.com/partnersource/communities/training/trainingmaterials/student"&gt;https://mbs.microsoft.com/partnersource/communities/training/trainingmaterials/student&lt;/a&gt; &lt;/p&gt;&lt;p&gt;  &lt;/p&gt;&lt;p&gt;Para X++ tem o livro do Luis Mourão que, apesar de ser sobre o AX 3.0, é o mais completo em relação a linguagem. Talvez você ache o capítulo de X++ disponível na internet. &lt;/p&gt;&lt;p&gt;&lt;a href="http://www.amazon.com/Dynamics-AX-Guide-Microsoft-Axapta/dp/1590594894"&gt;http://www.amazon.com/Dynamics-AX-Guide-Microsoft-Axapta/dp/1590594894&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Boa sorte,&lt;/p&gt;&lt;/blockquote&gt;&lt;img src="http://eduardomiranda.net/blogs/dynamicsax/aggbug/159.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Eduardo Miranda</dc:creator>
            <guid>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/06/26/159.aspx</guid>
            <pubDate>Tue, 26 Jun 2007 16:48:27 GMT</pubDate>
            <wfw:comment>http://eduardomiranda.net/blogs/dynamicsax/comments/159.aspx</wfw:comment>
            <comments>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/06/26/159.aspx#feedback</comments>
            <wfw:commentRss>http://eduardomiranda.net/blogs/dynamicsax/comments/commentRss/159.aspx</wfw:commentRss>
            <trackback:ping>http://eduardomiranda.net/blogs/dynamicsax/services/trackbacks/159.aspx</trackback:ping>
        </item>
        <item>
            <title>Novo blog sobre X++</title>
            <link>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/06/25/158.aspx</link>
            <description>&lt;p&gt;Através do blog o &lt;a target="_blank" href="http://blogs.msdn.com/mfp/"&gt;Michael&lt;/a&gt; descobri que o time do compilador X++ criou um &lt;a target="_blank" href="http://blogs.msdn.com/x/"&gt;novo blog&lt;/a&gt; e já está escrevendo.&lt;/p&gt;&lt;img src="http://eduardomiranda.net/blogs/dynamicsax/aggbug/158.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Eduardo Miranda</dc:creator>
            <guid>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/06/25/158.aspx</guid>
            <pubDate>Mon, 25 Jun 2007 20:15:56 GMT</pubDate>
            <wfw:comment>http://eduardomiranda.net/blogs/dynamicsax/comments/158.aspx</wfw:comment>
            <comments>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/06/25/158.aspx#feedback</comments>
            <wfw:commentRss>http://eduardomiranda.net/blogs/dynamicsax/comments/commentRss/158.aspx</wfw:commentRss>
            <trackback:ping>http://eduardomiranda.net/blogs/dynamicsax/services/trackbacks/158.aspx</trackback:ping>
        </item>
        <item>
            <title>Utilizando maps para reaproveitamento de código</title>
            <link>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/05/24/133.aspx</link>
            <description>&lt;p&gt; &lt;/p&gt;
&lt;p&gt;O Dynamics AX tem um objeto não muito comum em linguagens OO, mas que permite mapear suas Tables com um funcionamento semelhante ao de uma interface. Vou mostrar um exemplo simples como aplicar esta funcionalidade. &lt;/p&gt;
&lt;p&gt;A SalesTable, que contém as ordens de venda, e a PurchTable, que contém as ordens de compra, têm alguns campos em comum e podem ter algumas funcionalidades em comum. Por isto existe no sistema um Map, chamado SalesPurchTable, que mapeia alguns campos destas tabelas e pode ser usado em métodos cuja regra de negócio é comum as duas tabelas. &lt;/p&gt;
&lt;p&gt;&lt;img height="504" width="400" alt="" src="/blogs/images/eduardomiranda_net/blogs/dynamicsax/10/Maps_AOT.gif" /&gt;&lt;/p&gt;
&lt;p&gt;Como é possível ver na imagem um Map é composto de campos, grupos de campos, métodos e mappings. Estes mapeiam cada campo do Map ao campo correspondente da tabela. Você pode perceber que o map SalesPurchTable mapeia não só a SalesTable e PurchTable, mas também SalesBasket, SalesQuotationBasket e SalesQuotationTable. &lt;/p&gt;
&lt;p&gt;Uma forma interessante de utilizar o Map é evitar a duplicação de código, quando existe uma funcionalidade igual, ou muito parecida, mas que usam diferentes tabelas. &lt;/p&gt;
&lt;p&gt;O código de exemplo abaixo implementa uma classe e um método printOrderDetails. O método recebe como parâmetro um map SalesPurchTable. O método é bem simples, imprime alguns campos da ordem. O importante é que ele pode receber tanto objetos do tipo SalesTable como do tipo PurchTable que o seu comportamento será igual. &lt;/p&gt;
&lt;div class="CodeFormatContainer"&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; printOrderDetails(SalesPurchTable _table)
{;

    print _table.SalesPurchId;
    print _table.OrderAccount;
    print _table.CashDisc;
    
    pause;
}&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;O job abaixo executa o nosso método passando um SalesTable e em seguida um PurchTable e o comportamento é igual.&lt;/p&gt;
&lt;div class="CodeFormatContainer"&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Job11(Args _args)
{
    SalesTable salesTable;
    PurchTable purchTable;
    ;

    &lt;span class="rem"&gt;//Trocar o parâmetro para uma id de Sales Order válido&lt;/span&gt;
    salesTable = SalesTable::find(&lt;span class="str"&gt;'000004'&lt;/span&gt;); 
    &lt;span class="rem"&gt;//Trocar o parâmetro para uma id de Purchase Order válido&lt;/span&gt;
    purchTable = PurchTable::find(&lt;span class="str"&gt;'000001'&lt;/span&gt;); 
    
    MapSample::printOrderDetails(salesTable);
    MapSample::printOrderDetails(purchTable);
}&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Quem conhece um pouco mais de X++ sabe que posso obter um resultado semelhante com o tipo common, pois ele aceita qualquer objeto. Mas a solução utilizando Map tem uma grande vantagem: Ela é Type safe. Veja na imagem abaixo o que acontece se tento passar um objeto do tipo SalesLine para o método. Ocorre um erro em tempo de compilação, pois não existe mapping de SalesLine para SalesPurchTable. Utilizando o common este mesmo erro ocorreria em tempo de execução, o que é muito pior. &lt;/p&gt;
&lt;p&gt;&lt;img height="380" width="542" alt="" src="/blogs/images/eduardomiranda_net/blogs/dynamicsax/10/Maps_CompileError.gif" /&gt;&lt;/p&gt;
&lt;p&gt;Se durante a execução do código você precise saber qual tipo da tabela recebida ainda assim é possível, como você pode ver no código abaixo. &lt;/p&gt;
&lt;p&gt;O que você NÃO deve fazer é criar um método que recebe um map, mas no final das contas tem um comportamento completamente distinto para cada tipo de tabela. Neste caso o seu método fica muito grande, com muitas responsabilidades e confuso para os outros desenvolvedores lerem. &lt;/p&gt;
&lt;div class="CodeFormatContainer"&gt;
&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; printOrderDetails(SalesPurchTable _table)
{;

    &lt;span class="kwrd"&gt;switch&lt;/span&gt; (_table.TableId)
    {
        &lt;span class="kwrd"&gt;case&lt;/span&gt; TableNum(SalesTable):
            print &lt;span class="str"&gt;"Sales Order"&lt;/span&gt;;
            &lt;span class="kwrd"&gt;break&lt;/span&gt;;
            
        &lt;span class="kwrd"&gt;case&lt;/span&gt; TableNum(PurchTable):
            print &lt;span class="str"&gt;"Purchase Order"&lt;/span&gt;;
            &lt;span class="kwrd"&gt;break&lt;/span&gt;;
            
        &lt;span class="kwrd"&gt;default&lt;/span&gt;:
            print &lt;span class="str"&gt;"Order"&lt;/span&gt;;
            &lt;span class="kwrd"&gt;break&lt;/span&gt;;
    }

    print _table.SalesPurchId;
    print _table.OrderAccount;
    print _table.CashDisc;
    
    pause;
}&lt;/pre&gt;
&lt;/div&gt;&lt;img src="http://eduardomiranda.net/blogs/dynamicsax/aggbug/133.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Eduardo Miranda</dc:creator>
            <guid>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/05/24/133.aspx</guid>
            <pubDate>Thu, 24 May 2007 20:22:13 GMT</pubDate>
            <wfw:comment>http://eduardomiranda.net/blogs/dynamicsax/comments/133.aspx</wfw:comment>
            <comments>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/05/24/133.aspx#feedback</comments>
            <wfw:commentRss>http://eduardomiranda.net/blogs/dynamicsax/comments/commentRss/133.aspx</wfw:commentRss>
            <trackback:ping>http://eduardomiranda.net/blogs/dynamicsax/services/trackbacks/133.aspx</trackback:ping>
        </item>
        <item>
            <title>Links interessantes</title>
            <link>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/05/24/131.aspx</link>
            <description>&lt;p&gt;Existem alguns blogs que falam sobre AX por ai, não muitos. Seguem alguns posts que achei interessantes recentemente:&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;a href="http://axaptafreak.blogspot.com/2007/03/send-message-to-online-user-in-dynamics.html" target="_blank"&gt;Send message to online user in Dynamics AX 4.0 (quick &amp;amp; dirty)&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="http://axaptafreak.blogspot.com/2007/03/ax-40-sp1-new-clustering-functionality.html" target="_blank"&gt;Ax 4.0 SP1: new clustering functionality&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="http://dynamics-ax.blogspot.com/2007/05/microsoft-dynamics-vs-sap-erp-end-user.html" target="_blank"&gt;Microsoft Dynamics vs SAP ERP End-User Business Productivity Study&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="http://dynamics-ax.blogspot.com/2007/04/dax-40-filter-by-grid-update.html" target="_blank"&gt;DAX 4.0 Filter by Grid&lt;/a&gt; &lt;/li&gt; &lt;li&gt;&lt;a href="http://blogs.msdn.com/edglas/archive/2007/02/17/dynamics-ax-performance-workbench-coming-soon.aspx" target="_blank"&gt;Dynamics AX Performance Workbench coming soon&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="http://blogs.msdn.com/ukax/archive/2007/05/11/dynamics-ax-hardware-sizing-guides.aspx" target="_blank"&gt;Dynamics AX Hardware Sizing Guides&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="http://blogs.msdn.com/ukax/archive/2007/05/10/microsoft-dynamics-ax-snaps-on-the-ax-4-sp1-vpc-image.aspx" target="_blank"&gt;Microsoft Dynamics AX Snaps on the AX 4 SP1 VPC image&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="http://blogs.msdn.com/palle_agermark/archive/2007/04/24/what-is-mst.aspx" target="_blank"&gt;What is MST?&lt;/a&gt; &lt;/li&gt;&lt;/ul&gt;&lt;img src="http://eduardomiranda.net/blogs/dynamicsax/aggbug/131.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Eduardo Miranda</dc:creator>
            <guid>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/05/24/131.aspx</guid>
            <pubDate>Thu, 24 May 2007 13:53:31 GMT</pubDate>
            <wfw:comment>http://eduardomiranda.net/blogs/dynamicsax/comments/131.aspx</wfw:comment>
            <comments>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/05/24/131.aspx#feedback</comments>
            <wfw:commentRss>http://eduardomiranda.net/blogs/dynamicsax/comments/commentRss/131.aspx</wfw:commentRss>
            <trackback:ping>http://eduardomiranda.net/blogs/dynamicsax/services/trackbacks/131.aspx</trackback:ping>
        </item>
        <item>
            <title> Tips &amp; Tricks - Label lookup  </title>
            <link>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/04/23/tips-tricks-label-lookup.aspx</link>
            <description>&lt;p&gt;É uma boa prática utilizar labels ao invés de textos "hard coded" no AX. No entanto muitas vezes a leitura do código-fonte fica prejudicada. Qual a mensagem que o usuário verá se este trecho de código for executado?&lt;/p&gt;
&lt;p&gt;A boa notícia é que existe um atalho para você ir direto para o label escrito no código. Basta marcar o label, clicar com o botão direito do mouse e selecionar "Lookup Label/Text"&lt;/p&gt;
&lt;p&gt;&lt;img height="338" alt="" width="394" src="http://eduardomiranda.net/blogs/images/eduardomiranda_net/blogs/dynamicsax/10/o_1000.7.131.contextLookupLabel.gif" /&gt;&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt; Label editor se abrirá já com o label selecionado marcado, permitindo inclusive a alteração do label.&lt;/p&gt;
&lt;p&gt;&lt;img height="273" alt="" width="389" src="http://eduardomiranda.net/blogs/images/eduardomiranda_net/blogs/dynamicsax/10/o_1000.7.132.labelEditor.gif" /&gt;&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;&lt;img src="http://eduardomiranda.net/blogs/dynamicsax/aggbug/19.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Eduardo Miranda</dc:creator>
            <guid>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/04/23/tips-tricks-label-lookup.aspx</guid>
            <pubDate>Mon, 23 Apr 2007 17:29:05 GMT</pubDate>
            <wfw:comment>http://eduardomiranda.net/blogs/dynamicsax/comments/19.aspx</wfw:comment>
            <comments>http://eduardomiranda.net/blogs/dynamicsax/archive/2007/04/23/tips-tricks-label-lookup.aspx#feedback</comments>
            <wfw:commentRss>http://eduardomiranda.net/blogs/dynamicsax/comments/commentRss/19.aspx</wfw:commentRss>
            <trackback:ping>http://eduardomiranda.net/blogs/dynamicsax/services/trackbacks/19.aspx</trackback:ping>
        </item>
    </channel>
</rss>