Замок 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});
Определенно выполнимо, но это терра инкогнита с этого момента!