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(infolog.line());
}

print errorMessage;

pause;

The infolog.text() method gets the text of the line number argumented. the infolog.line() 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.

Thursday, April 26, 2012

C# Left, Mid and Right functions

To simulate the VB functions Left, Mid and Right, you could implement string extension methods in C#, like this:

Left

public static string Left(this string value, int length)
{
    if (string.IsNullOrEmpty(value))
    {
        return value;
    }

    return value.Substring(0, length);
}    

Mid

public static string Mid(this string value, int startPosition, int endPosition)
{
    if (string.IsNullOrEmpty(value))
    {
        return value;
    } 

    return value.Substring(startPosition, endPosition);
}  

Right

public static string Right(this string value, int length)
{
    if (string.IsNullOrEmpty(value))
    {
        return value;
    }         

    return value.Substring(value.Length - length);            
}


Then you could use them like this:

someString.Left(5);

someString.Mid(5, 20);

someString.Right(9);

Note that I'm not really sanitizing any exceptions that may occur, like, for example, if the string passed in is smaller than the one we try to get. You should implement that defensive code yourself. :)

Thursday, March 29, 2012

LINQ to Text Files

LINQing it

If you ever need to find something specific inside a text file, I'd advice you to go with LINQ.
Below is a very useful extension method to work with Text Files' content with LINQ:

static class Extensions
{
    public static IEnumerable<string> Lines(this StreamReader reader)
    {
        string line;
        
        while (!string.IsNullOrEmpty(line = reader.ReadLine()))
        {
            yield return line;
        }
    }
}

So with the above extension method in context, you could simply do things like this:


using (var sr = new StreamReader(myPath))
{
    var results = from line in sr.Lines()
                  where line.StartsWith("Foo") && line.Contains("this text")
                  select line;
}

This makes life a little easier.