У меня есть структура, которая позволяет пользователям выполнять запросы к определенному источнику данных (внутриигровая база данных Football Manager 2010, для тех из вас, кто заинтересован).
В этой структуре у меня есть два разных режима, в которых может работать моя структура: режим реального времени и режим кэширования. Я хочу, чтобы пользователи, использующие этот фреймворк, могли переключаться, просто вызывая другой конструктор (например, new Context(Mode.Cached)). Это должен быть единственный переключатель, который должен сделать пользователь, чтобы он мог по-прежнему иметь все те же вызовы Linq, но просто использовать режим кэширования, когда его приложение лучше подходит. Прозрачный.
Я решил, что использование PostSharp должно быть моим лучшим выбором, потому что:
- Создайте аспект для каждого свойства (которое уже было оформлено атрибутом)
- В этом аспекте проверьте, находимся ли мы в режиме
CachedилиRealtime - Вернуть значение либо из памяти, либо из кеша
Что ж, это работает. НО! Скорость недостаточно хороша. При выполнении следующих действий на 90 000 объектов:
foreach (Player p in fm.Players)
{
int ca = (short)ProcessManager.ReadFromBuffer(p.OriginalBytes, PlayerOffsets.Ca, typeof(Int16));
}
Это занимает всего 63 мс. (ReadFromBuffer — это высокооптимизированная функция, которая принимает byte[], int, Type и возвращает object), 63 мс — это очень разумно, учитывая большое количество объектов.
Но! В PostSharp я реализовал то же самое, используя это:
public override void OnInvocation(MethodInvocationEventArgs eventArgs)
{
if (eventArgs.Method.Name.StartsWith("~get_"))
{
if (Global.DatabaseMode == DatabaseModeEnum.Cached)
{
byte[] buffer = ((BaseObject)eventArgs.Instance).OriginalBytes;
eventArgs.ReturnValue =
ProcessManager.ReadFromBuffer(buffer, this.Offset, eventArgs.Method.ReturnType);
}
Теперь я называю это с помощью
foreach (Player p in fm.Players)
{
int ca = p.CA;
}
И это занимает 782 мс, то есть более чем в 10 раз больше!
Я создал аспект как:
[Serializable]
[MulticastAttributeUsage(MulticastTargets.Method, PersistMetaData = true)]
internal class FMEntityAttribute : OnMethodInvocationAspect
{
public FMEntityAttribute(int offset, int additionalStringOffset)
{
this.Offset = offset;
this.AdditionalStringOffset = additionalStringOffset;
}
//blah blah AOP code
}
И собственность оформлена как
[FMEntityAttribute(PlayerOffsets.Ca)]
public Int16 CA { get; set; }
Как я могу заставить это работать хорошо?!