Castle DynamicProxy: create a new property with a custom attribute for XML serialization

若如初见. 提交于 2019-12-14 02:35:50

问题


I have a situation where I have some DTO classes that should be implemented like:

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

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

The problem is, these are actually DTO classes for REST XML resources. And the City resource may include the State resource inline, or it may simply provide the resource ID (a URI). I am handling access to the DTO via the Repository pattern and would like it to be transparent to clients whether State is lazy loaded or not (like how NHibernate does with it's entity classes).

So my current plan is to use Castle DynamicProxy to create a proxy object when the REST Repository detects that the class isn't fully "hydrated" (i.e. not everything is inline). The proxy object will know how to lazy load attributes as needed.

To actually implement this, however, the only thing I've come up with is to have backing attributes for all relationships and put the Xml attributes on those. So the strategy looks like this:

[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; }
}

Then the Repository object knows to set up the proxy object to either get the object from the StateBacking property and use that (inlined resource case) or do a REST request to lazily retrieve the State object (resource URI case, i.e. lazy) from the ID specified in the backing property.

Question

The issue is, this backing field is pretty ugly. What I would like is a way to have Castle generate a class that would have the backing property with the XmlElement attribute applied that I could pass to the XmlSerializer. Then my DTO classes could look more like the first example and wouldn't have to be aware that the actual serialising class has a backing property.

Is something like this possible with Castle or any other Proxy library?


回答1:


After going an interesting and completely wrong way, i think it is indeed possible to create a backing field that won't be seen by clients. Since proxying works by inheriting from the proxied class, any property on the derived class won't be seen in the scope of the original class. So mixins are the way to go:

Given Foo

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

and Bar

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

We can declare an interface that will let us retrieve the backing field and an implementation

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
    }
}

Then you just have to mixin both classes when proxying:

var pg = new ProxyGenerator();

var hasBarBackingField = new HasBarBackingField();

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

and intercept the call interesting you in order to return the backing 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();
        }
    }
}

The HasBarBackingField class should be built to return either the direct object or retrieve the referenced REST object. Hope this helps




回答2:


Based on what i've seen NSubstitute do i'd say it is possible, as long as your properties are virtual: http://nsubstitute.github.io/help/partial-subs/ . Creating a City class with virtual property State that is then resolved at runtime using the substitution pattern should be feasable

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

Definitely doable, but it's terra incognita from here on!



来源:https://stackoverflow.com/questions/22377908/castle-dynamicproxy-create-a-new-property-with-a-custom-attribute-for-xml-seria

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!