0

我有一个 Table 类,它从其基类 DbTableBase 继承一个属性,并MegaList<DbColumnBase> Columns使用更具体的属性隐藏基类属性MegaList<TableColumn> Columns

最初我的应用程序仅针对 Oracle,但最近重构为一个公共基础,因此我可以添加 SQLServer 和其他目标导致 Velocity 模板失败。我的 NVelocity 模板模块在任何具有被派生隐藏的基本实现的集合上都失败,因为它评估基本属性。

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

  ...

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

我的容器声明是:

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

我的表类型有一个 _columns 字段,当作为 MegaList 公开时,NVelocity 无法迭代叶子 Table.Columns 列表:

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 正在评估基础 TableBase.Columns,但如果我的模板引用 $table.Columns2,NVelocity 会评估 Table.Columns2。

我不能使属性虚拟,因为类型不同,尽管更具体 大声思考,我认为这种行为是因为 VelocityContext 持有“对象”引用,并且在选择正确属性的情况下存在某种问题倍数,尽管我认为它应该选择叶子属性。

4

2 回答 2

1

我无法重现您看到的错误,如果有帮助,我已经在下面包含了我的测试代码,但是......

您将看到 a 的一个常见原因NVelocity.Exception.ResourceNotFoundException是您是否已在 Visual Studio 中捕获所有 CLR 异常并在调试器下运行代码。如果异常消息指出它找不到资源VM_global_library.vm,这是一个无害的异常,NVelocity 正在尝试为全局宏加载可选资源文件。因为 NVelocity 是 Java Velocity 的一个端口,所以异常通常用于 Java 库中的控制流。

[Test]
public void CustomObservableCollectionT()
{
    VelocityEngine velocityEngine = new VelocityEngine();
    velocityEngine.Init();

    Table table = new Table();
    table.Columns.Add(new TableColumn { Name = "Full Name" });
    table.Columns.Add(new TableColumn { Name = "Email Address" });
    table.Columns.Add(new TableColumn { Name = "DOB" });

    VelocityContext context = new VelocityContext();
    context.Put("table", table);

    using (StringWriter sw = new StringWriter())
    {
        velocityEngine.Evaluate(context, sw, "",
            "#foreach ($c in $table.Columns)\r\n" +
            "$c.Name\r\n" +
            "#end"
        );

        Assert.AreEqual(
            "Full Name\r\n" +
            "Email Address\r\n" +
            "DOB\r\n",
            sw.ToString());
    }
}
public class MegaList<T> : ObservableCollection<T>
{
}
public class Table
{
    private readonly MegaList<TableColumn> _columns = new MegaList<TableColumn>();

    public MegaList<TableColumn> Columns
    {
        get { return _columns; }
    }

    public List<TableColumn> ColumnList
    {
        get { return _columns.ToList(); }
    }

    public ObservableCollection<TableColumn> ColumnCollection
    {
        get { return new ObservableCollection<TableColumn>(_columns); }
    }
}
public class TableColumn
{
    public string Name { get; set; }
}
于 2014-04-19T10:36:33.003 回答
0

我证明这是 NVelocity 中的一个错误/限制。我下载了 NVelocity 1.1.0 的源代码(Nuget 的 1.1.1 在哪里?)并链接到我的项目并追踪到问题。自省代码有点幼稚,它会返回它找到的关于属性的第一件事,而不是检查是否有更好的候选者。使用 .NET 反射,可能有一个类型的属性列表,该类型名称相同但 DeclaringType 不同。特别是如果一个属性不是虚拟的,并且已经隐藏了一个基本属性,它必须检查 propInfo.DeclaringType == 对象类型或正确的 OO 是否在窗口外。

我修补了 NVelocity 以在 C# 继承的这一方面正常工作。我将浏览代码库,看看还有什么需要解决的。我将在某个地方发布我的修补版本,并希望得到改进的文档。

我对有关 NVelocity 的文档有多么纤细,工作或维护的证据多么少,以及最新的源代码所在的位置感到困惑/困扰。我已经对这个项目和其他项目缺乏进展感到非常沮丧,我可能会为了自己的利益而分叉它,因为我有依赖它的商业产品。

于 2014-04-20T10:13:44.503 回答