Showing posts with label .NET Programming. Show all posts
Showing posts with label .NET Programming. Show all posts

Saturday, August 3, 2013

Multiple recipients with SmtpClient in .NET

When sending a quick email, for example to notify of a PowerShell task failure, the simplest System.Net.Mail.SmtpClient method to use is usually this one:
public void Send(
string from,
string recipients,
string subject,
string body
)

The parameter name recipients implies that it is possible to send the email to more than one recipient - but the documentation does not say how. I tried separating the addresses with a semicolon, but that failed with the following error:
"Send" with "4" argument(s): "An invalid character was found in the mail header: ';'."
The correct separator to use, which I only found out by trawling through the .NET source with ILSpy, is a comma (,).

Thursday, July 4, 2013

Overhead of using async and await in C# 4.5

Although asynchronous code is touted as allowing for more scalable architectures through less threads (a la nodejs), there must be a latency overhead to this technology. While the total throughput of the system may be higher, an individual request must be slowed down due to increased context switching.

I was interested in seeing how much of a performance impact there is to using the new Async pattern in .NET (and simplified using the fantastic async / await keywords in C#).

I wrote an application that compares the FileStream.Read and FileStream.ReadAsync calls in a tight loop, reading one byte into a byte array 1 million times. Tweaking the file size and other parameters resulted in very different results, although the synchronous call was always at least five times faster than the asynchronous call.

Results

File length: 1000000 bytes.
Running 100 loops of Preload...
Done in 0 ms.
Running 1000000 loops of Asynchronous...
Done in 2452 ms.
Running 1000000 loops of Synchronous...
Done in 17 ms.

Conclusion:

In this single test scenario, using this particular method of file access, the overhead of using asynchronous calls for very fast operations is significant in that an operation that takes around 20 ms synchronously runs at 2500 ms asynchronously (125 times slower). In practice, this is an overhead of 0.00000248 seconds (2.5 μs) per asynchronous call. Whether this overhead is acceptable, and whether the reduction in system resources that asynchronous calls are supposed to yield is worth this slowdown is application dependent.

Source code

Sunday, July 3, 2011

Installing ReSharper 6 causes several error dialogs

I recently installed ReSharper 6 (6.0.2202.688) into Microsoft Visual Studio 2010 and was greeted with some error dialogs:

The 'RadLangSvc.Package, RadLangSvc.VS, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91' package did not load correctly.
The problem may have been caused by a configuration change or by the installation of another extension. You can get more information by running the application together with the /log parameter on the command line, and then examining the file 'C:\Documents and Settings\Administrator\Application Data\Microsoft\VisualStudio\10.0\ActivityLog.xml'.

The 'Language Package' package did not load correctly.
The problem may have been caused by a configuration change or by the installation of another extension. You can get more information by running the application together with the /log parameter on the command line, and then examining the file 'C:\Documents and Settings\Administrator\Application Data\Microsoft\VisualStudio\10.0\ActivityLog.xml'.

It looks as though this is a known issue, caused by a conflict between some plugins, Visual Studio 2010, and SQL Server 2008 R2.

The solution for me was to install \WCU\DAC\DACProjectSystemSetup_enu.msi from the Visual Studio 2010 installation media.

Saturday, July 2, 2011

NLog logging levels

NLog logging uses the following levels:

  • Trace: 0
  • Debug: 1
  • Info: 2
  • Warn: 3
  • Error: 4
  • Fatal: 5
  • Off: 6

Monday, April 25, 2011

Setting the current culture of a website

Rather than relying on the settings of the server, it can be prudent to set the current culture of your ASP.NET application using the following setting:


  ...
  <system.web>
    ...
    <globalization culture="en-NZ" uiCulture="en-NZ" />
    ...
  </system.web>

Tuesday, April 19, 2011

Serialise value to XML in one line of C#

This one-liner is useful in the immediate window of Visual Studio to serialise an object to XML:

new XmlSerializer(OBJECT.GetType()).Serialize(new System.IO.StreamWriter(@"c:\temp\x.xml"), OBJECT);

Note that this is not production ready because it does not dispose of objects that should be disposed!

Thursday, January 6, 2011

Capturing both output and error streams from a .NET process

MSDN documents a well-known problem that needs to be worked around when reading output from a console process started using System.Diagnostics.Process:

The code example avoids a deadlock condition by calling p.StandardOutput.ReadToEnd before p.WaitForExit. A deadlock condition can result if the parent process calls p.WaitForExit before p.StandardOutput.ReadToEnd and the child process writes enough text to fill the redirected stream. The parent process would wait indefinitely for the child process to exit. The child process would wait indefinitely for the parent to read from the full StandardOutput stream.

The example in MSDN is:

// Start the child process.
 Process p = new Process();
 // Redirect the output stream of the child process.
 p.StartInfo.UseShellExecute = false;
 p.StartInfo.RedirectStandardOutput = true;
 p.StartInfo.FileName = "Write500Lines.exe";
 p.Start();
 // Do not wait for the child process to exit before
 // reading to the end of its redirected stream.
 // p.WaitForExit();
 // Read the output stream first and then wait.
 string output = p.StandardOutput.ReadToEnd();
 p.WaitForExit();

The problem with this example is that it only reads from stdout - not from stderr. One solution, using the new .NET TPL (Task Parallel Library), is to launch two tasks that read the output and error streams.

ProcessStartInfo psInfo = new ProcessStartInfo
{
    Arguments = @"/c dir C:\",
    FileName = @"C:\Windows\system32\cmd.exe",
    RedirectStandardOutput = true,
    RedirectStandardError = true,
    UseShellExecute = false
};

Process proc = Process.Start(psInfo);
string stderr = null, stdin = null;
Parallel.Invoke(() => stdin = proc.StandardOutput.ReadToEnd(),
    () => stderr = proc.StandardError.ReadToEnd());
proc.WaitForExit();

There is one caveat with this approach - Parallel.Invoke does not seem to guarantee that both tasks will run in parallel. I will need to investigate this some more.

Wednesday, November 24, 2010

What exceptions can a WCF channel throw?

It seems that expected exceptions from a WCF channel include:

  • TimeoutException
  • CommunicationException - FaultException, FaultException, EndpointNotFoundException etc.

MSDN describes the exceptions that are caused by communication errors.

Also from MSDN:

When implementing custom channels and binding elements, it is strongly recommended that your components throw only System.TimeoutException or CommunicationException-derived objects. In the case where your components throw a recoverable exception that is specific to the component, wrap that exception inside a CommunicationException object.

MSDN also describes how to specify and handle faults in WCF contracts and services.

Tuesday, November 16, 2010

SSH client for .NET

I have been looking for a good, free, .NET SSH client. This SSH client seems to fit the bill.

I did, however, run into a problem where it appears that the state of the session is not preserved between calls. I will follow this up on the Codeplex page.

The following test fails:

[TestMethod]
public void ShouldMaintainSessionStateAcrossCommands()
{
    using (SshClient client = new SshClient("sssssss", "uuuuuuu", "ppppppp"))
    {
        client.Connect();

        client.RunCommand("cd /tmp");
        string result = client.RunCommand("pwd").Result;

        Assert.AreEqual("/tmp", result);
    }
}

EDIT: This is the issue I logged and the developer's response. It seems that this behaviour is "by design", which unfortunately means that there will not be a 'fix'.

The workaround would be to run all dependent commands separated by semicolons, although this is quite limiting in some situations.

Monday, November 15, 2010

Uploading a file using FTP and the WebClient

Note: more detailed documentation for the .NET 4.0 explains the reason for requiring the additional slash (/). See the remarks section for the FtpWebRequest class. If you are using .NET 4.0, this class is probably a better bet.

I ran into some trouble uploading a file using the System.Net.WebClient. Looking at examples in the web, it seemed quite straightforward, but I found that I needed to include an additional forward slash in order to get the transfer to work correctly.

At first, I struggled to upload a file with this call to my WebClient instance:

client.UploadFile("ftp://sssssss/folder/folder/folder/t.txt", @"c:\temp\t.txt");

I was able to download a file in that location with Internet Explorer using the URI above, but not upload to it using the WebClient.

This dump from Wireshark illustrates the problem (note line 14), which seems to be a bug in the WebClient to me:

220 sssssss FTP server (Version 1.1.214.4(PHNE_38458) Tue Jul 29 07:36:52 GMT 2008) ready.
USER uuuuuuu
331 Password required for uuuuuuu.
PASS ppppppp
230 User uuuuuuulogged in.
OPTS utf8 on
500 'OPTS utf8 on': command not understood.
PWD
257 "/home/fld/uuuuuuu" is current directory.
TYPE I
200 Type set to I.
PASV
227 Entering Passive Mode (xx,xx,xx,xx,xx,xx)
STOR folder/folder/folder/t.txt
553 Could not determine cwdir: No such file or directory.
221 You could at least say goodbye.

Noticing that the STOR command was providing a relative path (not starting with /), I assume that the server was trying to save the file into the current folder i.e. /home/fld/uuuuuuu/folder/folder/folder/t.txt. To test this theory, I added a slash at the beginning ot the path:

client.UploadFile("ftp://sssssss//folder/folder/folder/t.txt", @"c:\temp\t.txt");

I am not sure if this is a valid URI, but it seems to work with the WebClient, although I am not sure why the RETR command (line 19) is executed as in the following Wireshark dump:

220 sssssss FTP server (Version 1.1.214.4(PHNE_38458) Tue Jul 29 07:36:52 GMT 2008) ready.
USER uuuuuuu
331 Password required for uuuuuuu.
PASS ppppppp
230 User uuuuuuu logged in.
OPTS utf8 on
500 'OPTS utf8 on': command not understood.
PWD
257 "/home/fld/uuuuuuu" is current directory.
TYPE I
200 Type set to I.
PASV
227 Entering Passive Mode (xx,xx,xx,xx,xx,xx)
STOR /folder/folder/folder/t.txt
150 Opening BINARY mode data connection for /folder/folder/folder/t.txt.
226 Transfer complete.
PASV
227 Entering Passive Mode (xx,xx,xx,xx,xx,xx)
RETR folder/folder/folder/t.txt
550 folder/folder/folder/t.txt: No such file or directory.
221 You could at least say goodbye.


This is the final code that seems to work:

        public static void SendFile(string sourcePath, string server, string userName, string password, string remoteFolder, string remoteFileName)
        {
            using (WebClient client = new WebClient())
            {
                string dest = string.Format(CultureInfo.InvariantCulture, "ftp://{0}/{1}/{2}", server, remoteFolder, remoteFileName);
                client.Credentials = new NetworkCredential(userName, password);
                client.Proxy = null;
                client.UploadFile(dest, sourcePath);
            }
        }

And the corresponding integration test:

        [TestMethod]
        public void ShouldSubmitFileToFtpServer()
        {
            Guid uniqueId = Guid.NewGuid();

            string tempFile = Path.Combine(Path.GetTempPath(), "FtpTestFile.txt");
            File.WriteAllText(tempFile, "Test file - safe to delete. " + uniqueId.ToString());

            string server = "sssssss";
            string userName = "uuuuuuu";
            string password = "ppppppp";
            string folder = "/folder/folder/folder";
            string fileName = "t.txt";
            Ftp.SendFile(tempFile, server, userName, password, folder, fileName);

            string tempFileDown = tempFile + ".down";

            if(File.Exists(tempFileDown)) File.Delete(tempFileDown);

            using (WebClient client = new WebClient())
            {
                client.Credentials = new NetworkCredential(userName, password);
                string ftpLink = String.Format(CultureInfo.InvariantCulture, "ftp://{0}/{1}/{2}", server, folder, fileName);
                client.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
                client.DownloadFile(ftpLink, tempFileDown);
            }

            string content = File.ReadAllText(tempFileDown);

            Assert.IsTrue(content.Contains(uniqueId.ToString()));
        }

Thursday, October 28, 2010

Setting up FitNesse for .NET with SliM

Basic steps for setting up FitNesse for .NET with the wiki stored under source control. Review all code that you copy and paste from this page because it may need to be modified for your situation.

  1. Create a simple fixture:
    public class IsFitNesseCorrectlySetup
    {
        public int Value { get; set; }
    
        public int GetValueDoubled()
        {
            return Value * 2;
        }
    }
    
  2. Download FitNesse and SliM, and save these into separate folders under your third party folder in source control (everything here is presumed to be under source control).
  3. Create this .cmd file for testers to use (presumes TFS; will need to be modified for other source control systems):
    @echo off
    setlocal
    "%VS100COMNTOOLS%\..\IDE\TF.exe" get "%~dp0.." -r
    "%VS100COMNTOOLS%\..\IDE\TF.exe" checkout "%~dp0FitNesseRoot" -r
    C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe "%~dp0..\Solution\Solution.sln" /p:configuration=release
    set FitNessePort=8084
    start /min /d "%~dp0..\ThirdParty\FitNesse" java -jar fitnesse.jar -p %FitNessePort% -d "%~dp0." -r FitNesseRoot
    start http://localhost:%FitNessePort%/
    endlocal
  4. Launch FitNesse and add the following to the root page (modifying paths to match your folder structure):
    !*****> Configuration
    !define TEST_SYSTEM {slim}
    !path .dll
    !define COMMAND_PATTERN {%m -r fitSharp.Slim.Service.Runner,..\..\ThirdParty\FitSharp\fitsharp.dll %p}
    !define TEST_RUNNER {..\..\ThirdParty\FitSharp\Runner.exe}
    *****!
  5. Add a normal page (not a test page) called SetUp at the top level of your suite, and put the following in it to let FitNesse know which namespaces to find your fixtures in:
    !|import|
    |Your Fixture DLL namespace|
  6. Create a test page with this test:
    !|Is FitNesse correctly set up|
    |Value   |Get value doubled?  |
    |2       |4                   |
    |23      |46                  |
    |100     |200                 |
    |200     |399<_<401           |
    Run the test, which should pass.
Note that these files in the FitNesse wiki should not be checked in to source control:
  • *.zip
  • ErrorLogs\**
  • files\testResults\**
  • RecentChanges\**

Friday, July 16, 2010

Overriding protected members using Moq and/or RhinoMocks

Sometimes I want to override a protected method on an internal class using a mocking framework and make verifications against calls to that method. This problem also occurs when trying to mock an internal interface in the assembly under test.

When doing this with Moq or RhinoMocks (both of which use the same proxy generator), I got the following error:

"Castle.DynamicProxy.Generators.GeneratorException: Type is not public, so a proxy cannot be generated."

The solution is to publish the internals of the assembly under test to the appropriate assembly:

[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2," +
    "PublicKey=00240000048000009400000006020000002400005" +
    "25341310004000001000100c547cac37abd99c8db225ef2f6c8" +
    "a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7" +
    "852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0" +
    "a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15" +
    "605093924cceaf74c4861eff62abf69b9291ed0a340e113be11" +
    "e6a7d3113e92484cf7045cc7")]

Wednesday, April 28, 2010

Differences between csc (devenv) and msbuild

The drive letter in the stack frame text is lower case when built using msbuild, and upper case when build in VS 2008.

Sunday, April 18, 2010

Different types of SafeHandle

I ran into the following error:
"Cannot marshal 'return value': Returned SafeHandles cannot be abstract."

The Microsoft.Win32.SafeHandles namespace has several different handle types.

Wednesday, February 3, 2010

Converting SecureString to String

This blog shows the correct way to convert a SecureString to a String for the many APIs that require it.

Copied here in case the link breaks sometime in the future:
public static string ConvertToUnsecureString(this SecureString securePassword)
{
    if (securePassword == null)
        throw new ArgumentNullException("securePassword");

    IntPtr unmanagedString = IntPtr.Zero;
    try
    {
        unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(securePassword);
        return Marshal.PtrToStringUni(unmanagedString);
    }
    finally
    {
        Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
    }
}

public static SecureString ConvertToSecureString(this string password)
{
    if (password == null)
        throw new ArgumentNullException("password");

    unsafe
    {
        fixed (char* passwordChars = password)
        {
            var securePassword = new SecureString(passwordChars, password.Length);
            securePassword.MakeReadOnly();
            return securePassword;
        }
    }
}