Archive for VB.NET

Highlighting DataGrid Rows

Posted in The Project with tags , , , , , on June 22, 2008 by moffdub

I have a story about one instance in which The Project‘s management’s refusal to upgrade from an old version of .NET cost them valuable developer time on a feature that is trivialized in subsequent versions.

When showing all of the equipment of a certain type, there were certain statuses, like “In use” or “Discarded”, that were not to contribute to the equipment count of a room. And in order to keep the counts consistent with the full listing, it would make sense not to show such equipment in the listings. But then you had no easy way to change something from “In use” back to “Spare”.

The solution was to show them anyway in the listing, but highlight their rows to set them apart from the rest of the equipment. Since the natural Windows Forms control for this task is a DataGrid, I set out on figuring out how to highlight specific rows of a DataGrid in .NET 1.1.

The version number is key. Being restricted to .NET 1.1, I was unable to use a DataGridView, which is a customizable DataGrid. This cut me off from handling the CellFormatting event of DataGridView and implementing a rather painless solution to the problem.

After a long search, I found an article that describes the solution. Since the article is fairly detailed and contains lots of other examples, I will summarize how to color a specific row of a .NET 1.1 DataGrid (refer to the article itself for code examples):

  • Define a new event with a new data type that will keep track of a cell’s row, column, background color, and font color. Make sure the new data type is passed by reference.
  • Handle this event in the form that contains your DataGrid. Do not use the handles keyword. This way, you can re-use the handler and you aren’t tied to a specific number of columns.

    In this handler, you provide the logic that determines which rows are colored. For us, we assume we have this info in a Hashtable somewhere. So we compare if the event’s row shows up in our Hashtable and is not selected. If so, set the background and font color properties of the event.

  • Inherit from DataGridTextBoxColumn and override the seven-argument Paint method. This method will get called every time a cell is drawn on the screen.
  • Now you fire the event you earlier defined, providing it with the current row and column, which you can get from inside the overridden Paint() and from Me.DataGridTableStyle.GridColumnStyles.IndexOf(Me).
  • Finally, go back to your form and replace instances of DataGridTextBoxColumn with your derived column. Use the AddHandler statement to add the event handler to handle the derived column’s newly-defined event.

    The event is handled in the form and its argument gets altered. After the RaiseEvent statement, you can inspect the argument to see if it was altered and set the appropriate brush color to pass to MyBase.Paint().

All of that to highlight a lousy row.

Problems

Well, fine, it might be an annoying exercise in polymorphism and event handling for me, but it did get the job done.

But there was a problem. The columns of this DataGrid had to be sortable. As it stood after the above steps, if you clicked on a column, the grid would sort based on that column, but there wouldn’t be a re-painting of the rows, so the wrong rows would be highlighted after a sort.

Solution:

  • Keep track of a need-to-repaint flag.
  • Handle the DataGrid’s Paint event. Check the flag and re-paint if the flag is true. By “re-paint”, I mean iterate through each row, examine the correct column(s) value(s) that determine if a row needs to be highlighted, and add that row number to the Hashtable. Be sure to set the flag back to false or you’ll be painting forever.
  • Handle the DataGrid’s OnClick event, not its OnMouseDown event. Perform a HitTest and see if the click happened on a column heading. If yes, set need-to-repaint to true.

Why not handle OnMouseDown? I was never fully sure if I correctly understood this MouseClick article:

Depressing a mouse button when the cursor is over a control typically raises the following series of events from the control:

1. MouseDown event.
2. Click event.
3. MouseClick event.
4. MouseUp event.

and this MouseDown article:

Mouse events occur in the following order:

1. MouseEnter
2. MouseMove
3. MouseHover / MouseDown / MouseWheel
4. MouseUp
5. MouseLeave

but I think it is because MouseDown occurs before MouseClick, so in MouseDown, let the re-sort happen with no re-paint. Then, in MouseClick, do a re-paint; this avoids re-painting too early.

OK, that is solved, but now whenever I have a substantial amount of data in a grid, there is a “flickering” effect whenever the grid is loaded or a column is sorted on.

Solution: in DataGrid’s Paint handler, hide the grid before doing the re-paint and then re-show it once finished.

OK, that is solved, but now, whenever a very substantial amount of data is in a grid, sorting by a column introduces a considerable multi-second delay before the grid is visible again.

Solution: paginate the data. Problem: we are in Windows Forms, not ASP.NET.

So we’ve followed this to its logical conclusion: now we’ll have to implement a custom paging solution in order to side-step this performance problem. I never actually got around to implementing this feature, and possible solutions are the topic of a separate post.

So there you have it. Refusal to upgrade to a newer version of .NET, even though the platform itself is rather high-level, cost us substantial development and test time. Don’t underestimate the value of upgrades.

Domain-Driven Reports, Part 4

Posted in The Project with tags , , , , , on June 6, 2008 by moffdub

Approach

This is the third, last, and chosen approach to the problem of domain-driven reports.

Save all of its cons, the previous approach seems to me to be more “domain-driven” and preferable to an Anti-Corruption Layer approach. But what about those cons?

To me, the first big problem is needing to write the report logic ourselves. I’m talking about grouping, outer joins, counts, summation, etc. In other words, all of the stuff relational DBs are great at doing.

The second big problem is instantiating possibly a huge amount of domain objects in order to utilize them. This can, of course, be solved by using some kind of “external processing” technique, in a throwback to the days of the mainframe. But that is yet more code to write, and why write it when an RDBMS likely addresses this problem already?

So, in this approach, we should push the report logic back to an RDBMS. Specifically, instead of the Room Summary Report Repository being persist-only, flip the script and make it retrieve-only.

Retrievals from this “repository” will populate a table such that the rows of that table can be fed directly into a reporting engine to produce the formatted report. Optionally, and by default, the retrieval operation will also return the domain object representation of the report.

Example

We will re-use the RoomSummaryReport and RoomEquipmentSummary objects from the previous approach. The only re-write is the ReportService.

The ReportService is outfitted with five, count them, five possible output formats: PDF, Snapshot, String, domain object, and DataSet. For the DataSet, we need to essentially do a SELECT * FROM X query because we are essentially using a .NET DataGrid as a viewer of data that is already ready to view (just like Access, without all the formatting capability).

Dogmatically, we should create a new class, perhaps DataSetReportViewer or something, that is responsible for this, because this really isn’t a Repository function. Since the code that follows is already a mouthful, I implement this in a retrieveDataSet() method in the RoomSummaryReportRepository.

First, an overview of what the class and method signatures look like now:

Public Class ReportService

	Public Sub New()

	End Sub

	Private Function runReport(ByRef user As User, _
		ByRef rooms As Room()) As RoomSummaryReport
		
		Dim reportRepo As RoomSummaryReportRepository = _
			RoomSummaryReportRepository.getReference()
			
		' by default, retrieve will populate the formatted table
		' AND return a RoomSummaryReport object; this is what the
		' boolean argument controls. runReport can expose this
		' to callers if needed in the future
		Return reportRepo.retrieve(user, rooms, False)
		
	End Function	

		
	Public Function generate(ByRef user As User, _
		ByRef rooms As Room()) As RoomSummaryReport
		...			
	End Function
	
	Public Function generateText(ByRef user As User, _
		ByRef rooms As Room()) As String
		...		
	End Function
		
	Public Function generatePDF(ByRef user As User, _
		ByRef rooms As Room()) As String
		...
	End Function
	
	Public Function generateDataSet(ByRef user As User, _
		ByRef rooms As Room()) As DataSet
		...		
	End Function
	
	Public Function generateSnapshot(ByRef user As User, _
		ByRef rooms As Room()) As String
		...
	End Function

End Class

First, a function to get a report object:

	Public Function generate(ByRef user As User, _
		ByRef rooms As Room()) As RoomSummaryReport
		
		Return runReport(user, rooms)
		
	End Function

Next, a function to generate a text report:


	Public Function generateText(ByRef user As User, _
		ByRef rooms As Room()) As String
		
		Dim retStr As String
		
		retStr = "Room Summary Report" & vbCrLf & vbCrLf
		
		Dim report As RoomSummaryReport = _
			runReport(user, rooms)
			
		Dim summaries() As RoomEquipmentSummary = _
			report.getSummaries()
			
		Dim roomEquipSummary As RoomEquipmentSummary
		
		For Each roomEquipSummary In summaries
		
			retStr = retStr & roomEquipSummary.getUser().getFullName() & _
				" has entered the following equipment " & _
				" in room " & roomEquipSummary.getRoom().getName() & _
				vbCrLf & vbCrLf
		
			Dim equipTypes() As String = _
				roomEquipSummary.getTypes()
				
			Dim equipCounts() As String = _
				roomEquipSummary.getCounts()
				
			Dim i As Integer
			
			For i = 0 to equipTypes.Length
				retStr = retStr & equipCounts(i) & " " & _ 
					equipTypes(i) & "s" & vbCrLf
			Next i
		
		Next roomEquipSummary
		
		Return retStr
		
	End Function

Let’s suppose the PDF engine can, somehow, be told how to generate a report from our report object. This function demonstrates how we aren’t tied down to row-based reporting engines with this approach.


	Public Function generatePDF(ByRef user As User, _
		ByRef rooms As Room()) As String
					
		Dim report As RoomSummaryReport = _
			runReport(user, rooms)
			
		Dim PDFEngineService As PDFReportGeneratorService = _
			PDFReportGeneratorService.getReference() 
			
		Return PDFEngineService.Create(report)
		
	End Function

DataSet… for display within the application…


	Public Function generateDataSet(ByRef user As User, _
		ByRef rooms As Room()) As DataSet
		
		' this populates the table, called X, with the
		' formatted ready-to-display report
		Dim report As RoomSummaryReport = _
			runReport(user, rooms)
			
		Dim reportRepo As RoomSummaryReportRepository = _
			RoomSummaryReportRepository.getReference()
			
		' retrieveDataSet here kind of acts like Access
		' does in that it just displays the contents of
		' a table that contains the formatted report
		' ...unlike Access, that is all it does
		
		' this could easily be replaced by a "SELECT * FROM X..."
		Return reportRepo.retrieveDataSet()
		
	End Function

Finally, a Snapshot format:


	Public Function generateSnapshot(ByRef user As User, _
		ByRef rooms As Room()) As String
		
		Dim SNPFileName As String = "RoomsReport" & user.getFullName() & ".snp"
		Dim rptName As String = "RoomsReport"
		
		Dim fileName As String = ' from some config file
		
		Dim access As New Access.Application
		access.Visible = False
		access.OpenCurrentDatabase(fileName)
		
		' this populates the table, called X, with the
		' formatted ready-to-display report
		Dim report As RoomSummaryReport = _
			runReport(user, rooms)
				
		Dim outPath As String = System.Environment.CurrentDirectory & _ 
			"\" & SNPFileName
		
		access.DoCmd.OpenReport(rptName, acViewPreview)
		access.DoCmd.OutputTo(acOutputReport, rptName, _
			acFormatSNP, outPath)
			
		access.CloseCurrentDatabase()
		
		Return outPath
		
	End Function

Pros

By producing both a row-based representation and a domain object representation,

  • We inherit the maintainability and clarity of the pure DDD approach.
  • The Report Service remains domain-driven because it returns domain objects in addition to taking them in.
  • The performance problem is avoided because the actual generation of the report is done in the DBMS, and in the case of The Project, server-side. Only the data for the report itself is sent back to the client. Loading potentially huge amounts of data to generate the report is no longer an issue.
  • We have assumed, throughout this series, that the reporting engine is row-based. If, down the road, our row-based engine is complemented or even replaced by one that operates in a different paradigm, we’re covered.

    In the example, I implement methods for textual, Snapshot, and PDF reports to emphasize the flexibility provided by having objects returned. We didn’t have this benefit with the Anti-Corruption Layer approach.

Cons

  • Still, we are mis-using the Repository idea. Now, we are in a retrieve-only situation.
  • Still, we could possibly face a Vietnam of OR/M mapping since we have a Report Repository moving between tables and domain objects.
  • The retrieval operation has a side effect. It is at least declared and made publicly known to consumers. This makes it possibly testable. Also, side effects are turned off by default, forcing consumers to explicitly state they want a side-effect-only function.
  • If you implement this with stored procedures, as I did, then you could argue that some of the report logic is still split across a process boundary, but I think this con is tenuous at best. The same argument can be made for all of my hand-written OR/M code, which I also wrote as s-procs.

I believe we have ended up with an acceptable list of cons here for formatted domain-driven reports.

Whew, that was a doozy. Problems always seem to occur at layer, technology, and paradigm boundaries. If only we could stay in fluffy flexible object land for everything.

Domain-Driven Reports, Part 3

Posted in The Project with tags , , , , , on June 3, 2008 by moffdub

Approach

This is the second approach I considered as a solution to the problem of domain-driven reports. Instead of trying to fit the status quo way of reporting in The Company into DDD, let’s instead try to fit the idea of a report.

Let’s continue with the idea of a Report Service. The Report Service should take in various domain objects as input (in our previous example, a User object and a collection of Rooms) and return domain objects as output.

Now, what is the nature of this output? It may seem counterintuitive to attempt to model a Report object. On the surface, it seems manufactured. However, Eric Evans, the man himself, employed a similar approach in his canonical shipping example. In a presentation (30 minutes in), Evans reforms a Routing Service that wrote into a database into a Service that created a new Itinerary object.


(recall from part 1)

It might be the case that this works in the shipping domain because itineraries have meaning to the domain experts. There are examples of other itineraries in other domains. The idea does not seem manufactured.

On the other hand, creating a Room Summary Report domain object might seem fabricated. It did, in the case of The Project, because domain experts didn’t toss around the term “room summary report” much. It was a new idea, but I think that had The Company treated The Project like it was a full-fledged software project, this term could have and would have entered the ubiquitous language.

That said, the approach is to follow Evans: create a Report Service that takes in a User object and a collection of Rooms and returns a Room Summary Report.

Model a Room Summary Report object, containing a collection of Room Equipment Summaries. Each Room Equipment Summary is composed of

  • a User object for which the Room Summary applies
  • a Room object for which the Room Summary applies
  • a collection of string-integer pairs, the string indicating the equipment type and the integer indicating a count of that equipment type for this Room and User

Finally, to take advantage of a system like Access, or any other row-based reporting engine, create a Room Summary Report Repository to persist the report in a way (de-normalized) that makes easy for that engine to format the report. This implies that the repository should not be used to retrieve Room Summary Reports. They should be generated by the Service.

Example

Foregoing the UML diagrams, I give you some sample implementations. First, the RoomSummaryReport domain object:

Public Class RoomSummaryReport
	Private summaries As IList
	
	Public Sub New()
		summaries = New ArrayList
	End Sub
	
	Public Sub addSummary(ByRef summary As RoomEquipmentSummary)
		summaries.Add(summary)
	End Sub
	
	Public Sub removeSummary(ByRef summary As RoomEquipmentSummary)
		summaries.Remove(summary)
	End Sub

	Public Function getSummaries() As RoomEquipmentSummary()
		Dim retArr(summaries.Count - 1) As RoomEquipmentSummary

		Dim i As Integer = 0

		For Each obj In summaries
			retArr(i) = obj
			i = i + 1
		Next obj

		Return retArr
	End Function
	
End Class

Now, the RoomEquipmentSummary domain object.

Public Class RoomEquipmentSummary
	Private room As Room
	Private user As User
	
	Private Structure EquipTypeCount
		Dim equipType As String
		Dim equipCount As Integer
	End Structure
	
	Private counts As IList
	
	Public Sub New()
		counts = New ArrayList
	End Sub
	
	Public Sub addEquipCount(ByVal equipType As String, _
		ByVal equipCount As Integer)
		
		Dim toAdd As New EquipTypeCount
		
		toAdd.equipType = equipType
		toAdd.equipcount = equipCount
		
		counts.Add(toAdd)
		
	End Sub
	
	' other requisite getters and setters ...
End Class

Finally, the ReportService, complete with simple report logic:

Public Class ReportService

	Public Sub New()

	End Sub
	
	Public Function generateReport(ByRef user As User, _
		ByRef rooms As Room()) As RoomSummaryReport
		
		Dim roomRepo As RoomRepository = _
			RoomRepository.getReference()
			
		Dim roomsOfInterest() As Room
		
		roomsOfInterest = roomRepo.retrieve(user)
		
		' we now have all rooms for which
		' user has entered equipment
		
		Dim equipTypeEnum As DynamicEnum = _
			EquipmentTypeEnum.getReference()
			
		Dim equipTypes() As String = _
			equipTypeEnum.convertToStrings()
		
		Dim returnedReport As New RoomSummaryReport
		
		Dim r As Room
		
		For Each r In roomsOfInterest
			Dim roomEquipSummary As New RoomEquipmentSummary
			
			roomEquipSummary.setUser(user)
			roomEquipSummary.setRoom(r)
			
			Dim equipType As String
			Dim equipCount As Integer
			
			For Each equipType In equipTypes
				equipCount = r.getEquipCount(equipType, user)
				roomEquipSummary.addEquipCount(equipType, equipCount)
			Next equipType
			
			returnedReport.addSummary(roomEquipSummary)	
		Next r
		
		Return returnedReport
		
	End Function

End Class

Some commentary here: First, why go with ILists in some classes and native arrays in others? No reason, could go either way.

Second, depending on the implementation of getEquipCount in the Room object, you might have several database calls jumping back and forth to get a count of a specific equipment type for each equipment type. Now, might not be a problem depending on your deployment environment (see Cons section below). Also, the object itself might be instantiated with the counts upon reconstitution in the Room Repository. In that case, there wouldn’t be a jump to the DB.

I will also forgo the Room Summary Report Repository, but I will give a sample of what the underlying table looks like, a table which can be fed into Access or the like to create the formatted report.

UserName RoomName EquipmentType Count
MoffDub Den Hats 24
MoffDub Den Jerseys 16
MoffDub Den PCs 10
MoffDub Den Textbooks 21
MoffDub Den DVDs 3
MoffDub Bedroom Hats 12
MoffDub Bedroom Jerseys 5
MoffDub Bedroom PCs 0
MoffDub Bedroom Textbooks 11
MoffDub Bedroom DVDs 5

Pros

  • From a dogmatic POV, this is a true DDD Service.

    From a practical POV:

  • This is likely to be easier to maintain. If we need to produce multiple interfaces for this report, like a ToolTip capability, we have an in-memory representation of the report as well we can use.
  • Having an in-memory representation makes it easier to re-use it to create tabular or textual representations of the report.
  • This is likely to produce clearer code.

Cons

This has serious practical problems:

  • We have introduced more code to be written.
  • The report logic now has to be hand-written. This is not a huge problem for a simple report such as this, but if it gets more complex, you’re in trouble.
  • More generally, this places a stress on the runtime memory if the amount of domain objects to summarize is potentially vast. This has performance implications, especially if, as in the case with The Project, the database server and the Report Service live on tiers separated by a network.
  • The potential for complexity can get nasty when it comes time to flatten the Report object via the repository, though this isn’t as serious of a OR/M problem as a true DDD Repository.
  • We’ve kind of abused the idea of a Repository because we can only persist with it. Perhaps it would be better named as a Service, but then you have a Service touching the DB. So we’ll settle with a semantically modified Repository.

The performance problems, combined with time constraints, convinced me to ditch this solution. Specifically, I couldn’t stomach coding all of the report logic myself. I have an external tool that excels in such logic. I can write the report in a somewhat declarative way (Access SQL), and be done with it. It didn’t seem like a wise engineering decision for my situation.

Don’t get this twisted. The nice DDD OOA&D way outlined here may work if the time constraints hadn’t existed and I had more control of the deployment environment. What remains, then, is to weigh the benefits of coding the report logic versus offloading the logic to a reporting engine.

OK, the next and final part of the domain-driven reports series will build on this post and present the solution I went with.

Domain-Driven Reports, Part 2

Posted in The Project with tags , , , , , on May 29, 2008 by moffdub

Approach

This is the first approach to solving the problem of domain-driven reports. The first thought is to continue creating reports just as we’ve been doing before. Assume The Project already exists as an external data source. How would you create the report?

Well, link appropriate tables in a .mdb file. Create a query in Access that will drive the report. Create the report in Access. Then use the Microsoft Access Object Library to launch the Snapshot file from your Windows Forms application.

To fit this approach in the DDD spirit, follow the Anti-Corruption Layer (page 82):

To do this, the Report Service, which resides in our domain model, will talk with a facade. The facade participates with an adapter to another facade, which talks with the Access Object Library. Calls in this library enable us to launch Access, create a report, and save it.

What are we doing? Essentially, we are treating Access as if it were a legacy application like a rule-based engine or a workflow system. If you view it that way, then the SQL that is the query that drives the report is not a leak between the domain layer and data access layer. It is conceptually part of the domain.

Example

An example adapter:

Public Class MSAccessReportAdapter

	Private adaptee As Access.Application
	Private rptName As String
	
	Public Sub New(ByVal fileName As String)
	
		adaptee = New Access.Application
		adaptee.Visible = False
		adaptee.OpenCurrentDatabase(fileName)
		
		rptName = "rptRoomSummary"
	
	End Sub
	
	Protected Overrides Sub Finalize()
	
		adaptee.CloseCurrentDatabase()
	
	End Sub

	Public Function generateNonSQLExpressibleTables(_ 
		ByRef user As String, ByRef rooms As String())
		
		Dim rm As String
		
		adaptee.DoCmd.RunMacro("Complicated", user)
		
		For Each rm In rooms
			adaptee.Run("SomeVBAProc", user, rm)
		Next rm
		
		Return True
		
	End Function
		
	Public Function generateReportFile(ByRef user As String) _
		As String
	
		Dim rptFileName As String = "RoomsReport" & user & ".snp"
		
		Dim outPath As String = System.Environment.CurrentDirectory & _ 
			"\" & rptFileName
		
		adaptee.DoCmd.OpenReport(rptName, acViewPreview)
		adaptee.DoCmd.OutputTo(acOutputReport, rptName, _
			acFormatSNP, outPath)
			
		Return outPath
		
	End Function

End Class

What the heck was that (forgive the literal strings)? Assume that the report we are generating requires a macro and some VBA to run inside of Access. This is often the case when the report is complex and requires logic that is not expressible in standard SQL. I know the example report I gave was trivial, but just pretend for now.

The end game here is to return a file name to the service requestor, so the application layer can launch the Snapshot viewer and display the report.

An example Report Facade:

Public Class ReportFacade

	Private accessAdapter As MSAccessReportAdapter
	
	Public Function generateReport(ByRef user As User, _
		ByRef rooms As Room()) As String
		
		Dim roomIDs() as String = rooms.ToIDArray()

		If Not accessAdapter.generateNonSQLExpressibleTables(user.getID(), roomIDs)
			Throw New Exception("croak")
		End If
		
		Return accessAdapter.generateReportFile(user)
		
	End Function

End Class

This hides running the macro, the VBA routine, and saving the report to disk. Forgive the shoddy exception-throwing.

And the Report Service, now trivial:

Public Class ReportService

	Private rptFacade As ReportFacade
	
	Public Sub New()
		rptFacade = New ReportFacade
	End Sub
	
	Public Function generateReport(ByRef user As User, _
		ByRef rooms As Room()) As String
		
		Return rptFacade.generateReport(user, rooms)
		
	End Function

End Class

Pros

  • It is relatively simple.
  • Pretty efficient performance-wise.

Cons

  • The fact that some of the logic of the report is in Access VBA and queries really stinks because the domain layer is now split over a process boundary.
  • Another problem is re-use. If the query used to drive the report needs to be used to represent the same report in, say, a tabular or textual format, that query has to be re-coded elsewhere.
  • It doesn’t feel right. It isn’t a true service because it has side effects
  • It doesn’t feel right. It isn’t a true service because it, while it takes in domain objects, it doesn’t return them.

Looking back, this wasn’t too bad of a solution. Still, it wasn’t the solution I went with. Part 3 will show another solution I passed on.

Garbage collector, you dropped this

Posted in The Project with tags , , , , on May 13, 2008 by moffdub

OK, I lied. I was sitting there getting a haircut and my mind drifted to a funky bug I had early during The Project, and realized I could crank a post about it out today. Yep, this is what I think about when I let my mind drift aimlessly. Pretty sick.

First consider this singleton class, which just happens to raise an event:

Public Class Singleton
	Public Event Bang()
	
	Public Sub New()
	
	End Sub
	
	Private Shared myself As Singleton
	
	Public Shared Function getRef() As Singleton
		If myself Is Nothing Then
			myself = New Singleton
		End If
		
		Return myself
	End Function
	
	Public Sub foo()
		RaiseEvent Bang()
	End Sub
	
End Class

Now consider this consumer of the Singleton class, which just happens to handle that event:

Public Class SingletonUser
	Private WithEvents x As Singleton
	
	Public Sub New()
		x = Singleton.getRef() ' fires x times!
		'x = New Singleton ' fires once
	End Sub
	
	Private Sub handleBang() Handles x.Bang
		Console.WriteLine("handling")
	End Sub
	
	Public Sub demonstrate()
		x.foo()
	End Sub
End Class

Finally consider this module:

Module Module1

	Sub Main()
	
		Dim y As SingletonUser
		
		y = New SingletonUser
		y = New SingletonUser
		y = New SingletonUser
		y = New SingletonUser
		y = New SingletonUser
		
		y.demonstrate()
	
	End Sub

End Module

The Common Language Runtime provides garbage collection. Up there in Main(), I have instantiated five instances of SingletonUser. I have a reference to one of them. The other four are unreachable. They are, then, candidates for garbage collection.

Therefore, I say, the output of this code should be “handling” once because the four unreachable instances are, as far as I’m concerned, blown away. Right? WRONG:

Wait, you say, garbage collection is non-deterministic. Maybe it just hasn’t gotten to clearing them out. False: go ahead and carpet-bomb the Main() method with System.GC.Collect(). It won’t help.

Moreover, If I instantiate a new Singleton in getRef(), as opposed to getting a reference to the existing copy, this problem does not occur! What is going on here?

Bewildered, I scoured the Web for answers. Here was the only clue I could find:

If the VB6 client sets the withevents variable x times, events will fire x times.

Can anyone explain this to me or is it a bug?

Could it be? Are the four unreachable SingletonUser instances escaping garbage detection? I fired up the VS.NET debugger and took a look:

Look at the member _invocationList of x.BangEvent. Five delegates instead of one! Oh snap. This is a memory leak in a garbage-collected runtime!

Apparently, since the one event of the singleton exists, all of its previously registered handlers exist too, regardless of the fact that the instantiations providing those handlers are out of scope. This doesn’t happen if a new singleton is created in getRef() because a new instance of the event is created with only one handler registered. When that handler’s object is unreachable, so is its singleton member x, and the entire block is blown away.

Having seemingly found the cause of the problem, I used Yahoo! Answers to confirm my thinking.

The fix is actually simpler: blow away the reference to the shared singleton before you lose a reference to the singleton user.

To wit:

Public Class SingletonUser

    Public Sub releaseX()
        Me.x = Nothing
    End Sub

    Private WithEvents x As Singleton
    ...
End Class

Module Module1

    Sub Main()

        Dim y As SingletonUser

        y = New SingletonUser
        y.releaseX()

        y = New SingletonUser
        y.releaseX()

        y = New SingletonUser
        y.releaseX()

        y = New SingletonUser
        y.releaseX()

        y = New SingletonUser

        y.demonstrate()

    End Sub

End Module

Now the expected result:

Now, you’re asking, how on Earth did you get to investigating this esoteric problem? This problem can be a huge deal if the Singleton class is a class in your application layer, and the SingletonUser is a Windows form.

That was the situation right before user testing for one of the sprints of The Project. Since The Project was a multiple document interface environment, one window could be opened and closed several times. Each window holds a reference to the “brain” of the application layer, which is a singleton. This gave bizarre results like error messages, confirmations, and entire windows popping up multiple times.

Yikes. This was double jeopardy: buggy behavior on the user end, plus a memory leak behind the scenes, slowly killing the machine’s virtual memory; as forms were being instantiated, they were not being destroyed by garbage collection as they were closed.

Some .NET bigshots will accuse me of being ignorant of the disposable pattern. I say that this is a poor feature of the garbage collector. In a garbage-collected runtime, most developers don’t give a whiff about memory management (the same can be said in non-memory-managed runtimes…eek).

This might be exactly as how Microsoft prescribed it, but in practice, this will likely be logged as a memory leak in the bug tracking system of the development shop. It doesn’t fit the widely-held mental model of what garbage collection does.

The Loose Coupling Impedance Mismatch

Posted in The Project with tags , , , , on May 12, 2008 by moffdub

The Object-Relational Impedance Mismatch is a renowned “Vietnam” of Computer Science. Trying to translate to and from relational table form and object form has proven to be notoriously difficult in complex situations.

In terms of The Project, the first release and subsequent feature release had a relatively simple object model, and I ended up writing the mapping code by hand, due mostly to time constraints.

There was, however, another higher-level impedance mismatch: the Loose Coupling Impedance Mismatch.

Motivation

The Project’s architecture includes a UI layer, an infrastructure layer, and a domain layer. Each of these has its own format for data:

  • Domain layer: object model, in the interest of domain-driven design, clarity, and correspondence to the real world
  • UI layer: flat dumb structs, in the interest of loose coupling. This way, a Windows form needs only to map a member of the struct to a control on the form.

    Why? Think HTML. HTML is given data and where to lay it out on the page. Other than that, it has no way of knowing what the data means. Likewise, a Windows form in The Project is kept dumb.

    This enables a future team of developers to migrate the app to an ASP.NET site with minimal effort. All they’d need to do is translate the forms into web pages and process the dumb structs.

  • Infrastructure layer: rows in tables in the interest of efficiency, reporting, and, honestly, this was a constraint. There were no corporate-approved OO databases available.

Very well. Now in the application layer, you end up with code like this when retrieving a polymorphic object from the database:

Public Function loadEquipment(ByVal equipmentID As Integer) As Equipment
	Dim generalRepo As GeneralEquipmentRepository = _
		GeneralEquipmentRepository.getReference()

	Dim equipTypeEnum As EquipmentTypeEnum = _
		EquipmentTypeEnum.getReference()

	Dim equipType As EnumValue = _
		generalRepo.getEquipmentType(equipmentID)

	Dim returnedEquip As Equipment

	Select Case equipType.ID
		Case equipTypeEnum.valueOf("General").ID
			Try
				returnedEquip = generalRepo.getByID(equipmentID)
			Catch ex As Domain.EquipmentNotFoundException
				RaiseEvent ShowEquipNotFoundDialog(equipmentID, "General" )
			End Try		
		Case equipTypeEnum.valueOf("Hat").ID
			Try
				Dim hatRepo As HatRepository = _
					HatRepository.getReference()

				returnedEquip = hatRepo.getByID(equipmentID)
			Catch ex As Domain.EquipmentNotFoundException
				RaiseEvent ShowEquipNotFoundDialog(equipmentID, "Hat" )
			End Try
		Case equipTypeEnum.valueOf("DVD").ID
			Try
				Dim dvdRepo As DVDRepository = _
					DVDRepository.getReference()

				returnedEquip = dvdRepo.getByID(equipmentID)
			Catch ex As Domain.EquipmentNotFoundException
				RaiseEvent ShowEquipNotFoundDialog(equipmentID, "DVD" )
			End Try
		...
		' and so on for however many equipment types ...
	End Select

	Return returnedEquip
End Function

Yuck. It reeks of a code smell and is begging for Replace Type Code With Subclasses.

What’s even more tragic is we already have subclasses: Hat, DVD, Textbook, etc. all inherit from Equipment.


(details omitted for brevity; click to enlarge)

And we are not using that fact here. On the other hand, this sort of translation from the relational paradigm to the object paradigm is necessary for loose coupling, regardless of our OR/M strategy (NHibernate, by-hand, whatever).

Going the other direction, consider taking one of the dumb flat structs captured in the UI and instantiating an object from it:


' NewEquipmentScreenInfo = dumb struct

Public Function createObjectFromNewEquipmentScreen( _ 
	ByRef screenScrape As NewEquipmentScreenInfo) _ 
	As Equipment

	Dim equipTypeEnum As EquipmentTypeEnum = _
		EquipmentTypeEnum.getReference()

	Dim returnedEquip As Equipment

	Select Case screenScrape.type

		Case equipTypeEnum.valueOf("General").ID
			returnedEquip = New Equipment
			returnedEquip.setName(screenScrape.name)
			returnedEquip.setDateAcquired(screenScrape.acquiredDate)
			returnedEquip.setRetailPrice(screenScrape.price)

		Case equipTypeEnum.valueOf("Hat").ID
			returnedEquip = New Hat
			returnedEquip.setName(screenScrape.name)
			returnedEquip.setDateAcquired(screenScrape.acquiredDate)
			returnedEquip.setRetailPrice(screenScrape.price)

			' setType will use techniques found in the
			' Dynamic Enumeration post to go from HatType,
			' which is a String, to an EnumValue
			returnedEquip.setType(screenScrape.HatType)
			returnedEquip.setColor(screenScrape.HatColor)

		Case equipTypeEnum.valueOf("DVD").ID
			returnedEquip = New DVD
			returnedEquip.setName(screenScrape.name)
			returnedEquip.setDateAcquired(screenScrape.acquiredDate)
			returnedEquip.setRetailPrice(screenScrape.price)

			returnedEquip.setTitle(screenScrape.DVDTitle)
			returnedEquip.setLength(screenScrape.DVDLength)
		...
		' blah blah blah

	End Select

	Return returnedEquip

End Function

Same problem, different layer boundary. Again, in the name of loose coupling.

For quite a while, the code base of The Project was littered with code like this in multiple places with the same comparison occurring multiple times. It worked, but I knew it was sinful. It was a good thing I was the only one who had to look at it. I would’ve been embarrassed to have another developer see what you just saw in production code.

It begged for refactoring, but I thought I was stuck: I already did have subclasses, and I could solve the problem if I broke my layering properties and made at least the UI understand the domain objects, but then I lose my nice loose coupling.

Hash tables of Polymorphic Conversion Services

Thanks to some advice on Yahoo! Answers and a new way of looking at an old foe of a data structure, the hash table, I came up with this design:


(to the rescue; click to enlarge)

Essentially, the inheritance hierarchy we are trying to convert to and fro is mirrored in a set of application-level services.

Now, the code looks like this:

In GeneralConversionService.vb:

Public Class GeneralConversionService
...
	Public Overridable Function convertFromInfraToObject( _
		ByVal equipID As Integer) _
		As Equipment

			Dim generalRepo As GeneralEquipmentRepository = _
				GeneralEquipmentRepository.getReference()

			Dim returnedEquip As Equipment

			Try
				returnedEquip = generalRepo.getByID(equipID)
			Catch ex As Domain.EquipmentNotFoundException
				RaiseEvent ShowEquipNotFoundDialog(equipID, "General" )
			End Try	

			Return returnedEquip
	End Function

	Public Overridable Function convertFromUIToObject( _
		ByRef scrape As NewEquipmentScreenInfo) _
		As Equipment

			Dim returnedEquip As Equipment

			returnedEquip = New Equipment
			returnedEquip.setName(scrape.name)
			returnedEquip.setDateAcquired(scrape.acquiredDate)
			returnedEquip.setRetailPrice(scrape.price)

			Return returnedEquip
	End Function
...
End Class

In HatConversionService.vb:

Public Class HatConversionService
	Inherits GeneralConversionService
...
	Public Overrides Function convertFromInfraToObject( _
		ByVal equipID As Integer) _
		As Equipment

			Dim hatRepo As HatRepository = _
				HatRepository.getReference()

			Dim returnedEquip As Equipment

			Try
				returnedEquip = hatRepo.getByID(equipID)
			Catch ex As Domain.EquipmentNotFoundException
				RaiseEvent ShowEquipNotFoundDialog(equipID, "Hat" )
			End Try

			Return returnedEquip
	End Function

	Public Overrides Function convertFromUIToObject( _
		ByRef scrape As NewEquipmentScreenInfo) _
		As Equipment
			Dim returnedEquip As Equipment

			returnedEquip = New Hat
			returnedEquip.setName(scrape.name)
			returnedEquip.setDateAcquired(scrape.acquiredDate)
			returnedEquip.setRetailPrice(scrape.price)

			returnedEquip.setType(scrape.HatType)
			returnedEquip.setColor(scrape.HatColor)

			Return returnedEquip
	End Function
...
End Class

I realize the bit of code duplication evident in the HatConversionService, but I am leaving it in there because it does not obscure my point here.

Now set up the comparisons…once.


Private conversionServices As Hashtable

...
' somewhere in an initialization routine

Dim equipTypeEnum As EquipmentTypeEnum = _
	EquipmentTypeEnum.getReference()

conversionServices.Add(equipTypeEnum.valueOf("General").ID, _
	GeneralConversionService.getReference())
conversionServices.Add(equipTypeEnum.valueOf("Hat").ID, _
	HatConversionService.getReference())
conversionServices.Add(equipTypeEnum.valueOf("DVD").ID, _
	DVDConversionService.getReference())
... and so on ... but only once ...
...

And here is some usage:

Public Function loadEquipment(ByVal equipmentID As Integer) As Equipment

	Dim generalRepo As GeneralEquipmentRepository = _
		GeneralEquipmentRepository.getReference()

	Dim equipType As EnumValue = _
		generalRepo.getEquipmentType(equipmentID)

	Dim service As GeneralConversionService = _
		conversionServices.Item(equipType.ID)

	Return service.convertFromInfraToObject(equipmentID)

End Function

Public Function createObjectFromNewEquipmentScreen( _ 
	ByRef screenScrape As NewEquipmentScreenInfo) _ 
	As Equipment

	Dim service As GeneralConversionService = _
		conversionServices.Item(screenScrape.type)
	
	Return service.convertFromUIToObject(screenScrape)

End Function

Sweet…the hash table maps from type IDs to conversion services. loadEquipment and createObjectFromNewEquipmentScreen are now effectively reduced to one-liners. The comparison logic is offloaded to a hash table, making the comparison effectively constant time (as compared to encapsulating the Select-Case statement into a method, which could potentially take linear time). The same elegance can be used when going from domain model to UI or to persistence. There, now I’m not embarrassed by the code.

What I am curious about is how XML serializers, used a lot in SOA, make the transition from objects to XML; do they use a similar mechanism, or is the schema designed in such a way to mirror the objects so closely that the translation is seamless? Even in that case, you will still need to match a subtype or type specified in an XML instance document with the correct class.

Anyway, in the coming days, I will be relocating. After all of the commotion is finished, I’ll decide on the next offering in the series on The Project.

Dynamic Enumerations

Posted in The Project with tags , , , , , , on May 9, 2008 by moffdub

One of the goals of The Project was to enable arbitrary reports. As a result, the amount of free-form text fields was kept to a minimum, and the amount of drop-down menus was as large as possible.

This, combined with the need to edit the contents and the order of the drop-downs at run-time, led me to consciously design a “pattern” of some kind to manage the complexity that would occur as more and more drop-downs were found hidden in the requirements.

The first inclination is to use VB.NET’s native enumerations like so:

Public Enum HatType
	Padres = 1
	Chargers
	College
	Other
End Enum

And we’re all familiar with its usage:

Dim superBowlHatType As HatType = _ 
	HatType.Chargers

The pattern and its derivation

We’d like this same consumer-side usage, except we cannot hard-code the enumeration. I did float the possibility of using System.Reflection, but quickly rejected because 1) I didn’t think it was possible 2) if it was, building a dummy-friendly interface for it would likely be complex, and 3) centralizing it for all users across The Company would be equally horrible.

So I set on designing a simpler solution. First, I thought of what I wanted the consumer-side to look like. Essentially, just like the native enumeration maps a human-readable symbol to a number, I want the new Dynamic Enumeration to do the same:

Dim hatDynamicEnum As DynamicEnum = _
	HatTypeDynEnum.getReference()

Dim superBowlHatType As EnumValue = _ 
	hatDynamicEnum.valueOf( "Chargers" )
' superBowlHatType is now 2

This leads to our first class: EnumValue:

Simple enough. ID corresponds to the value of the enumeration. Order is what order in the list it appears.

Our initial consumer-side sketch also implied that there is a DynamicEnum type with at least a valueOf method:

DynamicEnum is an abstract base class. The mappings from String to EnumValue is done with the use of a Hashtable. Furthermore, just as native enumerations have a range of acceptable values (example in this post ranges from 1 to 4), so does the Dynamic Enumeration. Any values outside of this range will throw an exception.

convertToStrings() gives a string array representation of the contents of the dynamic enumeration, sorted on order. This comes in handy when filling the contents of a ComboBox.

DynamicEnum is abstract because of the populate() method. Subclasses must define how the inherited Hashtable, min value, and max values are filled in.

Speaking of which, in an attempt to follow DDD, let’s define a data access class that will let us retrieve and set values from our persistent source, whatever it may be:

Let me pause a minute and talk about where EnumValue fits into DDD. Is it an Entity, or is it a Value Object?

If two variables have the same EnumValues from the same Dynamic Enumeration, then they are equivalent by virtue of their attributes. That sounds like a Value Object. On the other hand, I just defined a repository for it. That sounds like an Entity. So…I guess we could classify EnumValues as persistent Value Objects.

At this point, theoretically we have all we need to glue the in-memory data structure with its persistent representation. To adhere to the Single Responsibility Principle, though, I throw in a factory that will grab the values from the repository and populate the Hashtable, the min value, and the max value:

The build() method’s arguments are all by reference. Ideally, the build() method would take a DynamicEnum as its argument, but you’d run into a circular project dependence. Notice I also throw in getKeysSortedOnOrder()… kind of a kludge and it is there to avoid adding a dependency between DynamicEnum and its repository.

The reason why some classes are interfaces and the reason why there even is a DynamicEnumValueFactory will become apparent later in this post.

Let’s put it all together. Assume that HatTypeEnumValueRepository implements DynamicEnumValueRepository, HatTypeEnumValueFactory implements DynamicEnumValueFactory, and HatTypeEnum inherits from DynamicEnum.

From the consumer side, here is what would be necessary to set up the dynamic enumeration (one-time setup per process):

Dim hatTypeEnumValueRepo As HatTypeEnumValueRepository = _ 
	HatTypeEnumValueRepository.getReference()
	
Dim hatTypeEnumBldr As HatTypeEnumValueFactory = _ 
	HatTypeEnumValueFactory.getReference(hatTypeEnumValueRepo)
	
Dim hatTypeEnum As HatTypeEnum = _ 
	HatTypeEnum.getReference(hatTypeEnumBldr)

Here is a test scenario of loading what is already in persistence, then adding a new value to the enumeration at run-time and using it immediately after:

' Usage

' Assume contents in persistence at this point are:
'
' ID Name    Order
'  1 Padres    1
'  2 Chargers  3
'  3 College   2
'  4 Other     4

hatTypeEnum.populate()

Dim superBowlHatType As EnumValue = _ 
	hatTypeEnum.valueOf( "Chargers" )

' superBowlHatType.ID = 2
' superBowlHatType.order = 3

' inserts into persistence for this enum 
' with specified order 
' assumes persistence has auto-increment 
' capability turned on
Dim insertedValue As EnumValue = _ 
	hatTypeEnumValueRepo.addValue( "Hunting" , 5) 

hatTypeEnum.populate()

superBowlHatType = hatTypeEnum.valueOf( "Hunting" )

' superBowlHatType.ID = 5
' superBowlHatType.order = 5

In UML terms, here is the overall Dynamic Enumeration “pattern”:


(click to enlarge)

Comments

In practice, DynamicEnumValueRepository can be an abstract base class with implementations for the usual case (see below). Subclasses can then simply redefine the name of the table in which the contents of the dynamic enumeration can be found. The same commentary applies to DynamicEnumValueFactory. They are presented as interfaces to retain maximal generality. This is, after all, a “pattern”.

In most cases, one implementation class for each interface will suffice because the usual case, in my experience, is the following:

Table name: LocationEnum
ID Name Order
1 Outside 3
2 Inside 1
3 Underground 2

Occasionally, you will run into enumerations that are related. For example, a dynamic enumeration for car makes, and another for car models:

Table name: CarMakeEnum
ID Name Order
1 Toyota 3
2 Mazda 1
3 Ford 2
Table name: CarModelEnum
ID Name Order Make
1 Tacoma 2 1
2 Pre-Runner 1 1
3 T-Series 3 3
4 RX-8 4 2

In this case, a CarModelEnumValue class should inherit from EnumValue and define a make attribute. Similarly, CarModelDynEnumValueRepository should implement DynamicEnumValueRepository.

The same can be said for CarMakeEnumValue. I’d give this class a collection of car model Strings that, along with the CarModelDynEnum itself, CarMakeDynEnumValueFactory takes to construct CarMakeEnumValues. That way, it is trivial to have ComboBoxes that filter models based on the selected make.

This example is mainly why the “pattern” uses interfaces and an abstract base class. In most scenarios, one implementation will suffice, but you always have special cases where specific implementations are needed.

The next topic will probably be the Hashtable of Polymorphic Conversion Services! ooooh, sounds fancy…

Follow

Get every new post delivered to your Inbox.