我有一种情况,我有一些应该实现的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或任何其他代理库可以实现这样的效果吗?
答案 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对象。希望这有帮助