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存储库检测到类不是完全"水合"(即不是所有的东西都是内联的)。代理对象将知道如何根据需要延迟加载属性。
[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属性。
是这样的东西可能与城堡或任何其他代理库?
在走了一个有趣的和完全错误的方式之后,我认为确实有可能创建一个不会被客户看到的后备字段。由于代理的工作方式是从被代理的类继承,派生类的任何属性都不会在原始类的作用域中看到。所以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});
绝对可行,但从现在起这是未知领域 !