All I want for Xmas is more decorations... (or On aspect weaving)

Dec 18, 2009 at 10:03 AM

First of all I want to say that I absolutely love T4 for L2S, as it finaly allows me to do what I always felt was missing from standard dbml-based approach: aspect weaving.

dbml is great for what it was written - i.e., persistence mapping. However a rigid structure of dbml schema and of sqlMetal precludes introduction of other aspects. Lots of people have commented on absence of the serialization aspect support - and this has bee addressed to some extent by the present version of T4 templates.

I want to add 2 other aspects - browsability and initialization.

1. Browsability. Let me make a bold statement: L2S + PropertyGrid = RAD. Hooking up L2S objects to the ProperyGrid control allows for virtually code-free programming. A propert grid control is actually a very versatile little beast, allowing for lots of customizations. There is one little gotcha: it relies heavily on the objects and fields being properly decorated with attributes. Some of the most essential ones are BrowsableAttribute and TypeConverterAttribute.

Unfortunately there is no way to decorate the object properties, as these are auto-generated. therefore being able to add these attributes would be invaluable. By the way, one of the most common tasks in this regard is to mask non-value type properties - unless a type converter is specified. There is quite a bit of heuristics that can be written to eliminate writing boilerplate code on a grand scale.

2. Fields initialization. T4/SqlMetal initialises only read-only fields, and only by giving them default values for the relevant type. Quite often one wants to initialise fields differently. Of course there is always a possibility of doing that in OnCreated() method, but yet again that requires writing lots of boring repetitive code.

I am sure people can think of many other aspects that would be nice to weave into these objects.

As a temporary solution (made possible by T4!!!) I created an auxiliary xml file that supplements dbml and contains all those other aspects. I then modified T4 template to weave both dbml and aux file together. This works beautifully, but I have an uneasy feelings of two kinds:

1. I had to make pretty substantial changes to T4 template, making upgrading to new versions rather difficult;

2. There are no consistency checks of the auxiliary xml file ensuring that it remains in synch with dbml.

I am showing below an xsd for that aux file. Perhaps someone can think of a better way to achieve the aspect programming objectives stated above?

 

Thank you

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Types">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" name="Type">
          <xs:complexType>
            <xs:sequence>
              <xs:element maxOccurs="unbounded" name="Attribute" type="xs:string" />
              <xs:element maxOccurs="unbounded" name="Column">
                <xs:complexType mixed="true">
                  <xs:sequence minOccurs="0">
                    <xs:element maxOccurs="unbounded" name="Attribute" type="xs:string" />
                  </xs:sequence>
                  <xs:attribute name="Name" type="xs:string" use="required" />
                  <xs:attribute name="Browsable" type="xs:boolean" use="optional" />
                  <xs:attribute name="InitialValue" type="xs:string" use="optional" />
                  <xs:attribute name="TypeConverter" type="xs:string" use="optional" />
                </xs:complexType>
              </xs:element>
            </xs:sequence>
            <xs:attribute name="Name" type="xs:string" use="required" />
            <xs:attribute name="TypeConverter" type="xs:string" use="optional" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

 

and here is an exampe of the xml file:

<?xml version="1.0" encoding="utf-8" ?>
<Types>
  <Type Name="CounterpartyMasterAgreement" TypeConverter="CMAConverter">
    <Attribute>Serializable</Attribute>
    <Column Name="TradeId" InitialValue="156" TypeConverter="CMAConverter" Browsable="false">
      <Attribute>Serializable</Attribute>
      </Column>
      </Type>
</Types>
Mar 3, 2010 at 12:41 AM

I did something very similar to weave localization into the output.  The idea was that I'd create resource strings such as DB_EmployeeType_Name_Supervisor that I wanted to be localized.  so rather than EmployeeType.Name returning a simple string, it would perform a resource lookup on the stored string and then return that.  I did a quick and dirty version of what you did (no schema file, just freehand XML)  Looks like this:

<?xml version="1.0" encoding="utf-8" ?>
<Localization DataContext="MyDataContext" ResourceManager="MyApp.Resources.ExternalMessages.ResourceManager">
  <Table TypeName="EmployeeType">
    <Column Member="Name"/>
  </Table>
</Localization>

The property gettor then looks like this:

get { return <#=(data.Localized && column.Localized) ? (column.StorageValue + " != null ? (" + data.LocalizationResourceManager + ".GetString(" + column.StorageValue + ") ?? " + column.StorageValue + ") : " + column.StorageValue) : column.StorageValue#>; }

If you've discovered a better way to do this sort of thing, let me know as I'd be very interested!

 

Thanks,

Tim