Friday, February 20, 2009

Loading objects' fields/properties automagically

There is a very nice function in the new ASP.NET MVC code that allows a Controller to load an object from values passed in from a HttpRequest (UPDATE: called UpdateFrom, see ScottGu's post). It did a best guess on matching values to object properties, but most (99%?) of the time that is all you need. Well, when I started using Domain-Driven Design (DDD) and Test-Driven Development (TDD) on the latest additions to our Community Manager.NET product I wanted to find something similar - but I couldn't find anything as terse and useful.

So I did what any good dev would, I read the sourcecode and derived my own functions to do something similar, but with the SqlDataReaders that my repositories would be getting out of SQL Server (it almost feels like the ActiveRecord pattern ...). Below are the result, two LoadToObject() functions (and a PopulateTypeException class) in my BaseRepository class that give me the functionality I need. I'm putting these out there in case someone else needs this, and in the hope that any glaringly obvious errors might get picked up by others!

One issue I had with the Microsoft version of this function was they only load public properties of an object, not public fields. Now I know that in C# you can setup properties as easily as fields, but in VB it's still a hassle. I personally like using fields for simple value storing because it is clearer to see what they do in the code and leave more space for real method calls.

The final problem I ran into was that my custom Html data type needed to implement it's own TypeConverter so that the HTML in the SQL Server ntext field could be converted to a strongly typed Html type. But that can go into another blog post if people are interested.

    ''' <summary>
''' Populates an object's public fields and properties with values from a SqlDataReader.
'''
</summary>
''' <param name="readerToLoad">The SqlDataReader that needs to be loaded into the object.</param>
''' <param name="obj">The object we want to populate.</param>
''' <param name="objectPrefix">A prefix to the object field/property names. Tries to match using "." and "_".</param>
''' <returns>The object passed in with fields/properties loaded.</returns>
''' <remarks>
''' Does a best guess as to how to match names from the SqlDataReader to the object.
'''
</remarks>
Protected Function LoadToObject(ByVal readerToLoad As SqlDataReader, ByVal obj As Object, ByVal objectPrefix As String) As Object
Dim
newColl As New System.Collections.Specialized.NameValueCollection()
Dim x As Integer

If
obj Is Nothing Then
Throw New
ArgumentNullException("obj", "Object must have a value in order to be loaded to.")
End If

For
x = 0 To (readerToLoad.FieldCount - 1)
newColl.Add(readerToLoad.GetName(x), readerToLoad.GetValue(x))
Next

Return
LoadToObject(newColl, obj, objectPrefix)
End Function

''' <summary>
''' Populates an object's public fields and properties with values from a NameValueCollection.
'''
</summary>
''' <param name="valueCollectionToLoad">The NameValueCollection that needs to be loaded into the object.</param>
''' <param name="obj">The object we want to populate.</param>
''' <param name="objectPrefix">A prefix to the object field/property names. Tries to match using "." and "_".</param>
''' <returns>The object passed in with fields/properties loaded.</returns>
''' <remarks>
''' Does a best guess as to how to match names from the NameValueCollection to the object.
'''
</remarks>
Protected Function LoadToObject(ByVal valueCollectionToLoad As System.Collections.Specialized.NameValueCollection, ByVal obj As Object, ByVal objectPrefix As String) As Object
Dim
objType As Type = obj.GetType()
Dim objName As String = objType.Name
Dim exceptionList As New StringBuilder()
Dim props As PropertyInfo() = objType.GetProperties()
Dim fields As FieldInfo() = objType.GetFields()
Dim ex As PopulateTypeException = Nothing
Dim
prop As PropertyInfo
Dim field As FieldInfo

' try writing to the object's properties
For Each prop In props
'check the key, going to be forgiving here, allowing for full declaration or just propname
Dim key As String = prop.Name

If objectPrefix <> String.Empty Then
key = objectPrefix + key
End If

If
valueCollectionToLoad(key) = Nothing Then
key = objName + "." + prop.Name
End If

If
valueCollectionToLoad(key) = Nothing Then
key = objName + "_" + prop.Name
End If

If
valueCollectionToLoad(key) <> Nothing Then
Dim
conv As TypeConverter = TypeDescriptor.GetConverter(prop.PropertyType)
Dim value As Object = valueCollectionToLoad(key)

If conv.CanConvertFrom(System.Type.GetType("System.String", True, True)) Then
Try
value = conv.ConvertFrom(valueCollectionToLoad(key))
prop.SetValue(obj, value, Nothing)
Catch e As Exception
Dim message As String = prop.Name + " is not a valid " + prop.PropertyType.Name + "; " + e.Message

If ex Is Nothing Then
ex = New PopulateTypeException("Errors occurred during object binding - review the LoadExceptions property of this exception for more details")
End If

Dim
info As New PopulateTypeException.ExceptionInfo()

info.AttemptedValue = value
info
.PropertyName = prop.Name
info
.ErrorMessage = message

ex
.LoadExceptions.Add(info)
End Try
Else
Throw New
Exception(String.Format("No type converter available for type: {0}", prop.PropertyType))
End If
End If
Next

' now try writing to the object's public fields
For Each field In fields
'check the key, going to be forgiving here, allowing for full declaration or just fieldname
Dim key As String = field.Name

If objectPrefix <> String.Empty Then
key = objectPrefix + key
End If

If
valueCollectionToLoad(key) = Nothing Then
key = objName + "." + field.Name
End If

If
valueCollectionToLoad(key) = Nothing Then
key = objName + "_" + field.Name
End If

If
valueCollectionToLoad(key) <> Nothing Then
Dim
conv As TypeConverter = TypeDescriptor.GetConverter(field.FieldType)
Dim value As Object = valueCollectionToLoad(key)

If conv.CanConvertFrom(System.Type.GetType("System.String", True, True)) Then
Try
value = conv.ConvertFrom(valueCollectionToLoad(key))
field.SetValue(obj, value)
Catch e As Exception
Dim message As String = field.Name + " is not a valid " + field.FieldType.Name + "; " + e.Message

If ex Is Nothing Then
ex = New PopulateTypeException("Errors occurred during object binding - review the LoadExceptions property of this exception for more details")
End If

Dim
info As New PopulateTypeException.ExceptionInfo()

info.AttemptedValue = value
info
.PropertyName = field.Name
info
.ErrorMessage = message

ex
.LoadExceptions.Add(info)
End Try
Else
Throw New
Exception(String.Format("No type converter available for type: {0}", field.FieldType))
End If
End If
Next

If Not
(ex Is Nothing) Then
Throw
ex
Else
Return
obj
End If
End Function

''' <summary>
''' Contains exceptions that arise during the populating of an object by the LoadToObject() method.
'''
</summary>
<Global.System.Serializable()> Public Class PopulateTypeException
Inherits Exception

Public Class ExceptionInfo
Public PropertyName As String
Public
AttemptedValue As Object
Public
ErrorMessage As String
End Class

Public
LoadExceptions As New List(Of ExceptionInfo)
Private PopulateTypeException()

Public Sub New(ByVal message As String)
MyBase.new(message)
End Sub

Public Sub New
(ByVal message As String, ByVal inner As Exception)
MyBase.new(message, inner)
End Sub

Protected Sub New
(ByVal info As System.Runtime.Serialization.SerializationInfo, ByVal context As System.Runtime.Serialization.StreamingContext)
MyBase.New(info, context)
End Sub

End Class

Tuesday, February 10, 2009

Challenging software development blogs

I am not hugely keen on top 100 lists or similar trite listings of people, blogs or posts, however sometimes I notice that I have too many tabs open in Chrome (hello, 58?!) and need to itemise some of them. Many of the ones I keep open are blogs that challenge me enough that I want to read them frequently (I know ... I should be using an RSS reader, so call me old-school! ;).

I think these are some of the more challenging blogs* out there with regards to software development - read these and you will find your brain, assumptions and dealer-held beliefs under high-voltage attack.

* OK, so I am going to miss a couple of fantastic ones - what is important is that you check out the ones I do mention!

59459065_0ed25c902e_o

Joel on Software (visit)

Joel Spolsky of Fog Creek Software and Stack Overflow fame has had one of the most well-read and discussed software development blogs on the planet for years now. He is an original thinker in a market filled with duplicates, he also has a deep commitment to giving his developers great office space.

The Software Simplist (visit)

Udi Dahan's blog specialises in Service Oriented Architecture (SOA), but I read him for his refreshing view of domain-driven design (DDD). I got a new insight into DDD from his DDD & Many to Many Object Relational Mapping post.

Scott Bellware's Blog (visit)

Scott Bellware is probably the most hated/respected .NET rebel on the planet. His abrasive manner and obvious chip on his shoulder with Microsoft and the ALT.NET mainstream put some people off, but he has a deep insight into what Agile development is meant to be all about and a straightforward manner in sharing what he knows. He also seems pretty sure that most developers are learning impaired. A good example of his (more positive) thinking is Productivity: It Comes from Software Design Rather than Software Tools.

Noop.nl (visit)

Jurgen Appelo's software development management blog has some of the most pragmatic and interesting advice available for functional managers of developers. His company's situation is similar to Elcom's, so I appreciate his honest and smart insight into how to make Scrum work with large teams working on many disparate projects. He has useful posts like How to Handle Many Simultaneous Projects which apply Lean/Scrum principles to the real world of custom software development.

You'd think with all my video game experience that I'd be more prepared for this (visit)

Jason Yip from ThoughtWorks focuses his blog on lean manufacturing principles and how they apply to the reality of software development. A challenging thinker and opinionated professional, he manages to stay as radical as someone like Scott Bellware with a more pleasant demeanour. A typical (longer) post from him would be No matter how many times you say it, we still don't need a QA on the team.

Greg Young's Blog (visit)

Greg Young is a Microsoft MVP who has some very interesting opinions on domain-driven design, namely that the domain model is only there for write operations. Check out his DDDD: Master-Detail Question post for more details on this and how he uses a thin DTO layer to power his read-only UI pages.

Chad Myers' Blog (visit)

I first found out about Chad from a talk he and Jeremy Miller gave on Opinionated MVC (see videos here and here). Jeremy Miller developed StructureMap, which is a Dependency Injection/Inversion of Control framework. Chad is also the driving force behind the Virtual ALT.NET (VAN) group which has helped developers connect from places as far afield as Texas and Australia!

James Bach's Blog (visit)

Interested in software testing? Then you need to quickly head over to James Bach's blog and check out his controversial, but smart and honest posts on the topic. To understand where he is coming from, check out his Schools of Testing ... Here to Stay blog post.

So ...

Who did I miss? Leave a comment and let me know!

Thursday, February 05, 2009

The Fat Controller must die!

As most parents with little boys know the Fat Controller is a key figure in the Thomas the Tank Engine TV series (and books, toys, clothing, linen, bags, crockery, snacks, etc.). he is a cheery fellow, unfortunately prone to angrily shouting at train engines, but equally kindly and caring about his beloved vehicles (especially steam powered ones like Elizabeth the lorry). For the sake of my children I must point out that I have no problem with this jolly fellow.

fat controller

There is another domain where fat controllers exist, and that is the world of the MVC (Model-View-Controller) pattern. The pattern has experienced a renaissance recently in web applications, particularly because of Ruby on Rails, and now Microsoft's ASP.NET MVC offering.

Now these Ruby on Rails guys have been doing this for a little while longer than the ASP.NET guys, and one key paradigm became clear fairly early on:

"Try to keep your controller actions and views as slim as possible."

This was most clearly explained by Jamis Buck in his excellent Skinny Controller, Fat Model post back in 2006, which is still worth reading even if you don't use Ruby on Rails. More recently Ian Cooper has pointed out this is the same problem webforms had:

"This is the good old problem of domain-logic in code-behind that we had in ASP.NET webforms. Indeed Webforms are just another type of controller, a page controller, and switching to an application controller model does not remove the need for us to watch for domain logic creeping into the controller. There can be a temptation to believe that just because the controller is easier to test it is now safe to put domain logic in there. Do not fall into that trap."

In his book, Domain-Driven Design, Eric Evans identifies this as the Smart UI Anti-Pattern.

“Put all the business logic into the user interface. Chop the application into small functions and implement them as separate user interfaces, embedding the business rules into them. Use a relational database as a shared repository of the data. Use the most automated UI building and visual programming tools available.”

SmartUI
Image from David Hayden's blog

Eric is kind (and pragmatic) enough to point out when it is useful.

“A project needs to deliver simple functionality, dominated by data entry and display, with few business rules. Staff is not composed of advanced object modelers.”

Clearly ASP.NET's webforms model encouraged this sort of practice (it is just far too easy to do), but surely a controller is a safe place for this code? After all, a controller can use many views, so it is more clearly separated than code behind. The problem is that whilst application logic ("Which view do I show next?", "What domain object is handling this request?") makes perfect sense in a controller, there are too many examples of them being used as stores for business/domain logic - which is the province of the domain model.

Hang on, we probably have a (ubiquitous) language problem here. You see, the "Model" in MVC != the "domain model" in Domain-Driven Design (DDD). In fact we need to map the DDD concept of layered architecture to MVC in order to see what we truly have.

MvcMapToDdd

Instead of giving the Model layer a fully fleshed out domain and supporting infrastructure (repository and services), many ASP.NET developers seem to want to treat the Model as pure database infrastructure and DAL (like the one in the Smart UI graphic above). This leads to the problem of where to put the business logic, and the natural assumption is that it belongs in the controller ... which is like making the navigator the captain of the ship.

Ian and Jamis have much more (technical) stuff to say on this subject so I'd advise you to check their posts out if you're into .NET or Rails, respectively, but do take this message with you, the Fat Controller must die!