Wednesday, February 08, 2006

Conditional XML serialization

In my post about implementing entity object row state using 'is dirty' and 'cloning' I described how to exclude properties during binary serialization using [NonSerialized]. The same can be done with XML serialization using [XmlIgnore]. In addition, properties can be temporarily excluded when serializing to XML. The mechanism behind conditionally excluding properties from XML serialization is not well known, thus this post to shed some light on it.

The bool <property name>Specified mechanism is the .NET serialization framework answer to XSD minOccurs="0" applied by code generators such as XSD.EXE, XsdObjectGen, and others. Most developers actually implicitly use this mechanism when they design and use typed datasets with optional fields (columns).

In short, whenever .NET serializes the properties of a class to XML, it always checks to see if there exists a boolean metod whose name is the name of the property suffixed with Specified. If such a method exists, and it returns false, the property is not included in the serialized XML; if it returns true, the property and value is serialized. The oppsite logic is applied on de-serialization of optional XML elements.

Thus, you can implement and use Specified methods at will to get conditional XML serialization in .NET. The serializer does not discriminate between methods implemented by code generators and methods implemented by you. Partial classes in .NET 2.0 also makes adding such methods to code generated classes much simpler.

I got to know the details of how XSD.EXE handles XSD minOccurs="0" and xsi:nil="true" very well when doing "web-service contract first" in combination with InfoPath last year. The support for doing WSCF in .NET 2.0 has improved with the support for e.g. interface based .ASMX web-services, but Visual Studio 2005 still has a way to go to support WSCF to its maximum extent. I expect that this will be finally solved when WCF (Indigo) is released.

Sunday, February 05, 2006

Implementing "RowState" for Entity Objects

At my current project, we have some object model design discussions about "the how" for our entity objects (whose primary objective is "the what" of the domain), between the DataSet clan and the POCO clan. Our system is quite big, and must be designed to make it easy to provide future services to other parts of the organization (including Java-based systems) and to external partners. Support for web-services is a planned feature of the system, not to mention SOA.

Personally, I always like to design a system in a way that make the client application just another consumer of the system services. This promotes re-usability and low coupling, and is best achieved through a TDD approach. Thus, as you may guess, I am not in favor of using DataSets as the basis for entity objects, value objects, messages, or for DTOs. I recommend reading Scott Hanselman's famous blog post about using DataSets as business objects or as message elements in web-services. Also check out this MSDN Mag article, including the referenced blog posts at the end of the article.

Design-time data-binding of anything but DataSets was not simple in .NET 1.1, but with the advent of the object binding source and generics (List<T>) in .NET 2.0, it has become viable to do data-binding to entity objects and lists/collections.

The software design discussion now revolve around the RowState to signal the state of an entity object in relation to a containing list. Having a RowState property is really useful when e.g. implementing the 'Unit of Work' pattern. I have promoted that the 'cloning' and 'is dirty' mechanisms, plus a deleted flag as enough, while the DataSet clan advocates the need for a separate, setable row-state property, which will introduce some ambiguity in deducing the actual state of an entity object when received from a client.

I claim that the RowState need not be a separate, serializable property, it is just a read-only combination of the entity object identifier .Id, .IsDirty, and an .IsDeleted flag (the latter being the only setable property, the other two are read-only to the clients). Each entity object must have a class member that contains a clone of its original state. The .Id (internal, hidden entity identifier; PK) is assumed to be e.g. -1 when the object is not an existing entity in the data store. The state of an entity object is deduced like this:

  • Added: .Id == -1 && .IsDirty == true && .IsDeleted == false
  • Deleted: .IsDeleted == true && .Id != -1 (must exist to be deleted)
  • Detached: not applicable, entites are not defined by membership in any List<T>
  • Modified: .Id != -1 && .IsDirty == true && .IsDeleted == false
  • Unchanged I: .IsDirty == false && .IsDeleted == false
  • Unchanged II: .Id == -1 && .IsDeleted == true (aborted insert, no operation on this operand)
Thus, RowState is just a read-only property on the entity object. If your system requires the need for public setable row-state (like the new feature in ADO.NET 2.0), I recommend that you add an AdviceRowState property that the system clients can use to signal their intended state of the entity object.

Remember to keep any "how" properties away from the serialization of the "what" when using serialization as the basis for .IsDirty, otherwise such extra properties will cause bogus 'is dirty' logic. This includes any 'original values' clone that you may include in the entity object to support the .IsDirty method. As the 'is dirty' code uses the BinaryFormatter, use the [NonSerialized] attribute on all fields/class members that should not be comprised by the .IsDirty comparison.

You may also need to exclude some properties from XML serialization e.g. to prevent exposure in your web-services.
Apply the XML serialization control attribute XmlIgnore to such properties. You may also conditionally serialize properties such as the AdviceRowState property to XML. Add an extra control property bool AdviceRowStateSpecified and set it to false to exclude the actual property from XML serialization. Apply [XmlIgnore] to the AdviceRowStateSpecified property as you do not want it to be serialized anyway.

It should always be an internal aspect of your system how domain entities are stored, how you implement locking, how you implement long-running work/operations, etc ( see Data on the outside vs. Data on the inside by Pat Helland). The AdviceRowState property is just for giving the client the perception of having a say. The operations provided by your system should always make the client say please: "please update the account with this data", "please delete this order", etc.

The WinForms DataGridView still favors the use of DataSets as it has built-in support for DataTable.DataRow.RowState, e.g. automatically hiding deleted rows. This will not happen when using an object binding source bound to a generic list. Using two List<T> are one way of solving this, one for the deleted entities and one for the others. Use the latter as an object binding source, and the former to keep track of deleted entities. Pass both lists in the message to your service to save the changes (the lists are the 'operands' of the message).

Thus, the RAD model of Visual Studio favors developer productivity over good software design. Read 'Does Visual Studio Rot the Mind?' by Charles Petzold for more on this topic.

Thursday, February 02, 2006

VS2005 inherited user control, abstract factory/dependency injection pattern

Yesterday the need arised for inheriting a WinForms user control to implement four specializations of a user control. Creating inherited user controls is very simple in Visual Studio, just click 'Add-New item' and select 'Inherited user control', name the new user control appropiately and click 'Add'. I then selected the user control to derive from (note that the class must be plain public, while the constructor can be protected) and clicked OK.

VS2005 dutifully added the new user control files, but the VS designer failed with this error message:


One or more errors encountered while loading the designer. The errors are listed below. Some errors can be fixed by rebuilding your project, while others may require code changes.

Object reference not set to an instance of an object.

at System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
. . .
at Microsoft.VisualStudio.Design.Serialization.CodeDom. VSCodeDomDesignerLoader.DeferredLoadHandler.Microsoft.VisualStudio. TextManager.Interop.IVsTextBufferDataEvents.OnLoadCompleted(Int32 fReload)


Some googling did provided me with only a few similar problems, but none of the suggested solutions worked for me. Even adding two new user controls with no code or children would fail when trying to inherit one from the other.

The designer always calls the user control's parameterless constructor New and the control's Load event handler (provided that they exists), thus these methods should not do any operations that depend on some run-time resources.

I was quite puzzled when even added the two new plain user controls failed, as they of course had no constructor or load event handler. Then I noted that I could no longer open any user controls or forms in the whole solution. The VS designer had gone into a zombie state. A restart of Visual Studio, plus clearing the TFS workspace cache, corrected the VS designer problem for all the forms and the non-inherited user controls. Before I even tried to add a derived user control again, I did the recommended rebuild (see error message above) to ensure that coding errors did not cause the problem.

This time I added a standard user control and manually added the Inherits statement to the class code file and the designer partial class file. Then I opened the user control in designer mode, and I still got the famous designer error page, but this time with useful error details pointing me to a specific line in the base class.

The offending line was a private class member that created a biz-logic component using the abstract factory pattern:

Public Class GenericUCBase
. . .
Private _bizManager As IBizManager = ObjectFactory.BizManager()
. . .

Protected Sub New()

' This call is required by the Windows Form Designer.
InitializeComponent()
End Sub
. . .

The factory uses a Public Shared (static) method that creates objects using the dependency injection pattern (interface based) from pre-compiled assemblies deployed to the VS project location. It was the Type.GetType(typeName as String) method that failed when called from the private member initialization. I don't know why the VS designer failed to locate my assemblies during initialization, VS has no problems with non-derived controls, or with running or debugging the project.

All class members that are "declare initialized" gets called when the designer loads a user control, along with the constructor and the load event handler. Knowing this, I moved the initialization of the biz-logic component to my InitControl method, and this solved the VS designer problem.

The TFS workspace cache is located here:

\Documents and Settings\%user%\Local Settings\Application Data\Microsoft\Team Foundation\1.0\Cache

I clear it whenever I have problems with VS or TFVC, typically when the 'pending check-ins' list contains bogus entries from other solutions that cannot be removed in any other way.

Monday, January 30, 2006

System.Transactions LTM - promoting to DTC

In an earlier post I wrote about how the ADO.NET 2.0 System.Transactions LTM mechanism for SQL Server 2005 gets promoted from LTM into a full DTC transaction when more than one connections are opened against the same SS2K5 database (the resource). This is bound to happen when using TableAdapaters, as they open and close their own SQL connections using the configured common connection string.

One possible solution is to open a single, common SQL connection and pass it around to all methods in the biz-logic and data access logic (a data session object) to ensure that all SQL and TableAdapters use a single connection to stay within the LTM limitation (i.e. avoid getting DTC). The drawback of this is that it is contrary to "aquire late, release early" use of SQL connections, and also introduces explicit code to open/close a connection. Thus, it re-introduces the negative aspects of explicit transactions, making it hard to reuse the components e.g. in higher level processes and services.

An alternative, simpler solution is available when you use the TableAdapter only for reading data: use the TransactionScopeOption.Suppress option when setting up the ambient transaction context of your method. This ensures that the current LTM transaction will not be affected by the TableAdapter read/fill/get operation. It does not matter that the TableAdapater opens a second SQL connection automatically, promoting to DTC will not happen as the code is using two different transaction contexts:

Using transxScope As TransactionScope = New TransactionScope(TransactionScopeOption.Suppress)

'code that utilize TableAdapter for reading data here

transxScope.Complete()
End Using


You must review your usage of TableAdapaters and ensure that they can be used outside transactions. If you use them for updating the database, or if you depend on reads to be inside a transaction (e.g. for row/table locking purposes, transaction isolation level), then you are out of luck with LTM. These scenarios requires the use of DTC or, when you require LTM, the passing-an-open-connection-around solution. The latter can be caused by DTC not being allowed by the customer's operations department.