You might already have noticed, that I am a supporter of using DotNet variables within C/AL. They allow us to achieve great things with little effort (most of the time). If we would need to achieve the same thing with pure C/AL code, we would either create an Add-in, create batch files to run through a shell command or just write a heck of a lot of code.
Question: But how do we know if we can do anything with DotNet variables?
Basically, anything can be achieved with DotNet variables, but not everything is necessary to do with C/AL variables…
Samples:
- Creating a file on an FTP server, might be a good candidate
- Appending text to a string might also be a good candidate (for that read up on StringBuilder in my Bootcamp notes about DotNet)
- Writing a file to a network location would be a good candidate to keep in C/AL
A good rule of thumb is using a search engine. Search is your friend. Lookup for example “.net ftp upload”, you will find lots of examples on how to make this using .net code. Remember if you have sample code, you can (most of the time) also achieve this in C/AL with DotNet variables.
Question: How do I find how to declare a .NET variable as a DotNet variable?
Most of the time this should be pretty straight forward. If we have a System.Net.WebRequest, we will most probably find it in System.Net.
If you do not find it, try looking in the above level, which would be System for System.Net.WebRequest.
If you still did not find it, it could be a core object, which would be found in mscorlib.
Alternatively, you just open up the MSDN website, and search for it. You will always find some information about the object, sometimes even which the enum values are and sample code. But after the description and inheritance level, you will see the namespace it is declared in, and more important, you find the assembly to declare the object in NAV.
Question: How do I take the .NET code and convert this into C/AL code?
Well, this is really easy to do. Let’s take our FTP upload example again. I searched the internet, and the first result was an MSDN example (always a good place to start with).
using System; using System.IO; using System.Net; using System.Text; namespace Examples.System.Net { public class WebRequestGetExample { public static void Main () { // Get the object used to communicate with the server. FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://www.contoso.com/test.htm"); request.Method = WebRequestMethods.Ftp.UploadFile; // This example assumes the FTP site uses anonymous logon. request.Credentials = new NetworkCredential ("anonymous","janeDoe@contoso.com"); // Copy the contents of the file to the request stream. StreamReader sourceStream = new StreamReader("testfile.txt"); byte [] fileContents = Encoding.UTF8.GetBytes(sourceStream.ReadToEnd()); sourceStream.Close(); request.ContentLength = fileContents.Length; Stream requestStream = request.GetRequestStream(); requestStream.Write(fileContents, 0, fileContents.Length); requestStream.Close(); FtpWebResponse response = (FtpWebResponse)request.GetResponse(); Console.WriteLine("Upload File Complete, status {0}", response.StatusDescription); response.Close(); } } } }
Let’s begin with the first line:
FtpWebRequest request = (FTPWebRequest)WebRequest.Create("....");
We create a DotNet variable of type FtpWebRequest. We look into our Symbol menu to the constructors and see the Create event there. This is our result:
FtpWebRequest := FtpWebRequest.Create('...');
Not that hard right? Let’s move on to the next line…
request.Method = WebRequestMethods.Ftp.UploadFile;
Here we have a piece of code that looks also pretty easy to implement. Let’s begin by defining WebRequestMethods as System.Net.WebRequestMethods.
Our symbol menu has no properties, constructors nor usable method for this, but while selecting, you might have noticed there was a System.Net.WebRequestMethods+FTP existing, so let’s choose that one.
Again we find no usable constructor, property nor method. It would seem that we would need to stop the implementation, or do we?
Let us fire up Visual Studio (come on, be honest, which of you doesn’t have this one installed?), and start a new project.
As it looks, this WebRequestMethods is merely an enumerator (list with possible values). So let us look at what the value actually is. Add a line of code in our project when it is run:
MessageBox.Show(System.Net.WebRequestMethods.Ftp.UploadFile);
After running the code, it looks as the result was a simple string containing ‘STOR’.
This means we can add the following line of code in C/AL:
FtpWebRequest.Method := 'STOR';
Off course, I agree, this is not the most beautiful solution, but it will work.
If the commands in the .NET variables change to ‘STOR2′, we would need to update our code…
Let this also be a call out to the readers, if anyone knows a better / more beautiful way to do this, shout it out in the comments below.
Let’s wrap up by converting 2 other pieces of the sample code, the rest should be obvious after those.
request.Credentials = new NetworkCredential ("anonymous","janeDoe@contoso.com");
Again, we start with adding the NetworkCredential as a DotNet variable and firing up the symbol menu.
Here we see a usable Constructor: NetworkCredential(string userName, string password), which let’s us write the following code in C/AL:
FtpWebRequest.Credentials := NetworkCredential.NetworkCredential('MyUserName','MyPassword');
Little effort and basically almost the same code in NAV as in .NET.
Last example, which is the most complicated of the bunch:
StreamReader sourceStream = new StreamReader("testfile.txt"); byte [] fileContents = Encoding.UTF8.GetBytes(sourceStream.ReadToEnd()); sourceStream.Close(); request.ContentLength = fileContents.Length;
The code above opens a source file, reads it with a stream and stores it binary.
First thing we notice is byte [ ] in the .NET code. Which is an array of byte. In NAV, we only have fixed size arrays, so we see our first problem already…
This doesn’t mean however, we cannot use byte arrays as return variables. One option is to use the return variable directly to an input variable/property of another DotNet variable. So in this example we would not read the file into a variable, but read it again every time. Let me show you how I personally would solve this in C/AL (assuming we already opened the stream to the file):
FtpWebRequest.ContentLength := UTF8Encoding.GetBytes(SourceStream.ReadToEnd).Length; // close stream ..... // reopen stream RequestStream.Write(UTF8Encoding.GetBytes(SourceStream.ReadToEnd),0,FtpWebRequest.ContentLength);
Another way would have been to read the stream into a text variable, and keep using the text to pass to the GetBytes function. Again, this is how I solved this issue, any other / better solutions, let me know in the comments.
To see a full working example, check my previous post.
Recapitulation:
- Define the .NET variable as a DotNet variable and match the properties / constructor
- If no match found, try if you can find the value
- Some code might be done with a workaround, instead of storing the value in between, call the output parameter again and use nested function calls. (Variable.Method.Property.SomeOtherProperty.AndSoOn)
- Remember that DotNet is case sensitive!!! tostring functions is not the same as ToString function.
This concludes this post, which has become quite a long read. Please, if you have any remarks / improvements, let me know. I am sure there is a lot of room to improve my code.