Castle DynamicProxy:为XML序列化创建一个带有自定义属性的新属性

本文关键字:一个 属性 新属性 自定义属性 DynamicProxy XML 序列化 创建 Castle | 更新日期: 2023-09-27 17:50:07

我有一个情况,我有一些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如何处理它的实体类)。

所以我目前的计划是使用城堡DynamicProxy创建一个代理对象,当REST存储库检测到类不是完全"水合"(即不是所有的东西都是内联的)。代理对象将知道如何根据需要延迟加载属性。

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

问题是,这个后备字段非常难看。我想要的是一种方式,让城堡生成一个类,将具有支持属性XmlElement属性应用,我可以传递给XmlSerializer。这样,我的DTO类就可以看起来更像第一个例子,并且不必知道实际的序列化类有一个backing属性。

是这样的东西可能与城堡或任何其他代理库?

Castle DynamicProxy:为XML序列化创建一个带有自定义属性的新属性

在走了一个有趣的和完全错误的方式之后,我认为确实有可能创建一个不会被客户看到的后备字段。由于代理的工作方式是从被代理的类继承,派生类的任何属性都不会在原始类的作用域中看到。所以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());

并拦截您感兴趣的调用,以便返回支持栏

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

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

绝对可行,但从现在起这是未知领域 !