Castle DynamicProxy:使用XML序列化的自定义属性创建一个新属性

时间:2014-03-13 11:57:28

标签: c# xml proxy castle-dynamicproxy

我有一种情况,我有一些应该实现的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)。我正在通过Repository模式处理对DTO的访问,并希望它对于客户端是否透明,无论State是否延迟加载(如NHibernate如何使用它的实体类)。

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

然而,要实际实现这一点,我唯一能想到的就是为所有关系提供支持属性,并将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对象(来自支持属性中指定的ID的资源URI情况,即lazy)。

问题

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

使用Castle或任何其他代理库可以实现这样的效果吗?

2 个答案:

答案 0 :(得分:1)

根据我所见过的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});

绝对可行,但从这里开始 terra incognita

答案 1 :(得分:1)

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

鉴于Foo

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

和Bar

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());

并拦截有趣的调用以返回支持栏

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对象。希望这有帮助