Search code examples
c#.netgeneric-collectionsnvelocity

Choose base class property when a new property exists in derived class


I have a Table class that inherits a property from its base class DbTableBase, and hides the base class property MegaList<DbColumnBase> Columns with a more specific property MegaList<TableColumn> Columns.

Originally my app targeted Oracle only, but a recent refactoring into a common base so I could add SQLServer and other targets caused the Velocity templates to fail. My NVelocity template module is failing on any collections that have base implementations that are hidden by a derivation because it evaluates the base property.

  context.Put("table", table);  // table is Table, not TableBase

  ...

  #foreach ($c in $table.Columns)
  $c.Name
  #end

My container declaration is:

 public class MegaList<T> : ObservableCollection<T>
 {
     public MegaList() {} 
     // etc. etc.
 }

My Table type is has a _columns field that when exposed as a MegaList, NVelocity fails to iterate the leaf Table.Columns list:

public class Table : TableBase {

   MegaList<ColumnBase> _columns = new MegaList<ColumnBase>();

   public MegaList<ColumnBase> Columns   // template takes this one, even though it is hidden
   {
       get { return _columns; }
   }
}

public class Table : TableBase {

   new public MegaList<TableColumn> Columns   // Velocity ignores this, chooses base Columns
   {
      get { return _columns.DownCast<TableColumn>(); }
      set { _columns = value.UpCast<DbTableColumn, TableColumn>(); }
   }

   public MegaList<TableColumn> Columns2  // Velocity sees this 
   {
       get { return _columns;  }   
   }
 }

NVelocity is evaluating the base TableBase.Columns, yet if my template references $table.Columns2, NVelocity evaluates Table.Columns2.

I can't make the property virtual because the type is different, albeit a more specific Thinking out loud, I assume this behaviour is because VelocityContext holds "object" references, and has some sort of problem with choosing the correct property in the case of multiples, though I would assume it should choose the leaf property.


Solution

  • I proved that this is a bug / limitation in NVelocity. I downloaded the source for NVelocity 1.1.0 (where is 1.1.1 from Nuget?) and linked into my project and traced to the issue. The introspection code is a bit naive and returns the first thing it finds regarding a property, not checking to see if there is a better candidate. With .NET reflection, there may be a list of properties for a type with the same name but different DeclaringType. Especially if a property is not virtual, and has hidden a base property, it must check the propInfo.DeclaringType == type of object or correct OO is out the window.

    I patched NVelocity to work correctly with this aspect of C# inheritance. I will go through the codebase and see what else may need addressing regarding this. I will publish my patched version somewhere with improved documentation I hope.

    I am boggled/bothered by how slim the docs are out there regarding NVelocity, and how little evidence of work or maintenance is going on, nor where the latest source code resides. I've had enough frustration with lack of movement with this and other projects that I may fork it for my own sake since I have commercial products depending on it.