Обмен данными между доменами приложений, выполнение запросов linq с обеих сторон и за ее пределами - PullRequest
1 голос
/ 30 марта 2011

Для начала я работаю над утилитой для анализа и просмотра журналов из стороннего приложения с явным требованием к чрезвычайной гибкости.

Для достижения этой цели я 'm сжимая LINQ до его полного потенциала (и, возможно, помимо этого: /).

Поскольку пользователь должен создавать свои собственные запросы через пользовательский интерфейс утилиты, мне нужно иметь возможность загружать и выгружать их динамически.Для этого я создаю фиктивные классы с помощью одного метода (буквально называемого «DoStuff»), который возвращает результат запроса (часто на основе LINQ, но могут быть исключения), компилируя их (через CSharpCodeProvider) во временныесборки, загрузка их на «одноразовый» AppDomain, выполнение запроса, а затем избавление от AppDomain и сборки.

И вот здесь срабатывают аварийные сигналы: LINQ + AppDomains = много возможностей для возникновения проблем.

Что еще хуже, может быть очень сложно точно знать, что будет работать в каждом домене, и что будет пересекать (или пытаться пересекать) границы домена.Я надеюсь, что пул знаний в stackoverflow может помочь пролить свет на это.

Итак, углубляясь в подробности, вот упрощенная версия того, что у меня есть:

На основной сборке (фактический файл .exe для утилиты), который будет загружен "обычным способом" пользователем, выполняющим его, у меня есть кое-что, похожее на это:

public abstract class LogEntry { // Should this inherit from MarshalByRefObject?
    protected internal abstract void Parse(StreamReader input);
    ...
    /* There are literally dozens of classes that inherit from LogEntry, dealing with different kinds of entries, and
     *   defining an appropriate implementation of Parse().
     * The logic to choose the appropriate sub-class would be within LogData's constructor (see below).
     */
}
internal class LogData: IEnumerable<LogEntry> { // Should this inherit from MarshalByRefObject?
    private List<LogEntry> loadedData = new List<LogEntry>();
    public LogData(IEnumerable<string> LogFiles) { ... }
    /* The enumerator for this class does some heavy-dutty work to find the log files and load them "as needed":
     * Each time a new entry is retrieved from the logs, it's saved on loadedData just before returning it;
     * When the enumerator is reset and used again, it goes through loadedData until exhausting it, and only then
     * goes back into actually loading (and saving) new data.
     * I'm not including the code here because it is ugly, verbose, and probably irrelevant to the question.
     */
}

(кроме некоторых форм, шаблон)код запуска и т. д.).Программа запросит пользователя предоставить коллекцию файлов журналов (используя FileOpenDialogs, текстовые поля, поддерживающие шаблоны имен файлов с классическими подстановочными знаками «*» и «?» И т. Д.) И использовать их для инициализации одного экземпляра LogData..

После этого пользователю предоставляется тонкий графический интерфейс для создания и точной настройки запросов, в том числе кнопка Doom's Day для запуска запроса.И вот тут начинается самое интересное.Идея состоит в том, чтобы сгенерировать код для представления пользовательского запроса, а затем обернуть его во что-то вроде этого (здесь я опускаю выражения «using» и другие вспомогательные элементы):CSharpCodeProvider для его компиляции (с предварительно заданным, настраиваемым списком ссылок) во временную сборку.Затем я загружаю сборку в «выбрасываемый» AppDomain, создаю экземпляр «что угодно», вызываю его метод DoStuff (передает ему мой экземпляр LogData), извлекаю результаты, выгружаю домен и избавляюсь от ассемблера.

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

1, LogData должен был бы наследовать от MarshalByRefObject, поэтому, когда это переданопо запросу пользователя (DoStuff), перечислитель по-прежнему выполняется на домене приложения «по умолчанию», и все работает правильно.

2,) Приведенное выше означает, что ни один экземпляр LogData никогда не пересекает границы AppDomain (обращения к его членам являются еще однимистория), но его записи (экземпляры LogEntry) делают, когда журнал повторяется из DoStuff.Так должны ли они быть сериализуемыми?Их было бы совсем не сложно де / сериализовать, хотя я чувствую, что это было бы как удвоение работы разбора.Будет ли работать, чтобы сделать их потомками MarshalByRefObject?

3, что бы ни возвращало DoStuff, его нужно будет скопировать, а не ссылаться, так что его можно будет использовать из основного AppDomain после выгрузки «выбрасывать» один,Таким образом, любой тип запросов, которые могут быть включены в их результаты, должен быть сериализуемым, а не MarshalByRefObject.

4, даже если DoStuff возвращает запрос LINQ (с чем-то вроде «return from entry in Input ...»), перечислитель запроса будет работать только в «выбрасываемом» домене и только отдельные элементы (которыеВ вышеупомянутом пункте я уже говорил, что они должны быть сериализуемыми в любом случае) фактически пересекают границы домена.

Верны ли эти выводы?Я что-то упустил?

Заранее благодарен за любой ответ.

PS: Исходя из комментариев Дэниела ниже, я думаю, мне придется немного углубиться в некоторые аспекты проекта:

  1. Хотя «построитель запросов» призван стать основным механизмом для определения запросов, программа также позволит пользователю вводить необработанный код, когда этого просто недостаточно (а такжедля тех пользователей, которые достаточно знакомы с программированием, которые предпочитают вводить некоторый код вместо навигации по выпадающим спискам, меню и другим причудливым вещам (что является значительной частью целевой аудитории проекта).

  2. Еще одна особенность, которую я не упомянул, заключается в том, что она позволит сохранять запросы для последующего использования.С запросами, представленными кодом C #, их сохранение тривиально (до компиляции они являются просто строками).Сохраняющиеся деревья выражений (или что-то похожее на них) могут быстро превратиться в большое препятствие.

  3. Это не первый раз, когда я погружаюсь в классы CodeProvider;динамическая загрузка сборок;ни LINQ.Но, вероятно, это первый раз, когда я углубляюсь в это;и, безусловно, в первый раз, когда я пытаюсь собрать все это вместе.Я знаком с потенциалом каждой из этих функций и довольно уверен, что то, что я пытаюсь сделать, будет относительно легким, как только сложные детали об их взаимодействии будут разобраны.Этот вопрос только о тех деталях, я могу решить все остальное оттуда.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...