Mikhail Sukhov
|
Дата: 30.01.2011
Увидел такую подписку: КодplazaStream.DataBegin += (p) => OnPlazaStreamDataBegin(key, p); Она плоха тем, что при таком подписывании нельзя будет отписаться от события и объект будет продолжать висеть в памяти (не так страшно при существующих гигабайтах, но все же). Лямбда выражения создают "на лету" метод обработчик, от которого потом не отписаться. Чтобы производить правильное освобождение ресурсов, нужно от них отказать вообще и писать по старинке: КодplazaStream.DataBegin += OnPlazaStreamDataBegin; Я думаю, лучше сразу проговорить для чего вообще нужны лямбда. Они появились тогда, когда в .NET начал входить функциональный стиль. Для этого и только для этого их и придумали (явный пример - это LINQ). Я в своих примерах использовал лямбду не потому что так правильно. А потому что мне было лень писать правильно, и быстрее + короче написать как это сделали Вы. Но внутри S# я так конечно же не делаю, к чему призываю делать и в ПлазаТрейдере.[laugh]
|
|
Спасибо:
|
|
|
|
|
Mikhail Sukhov
|
Дата: 30.01.2011
skuvv Добавил класс с кодами ошибок плазы - PlazaErrors парсит код ошибки и возвращает текст ps ктото начал заниматься ошибками - пользуйтесь Я этим занимаюсь[blush]. Но пока нет времени доделать, так что большое спасибо, что помогли. Но раз уж сделали, вот и Вам code review: 1. public class Errors. Его не нужно сделать статическим? Нужен ли он для public? 2. Error_Code. Лучше параметры называть в C# стиле. 3. Dictionary<int, List<string>> List. Конечно же, это никакой не Лист. Но и Дикшинари тоже будет неправильно назвать. У этого поля должно быть осмысленное имя. 4. List.ElementAt(i) - лучше использовать List[i]. Так быстрее, особенно если это Dictionary. 5. BitConverter.GetBytes(Error_Code)[i] Можно написать проще - Error_Code.To<byte[]>()[i].
|
|
Спасибо:
|
|
|
|
|
aspirant
|
Дата: 30.01.2011
|
|
|
|
Mikhail Sukhov 1. Плохой код. Кодtry { _dataStream.Close(); } catch (System.Runtime.InteropServices.COMException e) { // Исправить обработку исключения System.Diagnostics.Trace.WriteLine("Exception {0:X}: {1}".Put(e.ErrorCode, e.Message)); } Исключения нужно перехватывать как можно выше. Кто будет вызывать Close, тот пусть и этим занимается. А так ошибка просто проглотиться. Я пока сделал такую заплату, чтобы можно было тестировать код (поэтому и оставил для себя комментарий). Вообще мне кажется, COMException нужно везде перехватывать и выдавать свои исключения, чтобы внешнему коду не видеть внутреннюю кухню. Если внешний код имеет дело с PlazaStream, то он и должен оперировать понятиями (читай методами, статусами и т.д.) PlazaStream. Mikhail Sukhov 3. Код_dataStream.TableSet.set_rev(plazaTable.Name, _currentRevision++); Это меняется на более элегантное, как подсказал R#: Код_dataStream.TableSet.rev[plazaTable.Name] = _currentRevision++; исправлю. Mikhail Sukhov 4. ReplicationStream и ReplicationScheme явно как то не так используется. Это же уровень метаданных, а именно таблица PlazaTable (таблица олицетворяет стрим данных, PlazaStream по этом олицетворению производит подключение). А получилось так, что эти два перечисления размазались по всем практически классам в Плазе. Нехорошо. Конструктора PlazaStream(PlazaTable plazaTable, TRequestType requestType) более чем достаточно. Зачем нужен еще один я не понял.
5. public void AddStream(string key, PlazaColumns schema, IEnumerable<PlazaColumn> columns) Выглядит как то не так. Зачем столько аргументов? Можно же ведь передать один PlazaTable и уже по нему создать стрим, и уже оперировать им в дальнейшем.
Иерархия данных в потоках репликации: ReplicationStream -> ReplicationScheme -> Table -> Field, т,е. у каждой плазовской таблицы (в PlazaTrader это PlazaColumns) есть только одно значение ReplicationStream и ReplicationScheme, поэтому я и счел нужным добавить эти поля в PlazaColumns, чтобы при создании PlazaStream значения ReplicationStream и ReplicationScheme можно было вычислить из PlazaColumns. Есть два варианта получения данных репликации. Во-первых, можно передать Плазе полный или урезанный набор полей какой-то таблицы (создавать "свои" таблицы с полями из нескольких таблиц нельзя). Второй вариант запроса данных - без указания схемы (см. P2ClientGate.doc стр. 18: "Клиент также может не указывать никакой схемы при открытии потока, в этом случае сервер будет отдавать все данные, которые публикуются в потоке (далее этот режим будет называться «получение данных по схеме сервера»)". Поскольку такой вариант подключения существует, я предусмотрел второй конструктор PlazaStream, в котором только передается ReplicationStream. При таком подключении при получении данных через события нужно будет проверять название таблицы, именно поэтому у CP2DataStreamClass второй параметр событий StreamDataInserted, StreamDataUpdated и StreamDataDeleted - это string tableName. AddStream(string key, PlazaColumns schema, IEnumerable<PlazaColumn> columns) я исправлю на AddStream(string key, PlazaTable plazaTable)
|
|
Спасибо:
|
|
|
|
|
aspirant
|
Дата: 30.01.2011
|
|
|
|
Mikhail Sukhov Увидел такую подписку: КодplazaStream.DataBegin += (p) => OnPlazaStreamDataBegin(key, p); Она плоха тем, что при таком подписывании нельзя будет отписаться от события и объект будет продолжать висеть в памяти (не так страшно при существующих гигабайтах, но все же). Лямбда выражения создают "на лету" метод обработчик, от которого потом не отписаться. Чтобы производить правильное освобождение ресурсов, нужно от них отказать вообще и писать по старинке: КодplazaStream.DataBegin += OnPlazaStreamDataBegin; Я думаю, лучше сразу проговорить для чего вообще нужны лямбда. Они появились тогда, когда в .NET начал входить функциональный стиль. Для этого и только для этого их и придумали (явный пример - это LINQ). Я в своих примерах использовал лямбду не потому что так правильно. А потому что мне было лень писать правильно, и быстрее + короче написать как это сделали Вы. Но внутри S# я так конечно же не делаю, к чему призываю делать и в ПлазаТрейдере.[laugh] Это пока временный вариант, потому что я пока еще не определился с окончательным набором параметров событий. По ходу написания добавил string key, а поскольку хотелось залить первый черновой вариант, оставил как есть. Свои делегаты я убрал вообще. Вы правы, они лишние. Все вызовы событий передал через SafeInvoke.
|
|
Спасибо:
|
|
|
|
|
skuvv
|
Дата: 31.01.2011
Mikhail Sukhov skuvv Добавил класс с кодами ошибок плазы - PlazaErrors парсит код ошибки и возвращает текст ps ктото начал заниматься ошибками - пользуйтесь Я этим занимаюсь[blush]. Но пока нет времени доделать, так что большое спасибо, что помогли. Но раз уж сделали, вот и Вам code review: 1. public class Errors. Его не нужно сделать статическим? Нужен ли он для public? 2. Error_Code. Лучше параметры называть в C# стиле. 3. Dictionary<int, List<string>> List. Конечно же, это никакой не Лист. Но и Дикшинари тоже будет неправильно назвать. У этого поля должно быть осмысленное имя. 4. List.ElementAt(i) - лучше использовать List[i]. Так быстрее, особенно если это Dictionary. 5. BitConverter.GetBytes(Error_Code)[i] Можно написать проще - Error_Code.To<byte[]>()[i]. Просто готовый был класс [smile] насчет 4. в Dictionary таким способом (List[i]) доступ к ключу не получить вроде
|
|
Спасибо:
|
|
|
|
|
aspirant
|
Дата: 31.01.2011
|
|
|
|
Михаил, Вопрос и комментарий. Вопрос: еще раз хотел вернуться, потому что не совсем для себя уяснил. Как вам видится (изначально виделась) работа клиентского кода с PlazaTrader в применении к плазовским стримам (потоками репликации). При запуске PlazaTrader из клиентского кода, PlazaTrader подключается - к стримам, чьи схемы расписаны в ini-файлах специальной папке?
- и/или клиентский код создает схемы программным путем на лету?
Во втором случае это будет происходить через создание экземпляра PlazaTable и передачу его в PlazaTrader или мы оставим PlazaStreamManager public? Сейчас он сделан public только в целях тестирования (чтобы можно было вызывать напрямую из тестового проекта). Комментарий: наверное, перечисление PlazaTableTypes будет лишним. Как я понимаю, оно сделано для проверки совместимости PlazaTable и PlazaColumn (см. внизу) Mikhail Sukhov Для чего нужен тип таблицы. Для того, чтобы предотвращать в пользовательском коде такие вещи:
plazaTrader.SecuritiesTable.Columns.Add(PlazaColumns.OrdersColumns.Direction)
и выбрасывать исключения при запуске программы. Но тогда подразумевается, что в PlazaTable можно передать колонки из разных наследников PlazaColumns, но с одинаковым PlazaTableTypes. А это не пройдет. Еще раз повторюсь: "синтетические" схемы из колонок разных плазовских схем (PlazaColumns) создавать нельзя. Корректным при наполненни PlazaTable набором PlazaColumn'ов будет проверять каждую PlazaColumn на одинаковость с PlazaTable по ReplicationStream, ReplicationScheme и Table(Name). Что вы на этот счет думаете?
|
|
Спасибо:
|
|
|
|
|
Mikhail Sukhov
|
Дата: 31.01.2011
|
|
|
|
aspirant Вопрос: еще раз хотел вернуться, потому что не совсем для себя уяснил. Как вам видится (изначально виделась) работа клиентского кода с PlazaTrader в применении к плазовским стримам (потоками репликации). При запуске PlazaTrader из клиентского кода, PlazaTrader подключается - к стримам, чьи схемы расписаны в ini-файлах специальной папке?
- и/или клиентский код создает схемы программным путем на лету?
Во втором случае это будет происходить через создание экземпляра PlazaTable и передачу его в PlazaTrader или мы оставим PlazaStreamManager public? Сейчас он сделан public только в целях тестирования (чтобы можно было вызывать напрямую из тестового проекта). И то и другое. При старте настройки PlazaTable берутся из уже созданных ini файлов. Если их нет, то создается минимально допустимый набор метаданных (как это было сделано с Quik таблицами) и по свеже созданным таблицам создаются соответствующие ini файлы. Если клиентский код модифицирует PlazaTable, его действия так же применяются и к ini файлам. Но мне не понятно эта иерархия. Стрим - репликация - таблица. Это реально нужная вещь, без которой Плаза не взлетит? Пока выглядит как избыточность. aspirant Но тогда подразумевается, что в PlazaTable можно передать колонки из разных наследников PlazaColumns, но с одинаковым PlazaTableTypes.
Я думаю, неправильно было изначально делать разные PlazaColumns с одинаковыми PlazaTableTypes. Я же не знал, что для одной сущности в Плазе триллион таблиц соответствует. Потому и написал изначально, Position Portfolio и т.д. Так что да, или его переделывать, или его выкидывать.
|
|
Спасибо:
|
|
|
|
|
aspirant
|
Дата: 31.01.2011
|
|
|
|
Mikhail Sukhov Но мне не понятно эта иерархия. Стрим - репликация - таблица. Это реально нужная вещь, без которой Плаза не взлетит? Пока выглядит как избыточность. Это не мои изобретения. Выдача данных клиентов Плазой строится следующим образом: есть несколько потоков репликации (FORTS_OPTINFO_REPL и т.д.). У каждого потока есть dbscheme. Вроде у каждого потока она одна, но пока не успел проверить точно. Зачем она нужна, если она одна, - ума не приложу. И наконец в каждом потоке есть от одной до несколько таблиц. Это их мы описывали наследниками PlazaColumns. Чтобы подписаться на потоки и получать данные, я могу просто указать имя потока (второй конструктор PlazaStream). В этом случае будут приходить ВСЕ данные потока, т.е. если в нем несколько таблиц, например в FORTS_FUTINFO_REPL их 17 штук, их данные будут приходить через событие CP2DataStreamClass.StreamDataInserted вперемешку. Второй вариант - это, когда я отправляю на сервер содержимое ini-файла таблицы с обязательным указанием потока репликации, dbscheme, именем таблицы и списка нужных колонок запрашиваемой таблицы. В этом случае, понятно, приходят только запрошенные колонки запрошенной таблицы. Из-за того, что процесс подписки на потоки построен именно таким образом, я считаю необходимым добавить в PlazaColumns public abstract ReplicationStream ReplicationStream { get; } public virtual ReplicationScheme ReplicationScheme { get { return ReplicationScheme.CustReplScheme; } } public abstract string Table { get; } У каждой таблицы потока репликации, т.е. наследника PlazaColumns, своя уникальная неизменная комбинация ReplicationStream + ReplicationScheme + Table. Она мне нужна для создания ini-файла. Букв много. Надеюсь, что не запутал окончательно.
|
|
Спасибо:
|
|
|
|
|
Mikhail Sukhov
|
Дата: 01.02.2011
aspirant public abstract ReplicationStream ReplicationStream { get; } public virtual ReplicationScheme ReplicationScheme { get { return ReplicationScheme.CustReplScheme; } } public abstract string Table { get; }
У каждой таблицы потока репликации, т.е. наследника PlazaColumns, своя уникальная неизменная комбинация ReplicationStream + ReplicationScheme + Table. Она мне нужна для создания ini-файла.
Букв много. Надеюсь, что не запутал окончательно.
Стало чуть понятнее. А где теперь делать проверку, что в таблицу попала правильная колонка?
|
|
Спасибо:
|
|
|
|
|
aspirant
|
Дата: 02.02.2011
Mikhail Sukhov А где теперь делать проверку, что в таблицу попала правильная колонка? Предлагаю у PlazaColumn сделать readonly поле Owner типа PlazaColumns, инициализировать его в конструкторе PlazaColumn. В конструкторе PlazaTable перебирать IEnumerable<PlazaColumn> columns и сравнивать у каждого элемента Owner.ReplicationStream, Owner.ReplicationScheme и Owner.Table c replicationStream, replicationScheme, переданными в конструктор PlazaTable. Как вариант, можно убрать из параметров конструктора PlazaTable параметры ReplicationStream replicationStream, ReplicationScheme replicationScheme, string name вообще и считать, что правильные параметры ReplicationStream, ReplicationScheme, [Table]name указаны в первом элементе IEnumerable<PlazaColumn> columns. Соответственно, проверку на правильность остальных элементов IEnumerable<PlazaColumn> columns проводить с значениями полей первого элемента. Не знаю, какой вариант правильнее?
|
|
Спасибо:
|
|
|
|