Tuesday, February 9, 2010

Error 1075: The dependency service does not exist or has been marked for deletion

I do not know why but my Microsoft Windows Server 2003 SP2 unexpectedly refused to restart AOS instance. It gave the error:

Error 1075: The dependency service does not exist or has been marked for deletion.

It is strange because the only thing in the dependcies was Remote Procedure Call (RPC). No events in the logs.

I deinstall the AOS instance and re-install it  with SP2 right away with the same settings without restarting the server. Now it starts fine.

Monday, February 8, 2010

Error: Multiple calls to CodeAccessPermission.Assert

Multiple calls to CodeAccessPermission.Assert
(S)\Classes\SkipAOSValidationPermission\assert
(S)\Classes\BatchRun\runJob - line 166
(S)\Classes\BatchRun\do - line 54
(C)\Forms\BatchRun\Methods\doBatch - line 18

Sometimes I got this error while working with Change based alerts batch processing. After that the status of the related job in batch list changed to Executing, and the only way to get rid of it was deleting that job.


This issue arises in [Classes]BatchRun.runJob method because of absence of closing revertAssert() method in case of exceptions after calling runas() method.

So, my solution is to comment the existing call of revertAssert()...

if (batchClass.runsImpersonated())
        {
            // Ok to assert here because the user name comes from
            // the batch table
            runAsPermission = new RunAsPermission(batch.CreatedBy);
            runAsPermission.assert();
            // BP Deviation Documented
            runas(batch.CreatedBy,
                classnum(BatchRun),
                staticmethodstr(BatchRun, runJobStatic),
                [batchId]);

           // Alexey Voytsekhovskiy (SIS) (2010/02/08) (Demande #0074)
           // CodeAccessPermission::revertAssert();
        }
        else
        {
            BatchRun::runJobStatic([batchId]);
        }

and move it after catching all kind of exceptions:


catch (Exception::UpdateConflictNotRecovered)
    {
        isErrorCaught   = true;
        exception       = Exception::UpdateConflictNotRecovered;
    }

    // Alexey Voytsekhovskiy (SIS) (2010/02/08) (Demande #0074)
    // in case of exception Permssion should be revert anyway!!
    CodeAccessPermission::revertAssert();

It allows to avoid multiple calls to CodeAccessPermission.Assert (there is another call far in this method) and see the log of the job ended with an error.


By the way, here is a short and sweet trick with multiple calls if needed.

Tuesday, January 5, 2010

MorphX IT by Steen Andreasen

MORPHX IT by Steen Andreasen helps me a lot. I would like to have it at hand.

Just to remind the link to this very useful book in English.

There are the Russian and Danish versions for free downloading in PDF format.

Monday, December 21, 2009

Phone Number Formatting Mask

It is pity but AX does not have the mask functionality on StringEdit fields. (versions 3.x- 4.x at least)

I changed the standard functionality for Phone field of Customers form so that the input phone number will be formatted as (xxx) xxx-xxxx[x]

For example, if one input 1234567890 it will be presented and saved as (123) 456-7890

===>

For 12fs3.45*6.78--90 it will be presented and saved as (123) 456-7890 with no non-numericals.

12345678901234567 will be as (123) 456-7890123456 it does not truncate the tail.

If finally it does not look like (xxx) xxx-xxxx the system alerts the user about that however the input value will be saved.



The following methods were added/changed:

ClassDeclaration of Customers form

public class FormRun extends ObjectRun
{
...

boolean sisValidateCalled;
}


StringEdit Phone field methods:

public void enter()
{
super();
sisValidateCalled = false;
}

public boolean validate()
{
#define.CorrectPhoneLettersNumber(14)
boolean ret;
int length;
Phone newPhone;
;
ret = super();

// creates new phone number in the format (xxx) xxx-xxxx[x]
newPhone = SISTools::formatPhoneNumber(this.text());
length = strlen(newPhone);

if (length != #CorrectPhoneLettersNumber)
checkFailed(strfmt("Phone numbers should be like: (xxx) xxx-xxxx"));

CustTable.Phone = newPhone;
CustTable_ds.write();
sisValidateCalled = true;
return ret;
}
public boolean leave()
{
boolean ret;

ret = super();

if (!sisValidateCalled)
this.validate();

return ret;
}

SISTools class (some collection of utilities)

// creates new phone number in the format (xxx) xxx-xxxx[x]
static public Phone formatPhoneNumber(Phone _phone = "")
{
Phone newPhone = "";
str char;
int length = strlen(_phone);
int i;
container numbers = ['0','1','2','3','4','5','6','7','8','9'];
;
// remove all non numbers from field text
for (i=1; i<=length; i++)
{
char = substr(_phone,i,1);
if (confind(numbers,char))
{
newPhone = newPhone + char;
}
}
length = strlen(newPhone);

// create new phone number in the format (xxx) xxx-xxxx from 1234567890
newPhone = "(" + substr(newPhone,1,3) + ") " + substr(newPhone,4,3) + "-" + substr(newPhone,7, length-6);

return newPhone;
}

Inspired by Sonny Wibaba Adi

Friday, December 4, 2009

Flush Events related tables quickly

Quick job to delete all records from Events related tables. Inspired by this posting of Nitesh Ranjan.

static void FlushEventInbox(Args _args)
{
/*
To implement Alert functionality, Dynamics AX uses following tables:
EventParameters
EventCompanyRule
EventCUD

EventInbox
|
|- EventInboxData
|- EventRule
|
|-EventRuleData
|-EventRuleField
|-EventRuleIgnore
|-EventRuleIgnoreAggregation
|-EventRuleRel
|
|-EventRuleRelData
*/
EventRuleRelData EventRuleRelData;
EventRuleRel EventRuleRel;
EventRuleIgnoreAggregation EventRuleIgnoreAggregation;
EventRuleIgnore EventRuleIgnore;
EventRuleField EventRuleField;
EventRuleData EventRuleData;
EventRule EventRule;
EventInboxData EventInboxData;
EventInbox EventInbox;
;
if (Box::okCancel("Flush all Events related table?", DialogButton::Cancel, "Confirm deletion",
"Delete all records from: delete_from EventRuleRelData, EventRuleRel,"
+" EventRuleIgnoreAggregation, EventRuleIgnore, EventRuleField, EventRuleData,"
+" EventRule, EventInboxData, EventInbox") == DialogButton::Ok)
{
delete_from EventRuleRelData;
delete_from EventRuleRel;
delete_from EventRuleIgnoreAggregation;
delete_from EventRuleIgnore;
delete_from EventRuleField;
delete_from EventRuleData;
delete_from EventRule;
delete_from EventInboxData;
EventInbox.skipDeleteMethod(true);
EventInbox.skipDeleteActions(true);
delete_from EventInbox;
}

}

Tuesday, December 1, 2009

How to add all descendant classes to a new project

I bumped into the problem of a class compilation with no licence for X++ source code.

Forward compile option is not enough to make my changes working. Thus I need to export-import all descendant classes as well as the class I changed - FormLetter in my case.

I therefore have to add all these classes to my project. Natural laziness saved me again from this manual work.

I hope this short job inspired by system class SysCompilerOutput and miklenew's job from AXForum will help you in similar situations.






// add to a new project all descendant classes for forward compilation
public static void SISCreateCompileForwardProject(Args _args)
{
#AOT
str project = 'SIS_CompileForward';
SysCompilerOutput sysCompilerOutput;
Dictionary dictionary = new Dictionary();
DictClass dictClass = new DictClass(className2Id("Formletter"));
int numOfClasses = dictionary.classCnt();
ProjectNode sharedProjects;
ProjectNode newProject;

void addToProjectForwardClass(DictClass _dictClass, Dictionary _dictionary, int _numOfClasses)
{
ClassNode classNode;
DictClass dictClassLoop;
DictClass childClass;
int i;
;
if (_dictClass)
{
classNode = infolog.findNode(#ClassesPath + #AOTDelimiter + _dictClass.name());

if (classNode)
{
newProject.addUtilNode(UtilElementType::Class, classNode.name());

for (i=1; i <= _numOfClasses; i++)
{
dictClassLoop = _dictionary.classObject(_dictionary.classCnt2Id(i));

if (dictClassLoop.extend() == _dictClass.id())
{
childClass = new DictClass(dictClassLoop.id());
addToProjectForwardClass(childClass, _dictionary, _numOfClasses);
}
}
}
}
}
;

sharedProjects = infolog.projectRootNode().AOTfindChild('Shared');
sharedProjects.AOTAdd(project);
newProject = sharedProjects.AOTfindChild(project);
newProject.loadForInspection();
newProject = newProject.getRunNode();
addToProjectForwardClass(dictClass, dictionary, numOfClasses);
newProject.AOTsave();


}

Tuesday, October 20, 2009

Microsoft Brain

Yesterday, I was at a seminar about Microsoft Sharepoint Portal Services organized by Montreal community of .Net developers.

The presentation was good and interesting as usual. At the end of it they raffled three books about Best Practice for MOSS and one licence of Visual Studio 2008. However, yet there was one other prize that I got - Microsoft brain.

I am shocked how small it is...



picture borrowed from somebody