Pages

Tuesday, April 23, 2013

Getting the last error message from the Infolog

Even though you may think that you'll often have to do this, you won't. If you come to a situation where you'd consider doing this, you better think again, because there's always a workaround.

Still, if you really want to do this, you can do the following:


str errorMessage;

try
{
    throw error('Some error message being thrown here');
}
catch
{
    errorMessage = infolog.text(infologLine());
}

print errorMessage;

pause;

The infolog.text() method gets the text of the line number argumented. The infologLine() global method gets the total of lines in the Infolog. The code above always gets the last line added to the Infolog.


The code above will not hide the Infolog too. If you want to capture the last error message and then hide the infolog, or clear its contents, you can add a call to the infolog.clear() method right after you fetch the error message.


One thing to point out is that the infolog.text() method returns you the contents of the line number specified as text. This means it'll cast the line icon to a string too, making it become a tab character. You'll have to parse the returned string, because it'll be something like " Some error message being thrown here".

You can simply add a call to the strRem method to remove the tab character:

errorMessage = strRem(errorMessage, '\t');

Thursday, April 18, 2013

Be careful with Country Region Codes

When developing in X++, be careful when assigning some Extended Data Type to a table column, for example.

Extended Data Types have the Country Region Codes property, which, as the help text says, is a "Comma separated list of Country Region Codes where this can be shown".

This means that if you are working on a localization project for a Brazilian company for example, and you use an Extended Data Type which Region Code is "FR", for France, on a table column, that column won't be shown. Simple as that, it will not appear anywhere in any form.

You should always use the correct Country Region Code for your EDTs, depending on the country of the Legal Entity you're working with.

Monday, March 4, 2013

How to effectively refresh data on forms

Whenever you change some data in another process and you want to display it back on the form, you have to somehow refresh the values on the form.
On this post, I'll try to explain which methods we have to do that, and how and when to use them.


FormDataSource.refresh

The first method, available on the FormDataSource class, is the one that seems to be the most intuitive: the refresh method.

What it does is put the data stored in the form cache, for the current record on the controls. This means that it won't go all the way back to the database to ask it again for the record. In other words, data modified for that record on another process, outside of the form, won't be displayed since the cache won't be refreshed. If you need this, you can use the method below.

FormDataSource.research

The research method will do what the refresh won't do: it will ping the database and query it again with the form generated query. Because it does this, it will update all of the records on the data source.

There's also the optional parameter bool _retainPosition. When called with the argument true, it preserves the cursor position on the form's grid. This is very useful when you don't want the user to "lose" track of what he was doing, or to highlight whatever changed on the record.


FormDataSource.reread

This method is pretty straightforward: it simply rereads the current record on the data source. Also, this doesn't refresh any value that the user sees on the form. It only updates the record on the form data source level.

One common use of this method is to call the refresh method too right after.


FormDataSource.executeQuery

The executeQuery method also queries the database again and updates all of the records on the form data source. You should use this method whenever you modify the form's query, by adding a range or removing one, for example.


These are the ways of refreshing data in a form. And here's the post that helped me writing this one.

Tuesday, January 29, 2013

The Garbage Collector does not call the Dispose() method!

If you have ever wondered if the Garbage Collector calls the Dispose method on objects that implement the IDisposable interface, the answer is no.

It calls the Finalize method, which does nothing by default. If you have any unmanaged or additional resources that you want freed or released after the lifetime of an object, you must call the properly overriden Dispose method. I won't get in details on the Dispose pattern or anything like that, not on this post. Maybe on the future...

Anyway, this caught my attention because we had lots of objects that used unmanaged resources and they were not being freed, causing some memory leaks. The Dispose method was correctly implemented on these objects but the only thing is that it wasn't being called at all!

So my suggestion is that you always use try-catch-finally blocks or using blocks when dealing with disposable objects, specially when they use unmanaged resources. Make sure that you always call the Dispose method.

Simple example of a Foo class that uses some unmanaged resources inside of an using block, assuming that the Dispose method effectively frees any resources that the object uses:

using(var disposableFoo = new Foo())
{
    // Use your disposable foo
}

Tuesday, January 15, 2013

Ignore casing and diacritics on LINQ queries

When working with LINQ to objects, one could simply call methods to remove diacritics (accentuation) and ignore casing on LINQ queries. Since everything will run on .NET, the methods should resolve on the collections, and everything just works fine.

First of all, we'll need the following extension method:
public static string RemoveDiacritics(this String s)
{
    String normalizedString = s.Normalize(NormalizationForm.FormD);
    StringBuilder stringBuilder = new StringBuilder();

    for (int i = 0; i < normalizedString.Length; i++)
    {
        Char c = normalizedString[i];
        if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
            stringBuilder.Append(c);
    }
     return stringBuilder.ToString();
}
Now if you're working with LINQ to objects you could simply write LINQ queries like this:
var result = from p in People
             where p.Name.ToUpper().RemoveDiacritics().Contains(filter.ToUpper())
             select p;
But when you get to play with LINQ to SQL things get a little harder. LINQ to SQL will try to convert the query you've written into valid SQL statements, to run with SQL optimizations, on the database. It translates your query into SQL statements and executes them on the database. The result is then translated back into objects and sent back to your code so you can work with them. So when you work with LINQ to SQL, you either need to have a stored procedure declared on your database that the SQL statement generated can call, or you can "adjust" the database to your needs. I'd go with the second, by changing the column (or the entire table or database) collation, to one that ignores diacritics, or accentuation. For the above example, this could be done with the following statement:
ALTER TABLE People ALTER COLUMN Name [varchar](100) COLLATE SQL_Latin1_General_CP1_CI_AI
This should save you the trouble of developing a stored procedure to do that. Then, we could just use our filter in our queries:
var result = from p in People
             where p.Name == filter
             select p;
A question that I asked on StackOverflow motivated me to blog about this. It can be found here.

Wednesday, August 1, 2012

Using FileSystemWatcher

Undoubtedly one of the most interesting components in the .NET framework, and it's so useful that I was surprised to find out that it exists since the first version of the framework. I’m going to show simple and easy it is to work with.

I’ll adopt the description method first used by Felipe in here:

In my words:

FileSystemWatcher lets you monitor a given directory reporting every detected change, such as files and folders creations, renamings, exclusions, and changes. The most obvious use for it and probably what it’s more used for is automated file processing, for its reliability and speed.

In MSDN’s words:

Listens to the file system change notifications and raises events when a directory, or file in a directory, changes. Use FileSystemWatcher to watch for changes in a specified directory. You can watch for changes in files and subdirectories of the specified directory. You can create a component to watch files on a local computer, a network drive, or a remote computer.

A better look

It’s really a simple component, with few properties and events:

There are only two properties that would need some details:

Filter: A filter for files names and extensions. If you change it to *.txt, only changes made in .txt files will raise events.

NotifyFilter: Which kinds of changes will be used to determine a change. The default ones are enough for regular uses.

Its events are so simple that no further explanation is needed.

Demonstration

By now you should know what it does, and what it could be used for. I’m going to do a little demonstration on how to use it:

We’re going to use a simple form with a FileSystemWatcher, a TextBox, where we’re going to get the path to be watched from the user, and a button, to start monitoring.

To avoid having three methods that do the same thing, we’re going to use the same one to handle three of the events, Changed, Created and Deleted, since the three of them use the same signature. And an additional method for the Renamed event, as one of its arguments is not the same (to carry along the old name of the renamed file).

private void frmMain_Load(object sender, EventArgs e)
{
Watcher.Renamed += new RenamedEventHandler(Renamed);
Watcher.Deleted += new FileSystemEventHandler(Changed);
Watcher.Changed += new FileSystemEventHandler(Changed);
Watcher.Created += new FileSystemEventHandler(Changed);
}


Then we can set our two methods to handle these events, Renamed and Changed:

void Changed(object source, FileSystemEventArgs e)
{
string item = e.ChangeType + ": "+ e.Name;

lstChanges.Items.Add(item);
}

void Renamed(object sender, RenamedEventArgs e)
{
string item = "Renamed from: " + e.OldName + " to: " + e.Name;

lstChanges.Items.Add(item);
}

Now all we’ve got to do is to define what the Button is going to do:

private void btnStart_Click(object sender, EventArgs e)
{
try
{
Watcher.Path = txtPath.Text;
Watcher.EnableRaisingEvents = true;
}
catch (ArgumentException ex)
{
MessageBox.Show("This path does not exist: " + ex.Message);
}
}

Note: I don’t handle exceptions in any of these example codes to make it cleaner, but in this code above it’s really important, since it’s going to use the user input.

In case you’ve created the FileSystemWatcher programmatically, not by clicking the component on the Toolbox, then you have to take care of one more thing: a property of the watcher, SynchronizingObject. This is automatically taken care of by Visual Studio when you use it directly from the Toolbox, which is this demonstration’s case, but if you don’t, you just have to assign the form where you’re using to display the changes to the SynchronizingObject property like this:

Watcher.SynchronizingObject = this;

This code should be in the form, preferably right after the creation of the FileSystemWatcher instance.

Now we’re all set! Check below how this demonstration performed in a simple test:

Project

Felipe and I have been recently working on our first project in conjunction, and it’s entirely based on this component. I have to say that it’s getting really good, and we’re going to make the source code available, and then I’ll make a post about it in here.