October 27, 2009, 7:37 pm
WPF data binding has built in support for IDataErrorInfo, so it is easy to display a validation error when a property has invalid data. However, sometimes we need to manually find all the validation errors on an object. A perfect example of this is when trying to save data. Often you will want to verify that there are no validation errors before allowing the save operation to proceed. Using the MVVM pattern it would be ideal to determine whether there are any errors purely within your model view. The default behavior of IDataErrorInfo does not give a collection of all current errors. Instead, it will just tell you if a given property has an error (ie: using IDataErrorInfo.Item), so all we have to do to find all the errors is enumerate through each property on the class and call the Item property with that property name as the argument. The GetValidationErrors function below does exactly that:
Option Strict On
Imports System.ComponentModel
Public Class ValidationHelper
''' <summary>
''' Checks for errors in <c>validatable</c> and returns all the errors found.
''' </summary>
Public Shared Function GetValidationErrors(ByVal validatable As IDataErrorInfo) As IEnumerable(Of DataError)
Dim errors As New List(Of DataError)
' Iterate through every property in the class
For Each prop In validatable.GetType.GetProperties
' If the property has an error, then add it to the list
Dim errorMessage = validatable(prop.Name)
If errorMessage IsNot Nothing Then
errors.Add(New DataError(prop.Name, errorMessage))
End If
Next
Return errors
End Function
''' <summary>
''' Represents a single error. It's a propertyName, errorMessage pair
''' </summary>
Public Class DataError
Private _propertyName As String
Private _errorMessage As String
Public Sub New(ByVal propertyName As String, ByVal errorMessage As String)
_propertyName = propertyName
_errorMessage = errorMessage
End Sub
''' <summary>
''' The name of the property that has the error.
''' </summary>
Public ReadOnly Property PropertyName() As String
Get
Return _propertyName
End Get
End Property
''' <summary>
''' A description of the property's error.
''' </summary>
Public ReadOnly Property ErrorMessage() As String
Get
Return _errorMessage
End Get
End Property
End Class
End Class
October 24, 2009, 2:43 pm
Josh Smith just made a blog post about XAML DataContext comments when using the MVVM pattern. He makes a great point, which is that in many cases it is not immediately obvious what the DataContext of a view is intended to be. A simple comment as Josh suggests will go a long way, but the downside is that comments create a maintainability issue. If you rename a model view, or refactor code so that a page, window or user control expects a different DataContext you also need to update the comment. Here’s an example:
<!-- DataContext = SampleModelView -->
<UserControl x:Class="SampleUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="300" Height="300">
<Grid>
</Grid>
</UserControl>
But now what if I change my mind and decide that instead of SampleModelView, this UserControl will have AlternateModelView as its DataContext? If I forget to update the comment then it is now a source of misinformation. What I would really like to do is somehow specify the expected data context type for a given UI element, so I created an attached property called ExpectedDataContextType. When the element is loaded, it will fail at runtime if the DataContext is not of the desired type. It looks like this:
<UserControl x:Class="TestControl"
xmlns:local="clr-namespace:ExpectedDataContextType"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="300" Height="300"
local:DataContextHelper.ExpectedDataContextType="{x:Type local:TestModelView}">
<Grid>
<TextBlock>Hello</TextBlock>
</Grid>
</UserControl>
The code is extremely simple. It just attaches a handler to the Loaded event and then checks the type. When the types do not match up it gives you a warning via a message box if you are debugging. I chose not to throw an exception because the exception gets covered up and just ends up as a tiny message on Visual Studio immediate window. When I am debugging and a control has the wrong DataContext I want to make sure I find out about it, hence the message box. You can always replace the message box code with something else though.
Option Strict On
Public Class DataContextHelper
Public Shared Function GetExpectedDataContextType(ByVal element As DependencyObject) As Type
If element Is Nothing Then
Throw New ArgumentNullException("element")
End If
Return DirectCast(element.GetValue(ExpectedDataContextTypeProperty), Type)
End Function
Public Shared Sub SetExpectedDataContextType(ByVal element As DependencyObject, ByVal value As Type)
If element Is Nothing Then
Throw New ArgumentNullException("element")
End If
element.SetValue(ExpectedDataContextTypeProperty, value)
End Sub
Public Shared ReadOnly ExpectedDataContextTypeProperty As _
DependencyProperty = DependencyProperty.RegisterAttached("ExpectedDataContextType", _
GetType(Type), GetType(DataContextHelper), _
New FrameworkPropertyMetadata(Nothing, AddressOf OnExpectedDataContextTypeChanged))
Private Shared Sub OnExpectedDataContextTypeChanged(ByVal obj As DependencyObject, ByVal args As DependencyPropertyChangedEventArgs)
Dim element = DirectCast(obj, FrameworkElement)
AddHandler element.Loaded, AddressOf OnElementLoaded
End Sub
Private Shared Sub OnElementLoaded(ByVal sender As Object, ByVal args As RoutedEventArgs)
Dim element = DirectCast(sender, FrameworkElement)
RemoveHandler element.Loaded, AddressOf OnElementLoaded
' Compare the expected type to the actual type
Dim expectedDataContextType = GetExpectedDataContextType(element)
Dim actualDataContextType = element.DataContext.GetType
If expectedDataContextType IsNot actualDataContextType Then
#If DEBUG Then
' The types don't match and debug mode is on so notify the developer that the element
' has the wrong data context
MessageBox.Show(String.Format("DataContext has type {0}. Expected {1}.", _
actualDataContextType.ToString, _
expectedDataContextType.ToString))
#End If
End If
End Sub
End Class
The end result is that you are still specifying what the type of your DataContext should be, but now it is actually enforced. The best part is that if the you rename your model view without updating the ExpectedDataContextType property you will get a compile error because the type no longer exists. If the type still exists then you have to settle for a runtime error.
October 17, 2009, 8:26 pm
I recently learned about a WPF converter library on CodePlex. They are all very flexible and it is pretty amazing what you can do with just a few converters. Here’s a sample usage of one of the converters from the project’s documentation:
<MultiBinding>
<MultiBinding.Converter>
<con:ExpressionConverterExtension>
<con:ExpressionConverterExtension.Expression>
<![CDATA[
{0} && {1} && {3} && !{2}
]]>
</con:ExpressionConverterExtension.Expression>
</con:ExpressionConverterExtension>
</MultiBinding.Converter>
</MultiBinding>
This MultiConverter returns true if parameters 0, 1 and 3 are true and parameter 2 is false. At first this looks pretty awesome, but I think it is a little dangerous for any team trying to maximize their test coverage. As soon as you start putting relatively complex logic right into your XAML you are basically writing code that cannot be tested. One of the fundamental principles of MVVM is that your view should be simple. If this {0} && {1} && {3} && !{2} logic is moved to a regular converter, or better yet (maybe) into a ModelView it becomes testable code.
October 17, 2009, 8:07 pm
Lately I have been detecting a growing sentiment that any use of MultiBinding is poor practice in MVVM (this thread on WPF disciples for example). However, I think people who say you should never use MultiBinding are missing its point. It is probably better to make a single binding to a FullName property than to multi bind to FirstName and LastName and then convert it into a full name. But there is still a scenario that demands the use of MultiBinding: bindings that need to update when more than one UI property changes. MultiBindings are not just used for passing more information to the converter, they also specify when the value needs to be updated and call the converter again. This is a much more important feature and it requires MultiBinding.
October 17, 2009, 7:44 pm
I held out for a while, but I have officially become a Git convert. Now that TortoiseGit has reached version 1.0 I can’t think of a single way in which SVN is better that Git. There are a lot of advantages, but by far my favorite is the distinction between “commit” and “push”. In any version control system it is good to commit as often as possible so that you have better version history, but in SVN every commit gets pushed to the server. This means that if you want to commit, but you are not ready to give the code to other developers (ie: the code does not belong on the trunk of the master copy) then you have to make a branch. In SVN it can be a major pain to do everything in banches because of poor (not to mention slow) merging functionality. With Git you can commit as many times as you want, then when your code is ready for the trunk, you can push it to the master server. Much more civilized! If you haven’t tried Git yet then you should. You will be impressed.
Here’s an article on learning Git for SVN users: http://git.or.cz/course/svn.html
October 17, 2009, 3:29 pm
Update (Jan 23, 2010): I have made a new post explaining a method that I think is better than any of those described here.
In part one I talked about how there is no true way to unit test your data access code under the standard definition of a unit test. However, I think it is useful to consider your database and your data access layer as a single unit when it comes to automated testing (read part one if you’re wondering why). Everything is a trade off though so there are two important drawbacks to hitting a real database in your tests:
- If a test fails you don’t necessarily know right off the bat whether it was your .NET code that failed or something to do with the database.
This can be a pain if you really like your unit tests to point out the exact issue when they fail, but I would say it is a very minor problem. Also, don’t you want to know when there’s a bug in your database anyways?
- It can be very slow to roll back the database to its original state after every test.
This one is the real kicker. It is important that each test executes as fast as possible, but in order to prevent cross contamination between your tests, the database needs to be restored to its original state after each test. Even though it is very fast to access a local SQL Server instance, the process of rolling back can be slow.
I will go over three methods for rolling back that database, but I only recommend the last one:
- Put each test in a transaction
Rolling back a SQL Server transaction is an appealing option. It is by far the fastest method and seems easy to implement. The problem is that a transaction belongs to a single session, so you can get yourself into deadlock if you have multiple connections. If you begin a transaction on connection A and alter a table with that connection, then try to read from that table with connection B, then you have deadlock. Using the default SQL Server isolation level (read committed) connection B will wait for connection A to end its transaction, but the transaction doesn’t end until the test is finished, which requires connection B to read the data.
Basically, using this method puts a major restriction on the actual implementation making it pretty much useless. There are other problems too as your test needs access to the connection so that it can begin and end the transaction. In many cases you want the connection to be internal, so this leads to bad design.
- Rebuild the database after each test
This certainly works, but it is really slow and nobody likes slow unit tests.
- Use SQL Server snapshots
In SQL Server 2005 and later you can make a snapshot of a database, and then restore the database to that snapshot at any time. Restoring the snapshot is much faster than rebuilding the entire database because the snapshot file doesn’t store the entire database, it just stores data that has been changed so that it can quickly be reverted. There are a few caveats to this approach. One is that snapshots are not available in SQL Server Express, you will need one of the non-free versions. The other is that you cannot restore a snapshot while the database has active connections, so you need to make sure you kill them before attempting to restore the snapshot after each test.
Using SQL, you can create a snapshot like this:
CREATE DATABASE SnapshotName
ON (NAME=DatabaseFileLogicalName, FILENAME='PATH_TO_NEW_FILE')
AS SNAPSHOT OF DatabaseName
And then you can later restore the database to that snapshot:
RESTORE DATABASE DatabaseName
FROM DATABASE_SNAPSHOT = 'SnapshotName'
Restoring a snapshot usually still has a delay of half a second or so, but it’s better than the alternatives.
- Update (Jan 23, 2010): Distributed Transactions
Assuming that the use of MSDTC is an option, this is likely the best choice. In is described in this article.
October 17, 2009, 11:16 am
As almost anyone who tries to unit test a database application will quickly discover, databases present a huge problem for unit testing. Strictly speaking, if you are testing your C# or VB code and you actually hit a real database, then it isn’t really a unit test. It is actually an integration test. However, I have found that it doesn’t really matter what you call it, the end result is that your tests are much more useful if they actually hit a real database. You don’t have to worry about whether the test failed because you screwed up your mock object or if the actual application is buggy and you get better code coverage because even broken SQL will lead to a failed test.
There are several methods that can be used to prevent your unit tests from actually using a real SQL Server database, but they all have their problems:
- Using an in-memory provider like SQLite
There is an Entity Framework provider for SQLite that allows you to interact with a database without using a network or even going to your file system. This could certainly increase the execution speed of your unit tests and makes it easy to prevent cross contamination in your tests, but they are still integration tests. The only difference is that you are now testing whether your code works on SQLite, rather than the DBMS that you will actually use in production. The problem is that all database systems have different behaviors and feature sets, so your tests are no longer valid if you use a different DBMS for testing. There is also currently no system in place to automatically generate the SQLite schema from your entity data model, so you will need to find your own way of doing that, or you have to manually maintain a separate SQLite schema. Gross. If you are going to use another provider, it needs to be specially designed to behave exactly like your production database (ie: a mock SQL Server provider) but to my knowledge no such providers exists (if I’m wrong, please let me know!).
- Mock the Entity Framework ObjectContext
If all you want to do is read data, then this works well and is easy to implement. Unfortunately, in the vast majority of cases, we also need to write data and that’s where this method gets tricky. Your mock ObjectContext needs to be able to track changes and save them to an in-memory repository. And again, you have to make sure that it behaves exactly like your production database. Because this method often involves either a huge wrapper or major alterations to auto-generated code (which means you also need to make your own generator or you’ll lose maintainability) the mock object itself is extremely complicated, leaving a high likelihood that it will have errors. Since the mock is so complicated one could argue that you are again doing integration tests, not unit tests. But this time instead of testing your code and the database, you are testing your code and the mock ObjectContext. Just like the SQLite example, this is much worse because you are testing whether your code integrates with something you will not use in production. If you are going to do integration tests anyways, then you might as well integrate with the real thing. This method could lead to faster executing tests, but don’t forget that a local SQL Server instance is actually extremely fast and might be just as good.
- Encapsulate your data access layer and then mock it
I see this response on message boards all the time. Whenever someone asks how they unit test their data access code someone will respond “You’re doing it wrong, put all of your data access code into a separate module that you can mock”. There are a couple problems with this. First of all, you still need to test the code in the data access layer. If you have a function in your DAL that executes a complicated LINQ to Entites query, then you want to test that query. Without using one of the techniques mentioned above, this requires hitting the database. Secondly, making your client code completely unaware of the data access layer’s implementation leads to some issues. Let’s pretend that my data access layer looks like this:
Public Interface IUsersModel
Function GetUsers() As IEnumerable(Of Users)
Sub Save()
End Interface
Public Class UsersModel
Implements IUsersModel
Private _context As New DataTestEntities
Public Function GetUsers() As IEnumerable(Of Users) Implements IUsersModel.GetUsers
Return _context.Users
End Function
Public Sub Save() Implements IUsersModel.Save
_context.SaveChanges()
End Sub
End Class
It’s pretty simple, the code just allows you to get a collection of users and save any changes you make. UsersModel correctly implements the interface using the Entity Framework. Then we also have a controller that accesses the DAL. It looks like this:
Public Class UsersController
Private _usersModel As IUsersModel
Public Sub New(ByVal usersModel As IUsersModel)
_usersModel = usersModel
End Sub
Public Sub ChangeFirstUserNameToFoobar()
_usersModel.GetUsers().First.userName = "foobar"
_usersModel.Save()
End Sub
End Class
UsersController has a dependency on IUsersModel, so when unit testing the ChangeFirstUserNameToFoobar method, we pass in a mock implementation of IUsersModel, but we cannot simply verify that Save() was called, we also need to know what is going to happen when Save is called. Specifically, we need some way of checking that the first user’s username was changed to “foobar”. This means that a mocking framework like RhinoMocks or Moq will not be sufficient. There must be a fake implementation of IUsersModel that keeps track of the changes that have been made. Now we are getting back into “mock the ObjectContext” territory because that’s basically what we will have done.
There is a definite trend here: each of the above methods is complicated enough that you lose the benefits of isolating your tests from the database. They are all integration tests. In every case you are testing your client code, plus the repository. Since you have to test a repository, it might as well be the real one. Of course, this presents its own challenges. You will want to use a local instance of SQL Server (or whatever DBMS you use) to keep the tests fast (and isolated from other developers) and you will need to roll back changes after each test. In subsequent articles I will look at how to deal with these issues.
Update: I have posted the second article: Unit testing an Enitity Framework DAL part 2: Rolling back the test database