Homepage | About Me | Testing ASP.net Book | Best Blog Posts | Personal Projects | Follow me on Twitter | GitHub | SlideShare | RSS
Blog.BenHall.me.uk

Learning a new language? Write some tests…

Friday, December 12, 2008

The excellent Pragmatic Programmer book suggests that you should learn a new language every year – this is something which I strongly agree with. By learning a new language it does not mean C# 4.0 (when you already know 2.0 and 3.0), or how to create Silverlight applications. Instead, make the effort to learn a language with a different mindset and approach to what your used to and actively engage in that community. Coming from a Java\C# background, Ruby was an eye-opener for me and approach to software development. The priorities and principals are different, for example Ruby has much more emphasise on elegance, solving problems and testability where software is treated as an art form while still being pragmatic in their approach – it’s never prefect and can be improved at some point. This importance is also being effectively communicated throughout the community from the Ruby guru’s such as Dave Thomas and Chad Fowler to the developers writing applications on a day-to-day basis. As a result of learning more about the Ruby language,

I feel I can approach a solution with a more opened minded approach.

How should you learn a new language?

This is something which I have been thinking about for a while, what is the most effective way to learn a new programming language? There are a couple of approaches you could take, but this is my approach (if you have your own suggestion, please leave it as a comment).

1) Buy a book, but not just any book – the best book. Personally, online materials are great but I still find the most effective way to learn something is to have a physical book by my side.  However, be sure to pick your book wisely, I recommend you research the influences within the community and either buy their book, or the book they recommend.  The wrong book could take you down completely the wrong path.

2) Start writing tests

Once you have picked your language and book of choice, you need to start grokking the concepts. After trying a couple of different techniques, I've found the best way to learn a new language is to actually write tests. This isn’t as crazy as it might sound, the test will define your expected outcome from your sample and give you something to aim for. This will help focus your mind on the task in hand, while giving you a clear signal as to when you are complete – the test will pass.

For example, with C#, if I wanted to know how to write a line of text to a file I might write the following test with the implementation.  

public class IO_Examples
{
    [Fact]
    public void Write_Hello_World_To_The_File_HelloWorldTxt()
    {
        MyFileAccess.Write("Hello World", "HelloWorld.txt");

        Assert.True(File.ReadAllText("HelloWorld.txt").Contains("Hello World"));
    }
}

public class MyFileAccess
{
    public static void Write(string s, string file)
    {
        StreamWriter streamWriter = new StreamWriter(file);
        streamWriter.WriteLine(s);
        streamWriter.Close();
    }
}

I have also found tests to be a much more natural starting point for interacting with a language, with C# my starting point was a command line application, however this wasn’t the most effective way of learning as I was constantly commenting out calls to various methods to execute the correct block of code. By using a test framework and a test runner as your starting point, I would have been able to run the samples more quickly and effectively while still keeping everything readable.

However, if your anything like me, while learning you will end up going off on a tangent or being distracted mid-task, I can recall too many occasions where I have been deep in the middle of learning the inners the underlying code only to read a blog post which takes me in a different direction and then completely forgetting where I was. By having a test, or a series of tests, guiding me I am able to quickly get back on track by seeing which tests are currently failing.  Because the tests will describe my aim, I will have a much better chance of remembering what I was actually doing.

Finally, once you start moving onto real applications and solving real problems using the language, you will be able to look back and refer to the tests as a reference regarding everything you learned.  If you keep your tests in a source control repository, you will even be able to see how you adapted over time. This could be extremely useful a year down the line where you want a very quick refresher.

3) Solve a problem!

Once you have an understanding of the language with a series of tests describing how to do various different tasks, the next thing to do is solve a problem you, or someone else, is experiencing having. While this isn’t always possible, it’s a great motivator to finish the job.  I enjoy automation and improving productivity (it allows me to spend more time on twitter), I find it really interesting to see how problems can be solved in a more effective fashion using tools and technologies – if a new language can help me with that then all the better. Not only will I learn the language in a more ‘real world’ context, but it will be helping me in the future.

Technorati Tags: , ,

Labels: , ,

.Net Fault Injection – It’s not just about exceptions

Saturday, November 15, 2008

I had a interesting comment on my previous post about .Net fault injection. ‘Losing Side’ asked if this would work for simulating other faults such as timeouts. It’s a good point and one I didn’t think about yesterday, but there are other faults which are interesting when testing the application. Performance is one of those areas, creating performance problems, such as slow disk IO or a slow server is difficult if you don’t have the setup in place, and even then not always possible. How can you effectively, repeatability test for a slow hard drive (and using a virtual machine doesn’t count). Tools such as ANTS Profiler will help tell you where the problems are, but only if you can reproduce the problem.

First demo of the day, I ask the question – how can you simulate a slow write process when using StreamWriter?  Based on my previous post, I've changed the method to this:

private static void MethodFails()
{
    Console.WriteLine("Writing to a file @ " + DateTime.Now);

    string path = Path.GetTempFileName();
    StreamWriter sw = new StreamWriter(path);
    sw.WriteLine("This is a test @ " + DateTime.Now);
    sw.WriteLine("This is a test @ " + DateTime.Now);
    sw.WriteLine("This is a test @ " + DateTime.Now);
    sw.WriteLine("This is a test @ " + DateTime.Now);
    sw.Close();
    Console.WriteLine("Done");

    foreach (var s in File.ReadAllLines(path))
        Console.WriteLine(s);
}

Running my console application normally, I get the following output, notice no delays between each write:

Writing to a file @ 15/11/2008 13:23:48
Done
This is a test @ 15/11/2008 13:23:48
This is a test @ 15/11/2008 13:23:48
This is a test @ 15/11/2008 13:23:48
This is a test @ 15/11/2008 13:23:48

Running this using my injector, I have a five second delay (which I set, easily could have been a random number) between each of my writes to the file.

Writing to a file @ 15/11/2008 13:19:45
Done
This is a test @ 15/11/2008 13:19:51
This is a test @ 15/11/2008 13:19:56
This is a test @ 15/11/2008 13:20:01
This is a test @ 15/11/2008 13:20:06

I now get the same five second delay each time this happens, creating a scenario repeatable.

Disappointingly, I tried to use the SQLConnection object, but I couldn’t get this to work. I don’t know if its a limitation or a bug.  Still a lot more work to do until its even remotely useable, but I'm finding the concepts interesting.

Technorati Tags: , ,

Labels: , ,

ReSharper 4.0 EAP - First nightly build 729

Sunday, February 17, 2008

image EAP for ReSharper 4.0 is open so I thought I would post my initial impressions. On their blog, the announcement adds this comment:

"That is, not fully fledged tool, but rather our current development bits. Which could be very well broken, buggy, hanging, slow and useless at times. We will not take any responsibility if the tool wipes out all your source code!"

You have been warned.

Download it from here: http://www.jetbrains.net/confluence/display/ReSharper/ReSharper+4.0+Nightly+Builds

The release notes contain information on all the current features:

http://www.jetbrains.net/confluence/display/ReSharper/ReSharper+4.0+EAP+Notes

Linq and partial methods are not currently supported, but the other features still make this worth it.

I initially installed this as an upgraded from 3.1.  Sadly, my 3.1 License is not acceptable as I got it before 20/12/2007 so I have to use the 30 day free evaluation. Not sure what is going to happen after 30 days with the EAP? Addin loaded, everything seems to be working.

Running over the menu's I notice two new features, Profile Unit Tests which I guess will link into dotTrace - I wonder if this will profile the code being unit tested, or the unit test themselves.... I don't have dotTrace installed so this option was disabled :(

In the options, they now also have Code cleanup profiles which are executed via Ctrl+E, Ctrl+C.  This allows you to define rules based on how you want to clean up your code (remove this, reformat code, optimise using etc).  Nice to be able to do this via a single command.

Various improvements to Intellisense improvements, it now understands var and Extension methods are detected and work well.

Annoymous types support is included. Given this code:

var o = new {Values = new string[] {"One", "Two", "Three"}};

If I put my cursor just before new, I get the option to create a named type.  This creates internal class of the class of your method:

public class AnonymousClass
{
    public string[] Values { get; set; }
}

public void Test()
{
    var o = new AnonymousClass() {Values = new string[] {"One", "Two", "Three"}};

You can also view view recent edits (Ctrl+shift+,) which then tells you the classes and methods you have recently changed and allows you to go to them which is cool.  Not sure if this is new, but it's cool and I only just found it.

However, it's not prefect. Couldn't find any help, lucky I did find the release notes which answered all my questions.

  1. One or two bugs - http://www.jetbrains.net/jira/browse/RSRP-58041 After a while I just told it to ignore this exception as it was annoying me. (NOTE: This has been fixed in build 730 - the joy!)
  2. prop shortcut is not there for creating automatic properties.
  3. Automatic Properties doesn't appear to be great,  didn't tell me at various points when I expected to such as the following code.  Would have expected it to offer me to convert them for me. 

public string name;
public string address;
public int Age { get { return age; } set { age = value; } }

I'm sure there is more, but this is a good first build.  Can't wait for some more...

Technorati Tags: , ,

Labels:

Process.Start and first time launching Firefox

Monday, December 17, 2007

I was going over the MbUnit issue list and came across Issue 97. This had a really interesting scenario.  Basically, Firefox was the default web browser but didn't have a profile associated with it (it hadn't been executed yet) and when you try and launch it via Process.Start it will load firefox correctly however also throw a Win32Exception.

If you did something like this:

Process.Start(http://www.google.co.uk);

The exception would be:

System.ComponentModel.Win32Exception was unhandled
  Message="The system cannot find the file specified"
  Source="System"
  ErrorCode=-2147467259
  NativeErrorCode=2
  StackTrace:
       at System.Diagnostics.Process.StartWithShellExecuteEx(ProcessStartInfo startInfo)
       at System.Diagnostics.Process.Start()
       at System.Diagnostics.Process.Start(ProcessStartInfo startInfo)
       at System.Diagnostics.Process.Start(String fileName)
       at ConsoleApplication1.Program.Main(String[] args) in ......\Program.cs:line 13
       at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException:

Very strange, if anyone knows the extract reason why it decides to thrown an exception please leave a comment. Definitely something to be aware might happen.  Wonder how many applications would crash because of this...

When testing your applications, if you want to reproduce this without having to load a clean VM, go into your %AppData% and move the Mozilla directory to another location.  Next time you load Firefox, the dialog will be displayed and the exception will be thrown.

Technorati Tags:

Labels:

How to: Break when an exception is thrown.

Saturday, December 08, 2007

While helping me to solve a threading issue, Robert mentioned this little gem.  Within Visual Studio, you can set it so that it breaks on all Exceptions (not just the unhandled exceptions). No longer do I need to keep stepping over code to see when an exception was thrown, I can simply bring up the exceptions dialog (ctrl + alt + E, or Debug > Exceptions) and tell it to break when a CLR Exception is thrown!  Now, as soon as an exception is thrown it will break at the point the exception was thrown.  You can then enable editing and see what was actually happening.

image

I knew it could be done, but had completely forgotten how/where to set it, where to the point I had forgotten it could be done...

Happy debugging!

Technorati Tags: ,

Labels:

Finding assemblies\modules currently in use

Sunday, December 02, 2007

Today I was trying to debug a problem and needed to see if locking was the issue.  Quick search online and I came across the tasklist command line application.  This lists all of the applications currently open together with the modules they have loaded.

To execute the list, open a command line window and type

tasklist -m

This will return everything.  If you want to stop and see everything you will need to use the more pipe.

tasklist -m | more

If you want something a bit more useful, type in the module your interested in.  This will then return only the applications open which has the module loaded.

tasklist -m MbUnit.Framework.dll

Finnally, to see a particular application with all the modules loaded you need to use a filter based on the IMAGENAME.

tasklist /FI "IMAGENAME eq MbUnit.GUI.vshost.exe" -m

I thought it was cool! :)

Technorati Tags: ,

Labels: ,

NotifyIcon and Always Hide setting

Wednesday, November 14, 2007

Yesterday, I had another one of those moments with the NotifyIcon class in C#.

During testing, if the icon was set to always hide under Windows XP then it would simply never display in the task bar.  This I thought was very strange as it should be in the systray, just not visible when its in the compact state.

The reason? Well on a balloon close event I would also remove the icon from the systray as it had served its purpose. However, when an icon is set to Always Hide, when a balloon tip is shown it is instantly closed (by XP Shell) which causes the balloon close event to be raised which in turn removes the icon from the systray. 

I should have realised sooner but simply didn't link the two actions. Next time in situations like this, always check events which hook up to the close event.

Technorati Tags:

Labels:

C# Null Coalescing Operator (??) and String.Empty

Thursday, October 11, 2007

One of the features of C# 2.0 is the null coalescing operator (??) which says that if the value is not null, then use it otherwise use another value.

For example, if we wanted to use the myCust variable if it wasn't null, or if it was use order.Cust then we would write.

Customer c = myCust ?? order.Cust;

Using 1.1, we would use the ternary operator (?) and it would have been

Customer c = if(myCust != null) ? myCust : order.Cust

We could have chained this together, so we could also check order.Cust to see if that was null and provide an alternative to that.

However, one problem I had was with String.Empty.  I wanted to test to see if a string was returned from a method and if it wasn't, use the system default.

String importantStr = myObject.GetImportantString() ?? sysDefault;

The problem is that myObject.GetImportantString() returns string.empty if it cannot be found. The operator doesn't treat string.empty as a null and uses it as the variable value. Not what I wanted at all! 

Sadly, I had to go back and use the ternary operator.

String importantStr = if(!string.IsNullOrEmpty(myObject.GetImportantString()) ? myObject.GetImportantString() : sysDefault;

Sadly, myObject.GetImportantString() is an expensive call so calling it twice is bad. So I had to resort to the old way of doing things

string temp = myObject.GetImportantString();
string importantStr = sysDefault;
if(!string.IsNullOrEmpty(importantStr))
   importantStr = temp;

Oh well, still a useful operator.  Just not for strings...

Technorati Tags:

Labels:

Balloon Tips, NotifyIcon and Windows 2000

While i'm sure this isn't relevant to most of my readers, again this has bugged me on and off for the last hour so thought I would blog for future reference for anyone else.

I've got a nice working SysTray application which displays a balloon tip message, that when a user clicks the tip it launches another separate dialog.  This worked great on Vista and XP however I decided just to do a little test on Windows 2000 (yes, its still used!).  I found that when you click the balloon tip on 2000, nothing happens.  Turns out this is because the shell_NotifyIcon is a different version on 2000 than XP/Vista and the event isn't being handled correctly.

The only way I found to fix this is no to only have the balloon tip click (BalloonTipClicked) event wired up but also the Click event on the actual NotifyIcon class wired up as well.  This way, when the user clicks the icon is will display.  Sadly, if they click the Balloon tip nothing will happen but at least there is still some support for 2000 in place.

I then found that on Vista/XP where it worked correctly before, now when the systray icon is clicked both the BalloonTipClicked and the NotifyIcon.Click event are fired, hence opening the form twice!  Quick solution was to use a boolean member variable which knows if the form is currently visible or not.  Nothing fancy...

Technorati Tags:

Labels:

NotifyIcon icon still visible after closing form

Wednesday, October 10, 2007

I''ve been playing around with the NotifyIcon class within C# recently but been having one major issue with it.  The class itself allows you to place an icon in the systray (down by the clock) and for it to display the standard balloon tip dialog.

However, the problem I was having is that when my calling parent closed my NotifyIcon would still be in my SysTray.  The only time it was removed was when I hovered over the icon with my mouse, then it would automatically disappear. Not great from the users point of view!

The MSDN page says nothing about this however I did find a useful forum post on the subject.  The solution is to manually call dispose and more importantly, set the variable to null.

The code is shown below, the notifyIcon variable is a member variable (private NotifyIcon m_notify;)

protected override void Dispose(bool disposing)
{
    if(m_notify != null)
    {
        m_notify.Visible = false;
        m_notify.Dispose();
        m_notify = null;
    }

    if (disposing && (components != null))
    {
        components.Dispose();
    }
    base.Dispose(disposing);
}

After inserting this code, when my parent form is closed the notify icon also disappears.  This took me a while to find out about, hopefully I have saved you some time.

Technorati Tags:

Labels:

Zipping a directory using C# and SharpZipLib

Sunday, September 23, 2007

While I have used C# and SharpZipLib to zip a directory before, I simple used the sample provided in the framework.   However the sample doesn't support maintaining the directory structure in the zip file. 

So, I looked around the site very quickly and noticed a FastZip class.  This has all the common scenarios like CreateZip, ExtractZip and setting a password for the zip. Very simple, but very quick to integrate and test.

There is some sample code to zip the directory ZipTest into ZipTest.zip.

ICSharpCode.SharpZipLib.Zip.FastZip z = new ICSharpCode.SharpZipLib.Zip.FastZip();
z.CreateEmptyDirectories = true;
z.CreateZip("F:\\ZipTest.zip", "F:\\ZipTest\\", true, "");

if (File.Exists("F:\\ZipTest.zip"))
    Console.WriteLine("Done");
else
    Console.WriteLine("Failed");

Technorati tags: ,

Labels:

Problem with Debugger.Break();

Saturday, September 22, 2007

While writing my previous post on how to debug live writer plugins, I mentioned using System.Diagnostics.Debugger.Break();  However, I found a problem with this. If you have a debugger attached, for example Visual Studio, then the program will hit the line of code and the debugger will step in. Great!

However, if no debugger is attached, the following error occurs:

image

I instantly thought this was strange, so built it as a Release version. Same thing happened. I know that with Debug.Write() the calls are removed in Release mode to save time, space and so no strange errors like this occurs.

First thing I did was boot up Reflector.  The debug class uses the ConditionalAttribute which provides a way to include/exclude methods depending on the preprocessor symbol.  So, if the Conditional compilation symbol is set to DEBUG then the method is included in the build, otherwise all the calls are removed.

[Conditional("DEBUG")]

However, if we look at Debugger.Break(), it doesn't use a ConditionalAttribute which means we cannot take advantage of this compiler feature.  The result of this is that you need to manually remove all the lines of code which calls Debugger.Break() before shipping the code to the client, or when not running it with a debugger attached (like running unit tests). Not great at all!!

Strange how this got missed, unless it was done for a reason?? 

Technorati tags: , ,

Labels: , ,

Linq to SQL - DataLoadOptions - Does it improve performance?

Sunday, August 12, 2007

Following on from my previous post on DataLoadOptions, I decided to do a quick test to see if it does actually improve performance, even with its limitations.

If we take this query, with DataLoadOptions only a single query is executed, while with it not set a query is executed per order.

DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Customer>(Customer => Customer.Orders);
db.LoadOptions = options;

var query = from c in db.Customers
                        select c;

            foreach (var c in query)
            {
                Console.WriteLine("{0}, Phone: {1} with {2} orders", c.ContactName, c.Phone, c.Orders.Count);
            }

Without using DataLoadOptions, it took 1,739,904 Ticks to execute, while with using them it took 1,487,722 Ticks. An improvement, but nothing ultra special.

If we consider this query, which if you have read the previous, causes a query to be executed for each order even with DataLoadOptions on:

DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Customer>(Customer => Customer.Orders); options.LoadWith<Order>(Order => Order.Order_Details);
db.LoadOptions = options;

var query = from c in db.Customers
                     select c;

         foreach (var c in query)
         {
             Console.WriteLine("{0}, Phone: {1}", c.ContactName, c.Phone);
             foreach (var o in c.Orders)
             {
                 Console.WriteLine("Order Details for OrderID: {0} with {1} lines", o.OrderID, o.Order_Details.Count);

                 foreach (var od in o.Order_Details)
                 {
                     Console.WriteLine("Product ID: {0}", od.ProductID);
                 }
             }
         }

Without using DataLoadOptions, it took 10,003,725 Ticks to execute.  While using DataLoadOptions, taking into account the time to setup the options, it took 6,536,320 Ticks.  Much clearer performance improvements when a larger number of queries are being executed.

There is definitely performance improvements to be had even with it not being able to load all the data in at the same time and so should be used when executing these kinds of queries.

However, what happens if we are not using the Order/Order Details information and simply just using Customer data?  Executing our first query again but without c.Orders.Count, with options set it takes 1,609,867 Ticks, without it takes 1,174,217 Ticks.  So, if your not using them, then there is additional overhead to be aware of.

Technorati Tags: , ,

Labels: , ,

Linq to SQL - DataLoadOptions (Previously DataShape)

DataLoadOptions allows you to specify which child entities you automatically want to be loaded with their parent entity. By defining how the data should be loaded, we can reduce the number of requests made to the database to retrieve information and thus improving performance of our application.
Take this code for example:

var query = from c in db.Customers
                  select c;

foreach (var c in query)
{
   Console.WriteLine("{0}, Phone: {1} has {2} orders", c.ContactName, c.Phone, c.Orders.Count);
}

This is a simple query which outputs the name, telephone number and the number of orders they have to the console.  There doesn't look to be anything wrong with this, however if we run SQL Profiler that a large number of queries are being executed.

The query process is that all the customers are loaded into memory, and then for each customer a query is made to the orders table to return how many records they have which is then counted in memory.  Having queries like this will soon put pressure on your database.

This is where DataLoadOptions plays a part.  We can define that when a Customer object is loaded from the database, that all the Orders for that Customer should also be loaded with this code.

DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Customer>(Customer => Customer.Orders);
db.LoadOptions = options;
var query......

Now if we re-run the application, instead of having a query for each customer to find out the number of orders, we just have a single query being executed which returns the Customer and Order information.

SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country],
[t0].[Phone], [t0].[Fax], [t1].[OrderID], [t1].[CustomerID] AS [CustomerID2], [t1].[EmployeeID], [t1].[OrderDate], [t1].[RequiredDate], [t1].[ShippedDate], [t1].[ShipVia],
[t1].[Freight], [t1].[ShipName], [t1].[ShipAddress], [t1].[ShipCity], [t1].[ShipRegion], [t1].[ShipPostalCode], [t1].[ShipCountry], (
    SELECT COUNT(*)
    FROM [dbo].[Orders] AS [t2]
    WHERE [t2].[CustomerID] = [t0].[CustomerID]
    ) AS [count]
FROM [dbo].[Customers] AS [t0]
LEFT OUTER JOIN [dbo].[Orders] AS [t1] ON [t1].[CustomerID] = [t0].[CustomerID]
ORDER BY [t0].[CustomerID], [t1].[OrderID]

Changing the foreach loop to output the order information will still only result in a single query being executed on the server.

foreach (var c in query)
{
    Console.WriteLine("{0}, Phone: {1}", c.ContactName, c.Phone);
    foreach (var o in c.Orders)
    {
        Console.WriteLine("OrderID: {0}", o.OrderID);
    }
}

However, it doesn't solve all of our problems.  If we wanted a query which provided details on the Order Lines within the ProductID being outputted, we could write something like this:  

foreach (var c in query)
{
    Console.WriteLine("{0}, Phone: {1}", c.ContactName, c.Phone);
    foreach (var o in c.Orders)
    {
        Console.WriteLine("Order Details for OrderID: {0} with {1} lines", o.OrderID, o.Order_Details.Count);

        foreach (var od in o.Order_Details)
        {
            Console.WriteLine("Product ID: {0}", od.ProductID);
        }
    }
}

You would have thought that by adding the following code we would be able to limit the number of queries:

options.LoadWith<Order>(Order => Order.Order_Details);

Sadly, this is not the case and will still cause a query to be executed for each customer, in fact it is actually for each order. According to Michael Pizzo on the product team, this is by design with this explanation:

In LINQ to SQL, in order to reduce the complexity and amount of data returned by the join query required to do span, only one association in any given query is actually spanned in. LINQ to SQL picks the deepest level association in the query to do the join in order to minimize the number of queries generated.  So, in this case, by adding a span between orders and order_details, you are basically "hiding" any span of orders.

DataLoadOptions can improve the performance of your application and something which you should remember when writing Linq to SQL queries.  However, if we are going more than one layer deep, you are still going to have a huge number of queries being executed.  Remember to run SQL Profiler to make sure your application isn't going to take out your production server(s).

Technorati Tags: , ,

Labels: , ,

Extending DataContext using Extension methods

Thursday, August 09, 2007

Extension methods are great as they allow you to extend any class within C# 3.0 with the instance of the class being passed in as a parameter. 

In C# 2.0 we had an initial way with partial classes which allows us to extend classes with our own custom logic, however it only worked if the other class was marked as partial.

I talked before how we can extend the DataContext created by the designer (NorthwindDataContext) by using partial classes,  however if we wanted a method on all of the DataContext object in our system, we would have to create a partial class for each of them separate. This would result in a lot of duplicate code and a maintenance nightmare.

partial class DataClasses1DataContext { //... }

partial class DataClasses2DataContext { //... }

The other solution would be to add a partial class to the DataContext and have it be inherited by the other objects in the system, however this doesn't work as the DataContext class is not partial.

The answer is, Extension methods. Extension methods allow us to extend any object in the system, even if it is marked as sealed.  They require the following:

  • Be in their own public static class
  • Method be public static
  • use the 'this' keyword with the object we want to extend as the first parameter.  Other parameters can be added after this.
  • Accessible from the calling object.  If they are in there own namespace, the namespace needs to be referenced via a using directive.

Using this, we can extend the DataContext class and have all of our own DataContext's in the system implement our new functionality.

The following code could be used and access any information from the DataContext as a parameter:

namespace MyExtension
{
    public static class Extension
    {
        public static void PrintConnection(this DataContext dc)
        {
            Console.WriteLine(dc.Connection.ConnectionString);
        }
    }
}

This could then be accessed by any child object in the system like this:

DataClasses1DataContext db = new DataClasses1DataContext();
db.PrintConnection();

We are limited to the public interface of DataContext, but this is still a very useful technique. This also applies to any other object within .Net.

Technorati Tags: , ,

Labels: , ,

Partial Methods in Linq

Sunday, July 29, 2007

Following on from my previous post, I noticed Linq takes advantage of the Partial Methods feature in the language so I thought I would take a closer look.

Within VS Beta 2, the DataContext and database objects contain a number of partial methods to allow you to hook into various methods which are called at certain points within the execution.  All of the definitions are in the datacontext.designer.cs file in a region called Extensibility Method Definitions.

The standard methods included within the data context are:

  • partial void OnCreated();
  • partial void InsertCustomer(Customer instance);
  • partial void UpdateCustomer(Customer instance);
  • partial void DeleteCustomer(Customer instance);

OnCreated() is called when the DataContext object is created, while the others are called on SubmitChanges().  Once SubmitChanges() are called, the DataContext will start processing any changes and calling the methods, if they have been implemented.  HOWEVER I found that if you extend one of the methods, then the action would never be passed to the database. This must be a bug, so something to be aware of.  It might be by design and you use this to override the logic - its not very clear.  If they do override the functionality and its not a bug, I don't like the fact that there is no distinction between a method which is a hook and a method which overrides functionality. If it was something like partial override void .... then it would be a lot better that how it is at the moment.

The database entities also have extension points.

  • partial void OnLoaded();
  • partial void OnValidate();
  • partial void OnCreated();
  • partial void OnOrderIDChanging(int value);
  • partial void OnOrderIDChanged();

These partial methods are a little bit more interesting.  OnCreated is called when an object is initialised.  When loading from a database, OnCreated is called first, then OnLoaded is called.  OnXChanging() is called when the property is changed, just before the value is stored in the internal variable, not when SubmitChanges is called. OnXChanged() is called after the value is stored.

The most important method is OnValidate() which is called just before the action is passed to the database.  Here, you can add custom/business logic to verify that the object (can access everything as you are inside the object, for example this.OrderID) is correct and valid before being saved.  If there is a problem, you can throw an exception and catch it by putting a try/catch around db.SubmitChanges(). Very useful feature.

 

All of the code I wrote can be found here : C# Sample Code

Technorati Tags: , ,

Labels: , ,

Partial Methods in C# 3.0 and VB.net 9.0

Partial methods are a new language feature in both VB.net and C# and are designed to enable lightweight event handling and hooks into the class.  The code samples are in C#, but I have provided a link at the bottom for the VB.net version - the principals are the same.

Few important points:

  • Partial methods can only be defined or implemented within a partial class.
  • They must return void however can accept arguments (ref parameters but not out parameters).
  • Must be private

Consider the following C# code which has a simple getter and setter for the name of a person.

partial class Person
{
        string name;

        public string Name
        {
            get { return name; }

            set
            {
                OnNameChanging(value);
                name = value;
                OnNameChanged();
            }
        }

        partial void OnNameChanging(string name);

        partial void OnNameChanged();

}

When a name is set, a call is made to the OnNameChanging method, passing in the value as a parameter. After the name has changed the OnNameChanged method is called.  But these methods are just placeholders/interfaces which could be implemented by a developer.

In a separate partial class we can then implement this functionality which will handle what we want to happen when the methods are called.

partial class Person
{
        partial void OnNameChanged()
        {
            Console.WriteLine("OnNameChanged()");
        }

        partial void OnNameChanging(string name)
        {
            Console.WriteLine("OnNameChanging(string name)");
            Console.WriteLine(name);
        }
}

Now, when we compile, everything will be merged and act as a single class. If we then set the name on the object, the following is wrote out to the console

OnNameChanging(string name)
Ben Hall
OnNameChanged()

But what what happens if no partial class implements the method body for a partial method? Well, the compiler removes calling reference from the code and does not get compiled. If the two methods where not implemented, the setter would look like this:

set
{
  name = value;
}

The result is that it is really effective to include calls to partial methods within generated code, like from a Linq DataContext, because if they are not taken advantage of then they are removed.

Looking forward to seeing how these will be used.

Links

C# Sample | VB.net Sample

http://blogs.msdn.com/wesdyer/archive/2007/05/23/in-case-you-haven-t-heard.aspx
http://blogs.msdn.com/vbteam/archive/2007/03/27/partial-methods.aspx

Technorati Tags: , ,

Labels: , ,