Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

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

Thursday, November 24, 2011

Protecting against CSRF in MVC 3

It is important to protect your customers and other users of your website against CSRF attacks. To do this in ASP.NET MVC 3, you would normally use the ValidateAntiForgeryTokenAttribute. The test below will ensure that all appropriate actions on all controllers have this protection.
[TestMethod]
public void All_post_actions_validate_anti_forgery_tokens()
{
    var modelController = typeof(MyController);  //this can be any one of your controllers in this particular area
    var allControllers = modelController.Assembly.GetTypes().Where(t => t.Namespace == modelController.Namespace && t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase));
    foreach (var controller in allControllers)
    {
        AssertControllerValidatesAntiForgeryToken(controller);
    }
}

static void AssertControllerValidatesAntiForgeryToken(Type controller)
{
    Console.WriteLine("Checking " + controller.Name);
    var violations = (
        from method in controller.GetMethods()
        let attributes = method.GetCustomAttributes(true)
        let parameters = method.GetParameters()
        let isProperty = !method.Name.StartsWith("set_", StringComparison.OrdinalIgnoreCase) && !method.Name.StartsWith("get_", StringComparison.OrdinalIgnoreCase)
        let isPostMethod = parameters.Length != 0 && (!attributes.Any(a => a is ActionMethodSelectorAttribute) || attributes.Any(a => a is HttpPostAttribute))
        let noAntiForgeryToken = !attributes.Any(a => a is ValidateAntiForgeryTokenAttribute)
        let excludedMethods = typeof(ControllerBase).GetMethods().Select(m => m.Name)
        let isExcludedMethod = excludedMethods.Contains(method.Name)
        where isProperty && isPostMethod && noAntiForgeryToken && !isExcludedMethod
        select method.Name).ToArray();

    foreach (var violation in violations)
        Console.WriteLine("Failed: {0}/{1}", controller.Name, violation);

    Assert.AreEqual(0, violations.Count());
}         

Wednesday, September 21, 2011

Dump all properties of an object to a string in C#

It is often useful to dump all the properties of an object for debugging. This trivial implementation is good, although it only dumps the top level properties:

static string DumpAllProperties(object obj)
{
    return string.Join(", ", 
        TypeDescriptor.GetProperties(obj)
            .Cast()
            .Select(p => string.Format(CultureInfo.CurrentCulture, "{0} = {1}", p.Name, p.GetValue(obj))));
}

Tuesday, March 22, 2011

Setting up Unity in a new project

Using the Common Service Locator and Unity is fairly simple with Nuget. This post records the steps in case I need them again.


  1. Add the CommonServiceLocator and Unity packages to the project using Nuget.
  2. Add the following initialisation code (for example in Application_Start):
    UnityContainer container = new UnityContainer();
                
    //configure container
    ...
    
    var unityServiceLocator = new UnityServiceLocator(container);
    ServiceLocator.SetLocatorProvider(() => unityServiceLocator);
    
  3. Use the IServiceLocator and ServiceLocator interface and class for DI and service location, respectively.

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, April 23, 2010

Contract first WCF tips

Generate types from xsd using WSCF Blue with options set to correct capitalisation.

On the interface:
Apply the ServiceContractAttribute with the service namespace.
Apply the XmlSerializerFormat(SupportsFaults = true) attribute.

On the interface's operation:
Apply FaultContractAttribute.
Apply OperationContractAttribute.

On the service class:
Apply any behaviour attributes, such as for error handling.
Apply the ServiceBehaviourAttribute, and set ConcurrencyMode = ConcurrencyMode.Multiple, IncludeExceptionDetailInFaults = false.

The operation should take a type such as:

[MessageContract(IsWrapped = false)]
public class Input
{
    [MessageHeader(Name = "headerData")]
    public Header Header { get; set; }

    [MessageBodyMember(Name = "inputData")]
    public Body Body { get; set; }
}

The response must be similar:

[MessageContract(IsWrapped = false)]
public class Response
{
    [MessageHeader(Name = "headerData")]
    public Header Header { get; set; }

    [MessageBodyMember(Name = "responseData")]
    public Body Body { get; set; }
}

Saturday, March 20, 2010