Замок DynamicProxy: создайте новое свойство с пользовательским атрибутом для сериализации XML

У меня есть ситуация, когда у меня есть несколько классов DTO, которые должны быть реализованы как:

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

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

Проблема в том, что на самом деле это классы DTO для ресурсов REST XML. И ресурс City может включать встроенный ресурс State или может просто предоставлять идентификатор ресурса (URI). Я обрабатываю доступ к DTO через шаблон Repository и хотел бы, чтобы он был прозрачным для клиентов, независимо от того, загружен ли State с отложенной загрузкой или нет (например, как NHibernate делает со своими классами сущностей).

Так что мой текущий план - использовать Castle 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 для ленивого извлечения State объект (случай URI ресурса, т.е. ленивый) из идентификатора, указанного в свойстве поддержки.

Вопрос

Проблема в том, что это поле поддержки довольно уродливо. Я хотел бы, чтобы Castle генерировал класс, у которого было бы свойство backing с XmlElement Атрибут применяется, чтобы я мог перейти к XmlSerializer, Тогда мои классы DTO могли бы больше походить на первый пример, и им не нужно было бы знать, что фактический класс сериализации имеет свойство поддержки.

Возможно ли что-то подобное с Каслом или любой другой прокси-библиотекой?

2 ответа

Решение

Пройдя интересный и совершенно неправильный путь, я думаю, что действительно возможно создать вспомогательное поле, которое не будет видно клиентам. Поскольку прокси работает путем наследования от проксируемого класса, любое свойство производного класса не будет отображаться в области действия исходного класса. Так что миксины - это путь

Учитывая фу

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/. Создание City класс с виртуальным свойством State, который затем разрешается во время выполнения с использованием шаблона подстановки, должен быть выполнимым

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

Определенно выполнимо, но это терра инкогнита с этого момента!

Другие вопросы по тегам