【问题标题】:Castle DynamicProxy: create a new property with a custom attribute for XML serializationCastle DynamicProxy:为 XML 序列化创建具有自定义属性的新属性
【发布时间】:2014-04-18 03:28:59
【问题描述】:

我有一些 DTO 类应该像这样实现:

public class City
{
  public string Name { get; set; }
  public State State { get; set; }
}

public class State
{
  public string Name { get; set; }
}

问题是,这些实际上是 REST XML 资源的 DTO 类。 City 资源可能包含内联的 State 资源,或者它可能只是提供资源 ID(一个 URI)。我正在通过存储库模式处理对 DTO 的访问,并希望它对客户端透明,无论 State 是否延迟加载(就像 NHibernate 如何处理它的实体类一样)。

所以我目前的计划是在 REST 存储库检测到该类没有完全“水合”(即并非所有内容都是内联的)时,使用 Castle DynamicProxy 创建一个代理对象。代理对象将知道如何根据需要延迟加载属性。

然而,要实际实现这一点,我想出的唯一方法是为所有关系设置支持属性并将 Xml 属性放在这些关系上。所以策略看起来像这样:

[XmlType]
public class City
{
  [XmlElement]
  public string Name { get; set; }

  [ToOneRestRelationship(BackingPropertyName = "StateBacking")]
  public State State { get; set; }

  [XmlElement(Name = "state")]
  public ResourceBase StateBacking { get; set; }
}

[XmlType]
public class State
{
  [XmlElement]
  public string Name { get; set; }
}

然后 Repository 对象知道设置代理对象以从 StateBacking 属性获取对象并使用该对象(内联资源案例)或执行 REST 请求以延迟检索 State 对象(资源 URI 案例,即懒惰)来自支持属性中指定的 ID。

问题

问题是,这个支持字段非常难看。我想要一种让Castle 生成一个类的方法,该类将具有支持属性应用XmlElement 属性,我可以将其传递给XmlSerializer。然后我的 DTO 类可能看起来更像第一个示例,并且不必知道实际的序列化类具有支持属性。

Castle 或任何其他代理库是否可以实现类似的功能?

【问题讨论】:

    标签: c# xml proxy castle-dynamicproxy


    【解决方案1】:

    在采取了一种有趣且完全错误的方式之后,我认为确实可以创建一个客户看不到的支持字段。由于代理通过从代理类继承来工作,派生类上的任何属性都不会在原始类的范围内看到。所以 mixins 是要走的路:

    鉴于 Foo

    public class Foo
    {
        public virtual string  Name { get; set; }
        public virtual Bar bar { get; set; }
    }
    

    和酒吧

    public class Bar
    {
        public virtual string Name { get; set; }
    }
    

    我们可以声明一个接口,让我们检索支持字段和一个实现

    public interface IHasBarBackingField
    {
        Bar RetrieveBar();
    }
    
    public class HasBarBackingField : IHasBarBackingField
    {
        public HasBarBackingField()
        {
            // the constructor must contain ways to resolve the bar. Since
            // the class is built while proxying you should have all the data
            // available at this moment
        }
    
        public Bar RetrieveBar()
        {
            return new Bar(); // example, you could have a backing field somewhere in this class
        }
    }
    

    那么你只需要在代理时混合两个类:

    var pg = new ProxyGenerator();
    
    var hasBarBackingField = new HasBarBackingField();
    
    var options = new ProxyGenerationOptions();
    options.AddMixinInstance(hasBarBackingField);
    var test = pg.CreateClassProxy<Foo>(options, new BarInterceptor());
    

    并拦截您感兴趣的呼叫以返回支持 Bar

    public class BarInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            if (invocation.Method.Name == "get_bar")
            {
                var hasBarBackingField = invocation.InvocationTarget as IHasBarBackingField;
                invocation.ReturnValue = hasBarBackingField.RetrieveBar();
            }
            else
            {
                invocation.Proceed();
            }
        }
    }
    

    应构建 HasBarBackingField 类以返回直接对象或检索引用的 REST 对象。希望这会有所帮助

    【讨论】:

      【解决方案2】:

      根据我所看到的 NSubstitute 我会说这是可能的,只要您的属性是虚拟的:http://nsubstitute.github.io/help/partial-subs/。 创建一个具有虚拟属性 State 的 City 类,然后在运行时使用替换模式解决该类应该是可行的

      public class City
      {
        public string Name { get; set; }
        [StateId(10)]
        public virtual State State { get; set; }
      }
      
      var sCity = Substitute.For<City>();
      sCity.State.Returns((core) => {return null; // here you can access informations about the call});
      

      绝对可行,但从这里开始未知领域

      【讨论】:

      • 嗯,这样做的问题是状态必须返回一个状态,我不能用这个返回对象层次结构中更高的东西。这就是为什么我不会使用完全不同的属性。我还担心如何获得分配给这个新属性的静态属性。
      • State 属性将返回一个状态,除非我不理解您的评论,否则可以执行类似 sCity.State.Returns((core) => {return GetState(GetStateId()); // 获取状态 ID 将是一种检索属性值的方法 })
      • 哦,我明白你的意思了。是的,在 State 上进行代理很容易,我已经在这样做了。问题是,国家代理通过检查另一个财产来工作。这是存储库需要的属性,但 DTO 实际上不需要(上面的支持属性)。我宁愿在 DTO 中没有支持属性,但要删除它,我必须有办法以某种方式生成它。也就是说,我需要一种方法来生成我正在为其制作代理的类中不存在的新属性。
      • 你不能创建一个单独的实体来保存支持属性值吗?还是简单地使用缓存以从其滑动到期中受益并从 State 代理中引用一个密钥?
      • 好吧,我想我可以制作一个 Castle Mixin,其中我的额外“属性”只是所有支持属性的数组。但是我仍然会遇到 XML 序列化的问题。问题是,XmlSerializer 将 XmlElement 等作为它正在序列化的类的属性的属性。我需要一些方法来让 XmlSerializer 查看我的仅代理方法并从中读取属性。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-02
      • 2015-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多