Дипломна работа Система за управление на инвестиционен портфейл Дипломант: Иван Драгоев, F09895 Ръководител: доц д-р Станислaв иванов



страница5/5
Дата27.04.2017
Размер0.56 Mb.
#20004
ТипДиплом
1   2   3   4   5

Софтуерни изисквания


При разработването на приложението трябва да се вземат предвид следните изисквания:

  • Цялата система е базирана на Windows системите;

  • Данните трябва да се съхраняват в база от данни

  • Надграждането на системата да става по лесен начин;

  • Да се използват свободни за употреба компоненти;

  • Да има поддръжка на различни езици на потребителския интерфейс.

Конвенции за именуване и стандарти за кодиране


При имплементирането на даден проект е много важно всички програмисти да използват едни и същи конвенции при кодирането на програмата. Това води до унифициране на стила на писане, както и подобрява общата четливост на изходния код. По-долу са описани основните правила, които прилагам при писане на изходния код. За случаите, за които не е дефиниран стандарт за кодиране, програмистът е свободен да форматира кода по начин, който да е лесен за четене от друг програмист. За предпочитане е кодът да е лесен за четене и разбиране и по-малко ефективен, отколкото по-ефективен, но труден за четене.

Именуване


Всички наименования трябва да са на английски език.

  • Класове (Classes) – всяка дума от името е с главна буква: Person, MenuItem, InvestmentFrame;

  • Член-данни (Member fields) –започват с малка буква, a останалитe думи са с главна буква: firstName, ssn;

  • Свойства (Properties) – всяка дума е с главна буква. Ако се използва поле, в което да съхранява стойността, то името на полето трябва да е с малка първа буква и да е private;

  • Методи (Methods) – всички думи от името са с главна буква.

  • Пространства на имената (Namespaces) – Всяка дума е с главна буква;

  • Имена на файлове – всеки файл съдържа само един клас. За име на файла се използва името на класа;

  • Имена на проектите:

    • Data – проект за data access;

    • BusinessRules – проект за бизнес правилата;

    • Façade – проект за фасадата;

    • WinUI – проект за Windows базирания потребителски интерфейс;

    • WebUI – проект при последващ WEB интерфейс;

Отместване (Indentation)


  • Съдържанието на блока е отместено с едно ниво надясно;

  • Скобите са на едно ниво с декларацията на блока;

  • Съдържанието на case блока е отместено с едно ниво спрямо case-a;

  • Етикетът на case блока е отместен с едно ниво спрямо switch декларацията;

  • Скобите са винаги на нов ред с изключение на get и set методите на property и то само, ако са от вида:

    • get{ return }

    • set{ = value }

  • Ключовите думи else, catch и finally са винаги на нов ред;

Интервали


  • При деклариране на метод, между аргументите и скобите има един интервал: int Method( int input );

  • При извикване на метод, между аргументите и скобите има един интервал: Console.WriteLine( "In Method" );

  • При изрази, type casts и условни изрази се слага един интервал между скобите и аргументите: return a * ( b – a ); ( int )list[0]; if( a > b );

Именуване на обекти в базата данни


Имената на обектите в базата данни са само с главни латински букви и английски имена. Дефинираните потребителски типове имат префикс ‘D_’. Имената на таблиците започват с трибуквен префикс на модула следван от долна черта и името на таблицата: ‘COM_REGISTER’, ‘BSE_ISSUES’…

За всяка таблица се създава изглед ‘View’, който носи името на основната таблица, и префикс ‘VW_’: ‘VW_COM_REGISTER’, ‘VW_BSE_ISSUES’…

Имената на функциите имат префикс ‘FN_’ и трибуквен код на модула, към който принадлежи: ‘FN_COM_GET_VALUE’.

Имената на съхранените процедури имат префикс ‘SP_’ и трибуквен код на модула: ‘SP_COM_ADD_ORDER’.


Глава 4. Реализация на система за управление на инвестиционен портфейл

Процес на реализация


Преди същинската реализация на проекта следва да се определи как ще протече процесът на реализация. Съществуват два основни метода на реализация – последователен и итеративен.

При последователния стил имаме разделение на проекта по дейности, които се извършват една след друга: анализ на изискванията, дизайн на системата, имплементиране, тестване и т.н. Ако предположим, че имаме една година за реализация на проекта, то процесът би протекъл по следния начин: 2 месеца за анализ на изискванията, 2 месеца за дизайн на системата, 6 месеца за имплементиране и 2 месеца за тестване и фиксиране на грешките.

За разлика от последователния стил при итеративния имаме разделяне на проекта на базата на функционалност. При нашия примерен едногодишен проект, мога да разделя периода на итерации от по 3 месеца. Вземат се четвърт от изискванията и се преминава през пълния цикъл на софтуерна разработка: анализ, дизайн, имплементация, тестване. Така, в края на трите месеца би трябвало да имаме работеща система, която има само ¼ част от цялата функционалност. След това се пристъпва към следващия период и така, в края на годината ще имаме 4 завършени цикъла и цялата функционалност.

Решението кой стил да се приложи зависи от самия проект, както и от опита на разработчиците. При последователния стил обикновено има формално приключване на дадена фаза, но по време на имплементацията би могла да изникне нужда от преразглеждане на анализа и дизайна. Би било грешка да се допусне, че преди самото писане на код имам цялостен дизайн, и че няма да се налагат никакви промени.

При итеративния процес обикновено има фаза на изследване преди началото на итерациите. Това дава възможност да се разпределят функционалностите по итерации. Въпреки че всяка итерация би следвало да даде готов краен продукт, обикновено не се стига дотам, а има нужда от допълнително време за синхронизация между итерациите и отстраняване на бъговете, породени от това.

Има възможност и за смесване на двата стила, например последователно разработване на анализа и дизайна, последвани от итеративно имплементиране и тестване.

Тук ще използвам точно такъв смесен подход – анализът и дизайнът в последователен стил, а всеки модул ще бъде имплементиран в една отделна итерация. Това ще позволи основните проблеми да се разрешат при направата на първия модул и всички следващи модули да отнемат по-малко време.

Концептуален модел


При изработването на концептуалния модел ще се спра само на два от модулите – базовия модул и модул БФБ. Това е така, защото само тези два модула са свързани директно с бизнес-логиката на портфейла.

Базов Модул


Реализацията на проекта започва с концептуалния модел, който ще помогне за определяне на понятията в областта, както и за връзките между тях. Това от своя страна ще подпомогне планирането и последователността на изпълнение на отделните модули.

При създаване на концептуалния модел се дефинират понятията от реалния свят, върху които ще се построи системата. За всяко понятие се дефинират неговите характеристики, като за по-сложните характеристики се дефинира отделно понятие. По този начин се постига раздробяване на прости концепции, което ще улесни по-нататък процеса на разработка. След като имам понятията, мога да добавя и връзките между тях, като в резултата на това ще се определят всички зависимости.

В този проект основното понятие е ‘портфейл’. Портфейлът има име, начална дата – дата на създаване на портфейла, както и крайна дата – дата, на която се закрива портфейлът, и наличните средства се пренасочват другаде.

Всеки портфейл принадлежи на инвеститор – било то физическо или юридическо лице. Понятието ‘инвеститор’ е сложно понятие и затова е отделено от понятието ‘портфейл’. Тук е времето да уточня, че концептуалният модел не бива да се бърка с модела на класовете в дадено приложение. Клас диаграмите пресъздават конкретна имплементация, отговаряща на дадените нужди, докато концептуалният модел по-скоро дава на идейно ниво връзките между обектите.

Концепцията за инвеститор има следните характеристики: име на инвеститора, адрес, телефони за контакт, електронна поща и уеб адрес. Един инвеститор може да няма изобщо портфейл, както и да има неограничен брой портфейли, като това се означава като връзка “едно-към-много”( 1..*). От своя страна, един портфейл има точно един инвеститор, т.е. връзка “едно-към-едно”(1..1)



Фиг. 1

Дотук имам портфейл и инвеститор. За всеки един портфейл обаче инвеститорът дефинира инвестиционна рамка, задаваща критериите, по които този портфейл ще бъде управляван и дефинираща желаните цели. Връзката между портфейл и рамка е “едно-към-едно”, като тук се взима под внимание фактът, че в даден момент има само една инвестиционна рамка. Историята на промените в параметрите на рамката не прави връзката от тип “едно-към-много”





Фиг. 2

Следващите две понятия, които ще добавя към модела, са ‘актив’ и ‘сделка’. Активът в нашата система представлява всяко нещо, което е цел на инвестиция. Тъй като искам да обхвана най-много възможни активи, свеждам характеристиките до три: код, име и валута. Кодът на актива ще се използва в цялата система и всички връзки ще бъдат базирани на него. Името е описание на актива или емитента.

Сделката от своя страна пресъздава сделка за покупка или продажба на актива в реалния свят. В моето приложение няма да се допуска дефинирането на актив, без да има съответна сделка за него. Сделката има тип – купува или продава, дата на която е извършена, брой единици и цена на придобиване, както и комисионна при сключване, ако има такава.

Всяка една сделка има за цел само един актив и това определя връзка “едно-към-едно”:





Фиг. 3

След като дефинирах активите и сделките към тях, може да видим и какви са връзките между тях и портфейла.

Всеки портфейл има връзка към сделките “едно-към-много”, като в един портфейл може да няма нито една сделка или да има неограничен брой сделки. Портфейлът има също такава връзка “едно-към-много” и към активите – в даден момент портфейлът може да няма нито един актив или да има неограничен брой активи. Не е задължително да имаме активи при наличие на сделки, но обратното не е вярно – не можем да имаме активи без да имаме сключени сделки за тях:



Фиг. 4

На следващата фигура са показани всички дефинирани до момента концепции и техните връзки. Тези концепции се покриват функционално от базовия модул:





Фиг. 5

Модул БФБ


Модулът БФБ добавя към този модел допълнителни концепции, специфични за търговията на Българската фондова борса – ‘емитент’, ‘поръчка’, ‘котировка’

Понятието ‘емитент’ представлява финансовата институция или компания, която е в списъка на фондовата борса. За БФБ емитентът има следните характеристики: име, борсов код, брой ценни книжа, номинална стойност и валутата. Всъщност, понятието ‘емитент’ разширява понятието ‘актив’, добавяйки специфичните за областта на финансовите пазари атрибути. От тук може да се дефинира и връзката актив-емитент, която е “едно-към-едно” (1..0-1). Тази връзка следва да се разбира така: един актив може да няма съответстващ емитент т.е. активът да не е ценна книга, търгувана на фондовата борса, или да има точно един съответстващ емитент. От своя страна, ценната книга, издадена от емитента, може да не присъства като актив в нито един портфейл т.е. да няма съответстващ актив или да има неограничен брой активи в дефинираните портфейли:





Фиг. 6

Поръчката за покупка или продажба на ценни книжа от даден емитент е второто понятие, специфично за финансовия пазар. Тя е свързана с един единствен емитент и има следните атрибути: тип – купува или продава; брой ценни книжа, цел на поръчката, единичната цена, време на живот – дневна или до отмяна и статус. Поръчката има връзка с понятието ‘сделка’, но само при условие, че конкретната поръчка е удовлетворена и има сключена сделка. Това означава, че връзката между поръчка и сделка е нула-към-едно. От своя страна, сделката може да е в резултат на поръчка от БФБ, но може и да не е, което дефинира връзка сделка-поръчка отново нула-към-едно:





Фиг. 7

Последното, трето понятие е ‘котировка’. Котировката представлява всички пуснати поръчки за покупка и продажба на даден актив. Котировката има следните атрибути: тип – купува или продава, дата на подаване на поръчката, брой и единична цена както и времето за живот – за деня или до отмяна. Разликата между поръчка и котировка се състои в това, че под поръчка се разбира поръчка, пусната от нас, като участници на борсата, а котировка са всички поръчки за същия актив, които са подали всички други участници. От тук следва, че има връзка между поръчка и котировка, тъй като се сключва сделка при съвпадение на цената на котировка и поръчката:





Фиг. 8

Връзката следва да се разбира по следния начин: една поръчка представлява една котировка и зад една котировка стои точна поръчка. А ето и връзката ценна книга /емитент/ и котировка:





Фиг. 9

За дадена ценна книга може да няма нито една поръчка/котировка, но всяка една котировка се базира на дадена ценна книга.


Дизайн и имплементация


Основите на софтуерния дизайн на системата донякъде са дефинирани от избраните средства за разработка а именно Smart Client CAB и Enterprise Library.

Общи положения

Smart Client – Composite UI Application Block (CAB)

Този application block подпомага създаването на комплексни системи, използвайки възможностите на Windows, които да си взаимодействат с различни back-end системи. CAB позволява да се обединят различни функционалности в удобен за използване и богат на възможности интерфейс, базиран на модули. Решени са основните проблеми при комбинирането на модулите, предоставяне на функционалност от един модул в друг, разделянето на бизнес логиката от потребителския интерфейс и т.н.



Фиг. 10

На Фиг. 101 е показана една такава примерна система, която използва функционалност, предоставена от различни под-системи.

CAB-ът се състои от следните елементи:


  • SmartParts – предоставя визуалните елементи в даденото приложение. Тези елементи могат да бъдат напълно независими от приложението, което ги използва и това позволява да бъдат и независимо разработвани;

  • Workspace (работно пространство) – е контейнер и мениджър на SmartPart. Може да контролира кога даден SmartPart е показан или скрит, както и къде и как е разположен визуално. Следните работни пространства са предоставени от CAB:

    • WindowWorkspace – показва контролите в отделни прозорци;

    • MdiWorkspace – позволява показването на контроли и SmartParts като много-документни дъщерни (Multi-document child) форми. Базира се на WindowWorkspace;

    • TabWorkspace – позволява показването на контроли и SmartPart в отделни страници – tab pages;

    • DecWorkspace – показва контролите по припокриващ се начин. Всеки активиран контрол се показва върху останалите контроли, като ги припокрива визуално така, че те не се виждат;

    • ZoneWorkspace – позволява дефинирането на собствена наредба (layout), по който да са подредени контролите SmartParts. Тъй като е контейнер, позволява съхраняването на всякакви контроли, като за всяка една контрола добавя допълнителен атрибут, указващ име на Workspace-а, който в последствие може да се използва за интегриране на SmartPart;

  • WorkItem – е run-time контейнер, който съдържа SmartParts необходими за извършването на определена функционалност.

  • Infrastructure Services – базови услуги предоставени от CAB, които могат да бъдат използвани и в приложението. Такива са например Module Loader, Authentication Service и State Persistence Service;

  • Module – модулите представляват набор от Workitems, SmartParts, контролери, бизнес логика и др, необходими за инициализирането и използването на WorkItems;

  • Module Services (услуги) – са обекти, които се маркират със специален атрибут и след това могат да бъдат използвани от всеки компонент във даден WorkItem;

  • State – предоставя възможност за съхраняване на специфични данни между различните стартирания на системата;

  • Controllers – имат същата роля, каквато имат в Model-View_Controller: да имплементират бизнес-логиката зад конкретно View.

  • Service Agents – компоненти за взаимодействие с всякакви back-end системи.

Идеята, върху която е изграден Smart Client блокът, се състои в това да се създадат множество от SmartParts, които в последствие да се визуализират в някое от дефинираните работни пространства на приложението, като самият SmartPart не се интересува къде точно е разположено по екрана самото работно пространство. Това позволява да се създаде разположение на работните пространства, което лесно да може да се променя и разширява. Всеки добавен SmartPart може да дефинира собствени Workspaces, които са достъпни в цялото приложение.

Друга концепция, заложена в този блок са услугите (Services). Те служат като доставчици на данни, с които оперира даден SmartPart. Те комуникират с всякакви back-end системи и предоставят интерфейс за комуникация между тях и приложението. За да се имплементира една такава услуга се създава клас, който се маркира с атрибута [Service]. Той указва на CAB фреймуърка, че това е услуга и при инициализиране на модулите и/или инстанциране на SmartPart, се проверява за свойство, маркирано с атрибута [ServiceDependency]. За такива свойства се проверява чрез рефлексия типът, след което от списъка с регистрираните услуги се проверява за услуга със същия тип, като на свойството. Например, поддръжката на Market Minder-а се извършва чрез услугата PortfolioMarketMinderService, която е дефинирана ето така:

[Service]

public class PortfolioMarketMinderService

{

public DataSet Load( Guid portfolioId )



{

}


public void Save( DataSet dataSet )

{



}

}

А в съответната имплементация на SmartPart-а за Market Minder-a, presenter-a от MVP шаблона имаме следното:



public class PortfolioMarketMinderViewPresenter: IDisposable

{

[ServiceDependency]



public PortfolioMarketMinderService Service

{

set



{

this.service = value;

}

}


}

При инстанциране на PortfolioMarketMinderViewPresenter неговото свойство ще бъде автоматично инициализирано с инстанция на PortfolioMarketMinderservice.

На фиг. 11 са показани подсистемите, от които е изграден CAB-a1, а фиг. 12 отразява най-общата структурата на един workitem2.



Фиг. 11



Фиг. 12

Data Access Application Block

Това е част от Enterprise Library, която отговаря за връзката с база данни, като предоставя абстрактно различните видове бази данни. По този начин опростява значително задачата едно приложение да може да използва различни бази от данни. За тази цел се използват абстрактни класове като DbCommand, и DbConnection, които са част от ADO.NET 2.0. Това премахва необходимостта клиентският код да бъде променян при смяна на базата.



Фиг. 13

На показаната по-горе схема1 се виждат основните класове, които съставляват този програмен блок. Database класът предоставя достъп до данните в базата и се инстанцира чрез Database Factory. Този блок използва Configuration Application Block, за да прочете от външен XML файл типа на базата, към която искаме да се свържем, както и останалата информация, необходима за да се създаде връзка. От своя страна, клиентското приложение използва този клас, за да комуникира с базата и не се интересува от конкретния й тип.

Ползата от прилагането на Data Access блока се вижда от следния пример: ако дадено приложение иска да изпълни обикновена SQL заявка, използвайки предоставените във Visual Studio контроли, тогава трябва да избере каква точно ще е връзката – директна, използвайки SQLConnection, за да се възползва от скоростта и специфичните за MS SQL функции, или ODBCConnection, за да използва ODBC. И тук идва невъзможността това да стане без промяна в самите заявки, тъй като поддържането на параметри става по различен начин.

При използване на SQLConnection параметрите са наименовани, докато при ODBCConnection те са позиционни В първия случай заявката би изглеждала така:

SELECT * FROM TABLE_1 WHERE Name = @NAME AND REC_DATE = @DATE

като при подаване на параметрите не е нужно да се спазва редът им в заявката. За втория:

SELECT * FROM TABLE_1 WHERE Name = ? AND REC_DATE = ?

и тук вече редът е от значение. Още повече, че при заявка, която използва един и същи параметър няколко пъти, се налага той да бъде подаван всеки път.


Exception Handling Application Block

Всяка програма се нуждае от цялостна стратегия за обработката на възникналите грешки по време на изпълнение. С помощта на този блок разработчикът може да укаже всяка една грешка по какъв начин да бъде обработена, дали да се прихване и скрие от потребителя, дали да се изведе друга грешка с конкретно съобщение или пък да се запише във файл.

Как да се обработи съответната грешка се указва във външен XML файл, позволявайки по всяко време да се смени стратегията за обработка.


Имплементация

Модел на базата данни


Преди да продължа с описанието на модела на данните, ще разгледам правилата, по които ще се ръководя при работа с базата.

При създаване на нов обект в базата, като таблица или съхранена процедура, ще ми е нужно да знам типовете данни, които ще са ми необходими – реални числа, стрингове и т.н. Тук ще се ръководя от правилото, че за всеки логически тип данни ще дефинирам собствен тип. Например, за да съхраня стойност в смисъла на пари, ще дефинирам тип D_MONEY, който ще използва системния тип money. За полета, които представляват някакъв код, дефинирам D_CODE – nvarchar(25). Това въвежда едно ниво на абстракция и ни отделя от системно дефинираните типове. По този начин мигрирането от един сървър към друг става много по-лесно и единственото което е нужно, е да се създадат потребителските типове, като се използват подходящите за новия сървър системни типове. Например ако новият сървър няма тип money, тогава D_MONEY създавам базирано на numeric(14,4).

Друго правило е всяка таблица да има първичен ключ, който да се нарича ID е от тип D_ID (uniqueidentifier). За логическите първични ключове може да се създаде уникален индекс при необходимост.

Също така се стремя да създам база от данни, която да не позволява нарушаване на интегритета на данните. За целта използвам вторични ключове, индекси, тригери и т.н.

За всяка таблица създавам и съответното view, като при изпълнение на SELECT заявка използвам view-to. Не бива да имам извеждане на данни директно от таблица.

След като представих правилата, по които ще се водя при създаването на обекти в базата, мога да продължа с модела на данните за базовия модул, който е показан на диаграмата:





Фиг. 14

В таблицата COM_REGISTER се съхраняват всякакъв вид номенклатури, които ще бъдат групирани посредством GROUP_ID. Такива номенклатури ще са например типовете поръчки, статус на поръчките, и др. За тези данни, които са системни т.е. се използват и реферират от приложението, флагът IS_SYSTEM ще бъде установен на 1.

COM_INVESTOR таблицата съдържа данни за инвеститора. Един инвеститор може да има повече от един портфейл, а всеки портфейл има точно един инвеститор.

В таблицата COM_PORTFOLIO ще съхранявам данните за портфейлите. За момента имам само име на портфейла и връзка със инвеститора.

В COM_PORTFOLIO_ACCOUNT имам данни за паричните транзакции като например внасяния на средства по сметка на портфейла и т.н.

COM_PORTFOLIO_ORDER съхранява данните за сключените сделки, а COM_MARKET_MINDER за текущите котировки на даден актив.

Както е видно от модела на данните, нямам таблица, която да съхранява активите. Това е така, защото искам да постигна максимална независимост от актива и да не налагам конкретно описание. Затова навсякъде използвам ASSET_CODE като идентификатор на актива.

Всеки допълнителен модул, който добавя поддръжка на даден актив, може да има отделна таблица, която да дава детайлно описание на актива. Например за модула това ще е таблица, която съдържа данните за емитентите, където също има поле АSSET_CODE.

При добавяне на нов ред или промяна прибягвам до съхранени процедури с изключение на случаите, в които нямам допълнителна логика при тези операции както е например за таблицата COM_REGISTER.

Например за да добавя или променя данни в таблицата COM_MARKET_MINDER, трябва да използвам процедурата: SP_COM_UPDATE_MARKET_MINDER, която има следния код:

CREATE PROCEDURE [dbo].[SP_COM_UPDATE_MARKET_MINDER] (

@ASSET_CODE D_CODE,

@BID_PRICE D_MONEY,

@ASK_PRICE D_MONEY,

@LAST_TRADE_PRICE D_MONEY ) AS
DECLARE

@ID D_ID
SELECT @ID = ID

FROM VW_COM_MARKET_MINDER

WHERE ASSET_CODE = @ASSET_CODE


IF( @ID IS NULL )

BEGIN


SET @ID = NEWID();

INSERT INTO COM_MARKET_MINDER

([ID], [ASSET_CODE], [BID_PRICE], [ASK_PRICE], [LAST_TRADE_PRICE])

VALUES


(@ID, @ASSET_CODE, @BID_PRICE, @ASK_PRICE, @LAST_TRADE_PRICE )

END


ELSE

BEGIN


UPDATE COM_MARKET_MINDER

SET


BID_PRICE = @BID_PRICE,

ASK_PRICE = @ASK_PRICE,

LAST_TRADE_PRICE = @LAST_TRADE_PRICE

WHERE ASSET_CODE = @ASSET_CODE

END

Както се вижда от кода, процедурата проверява дали има съществуващ запис с указания код на актива. Ако не е намерен такъв запис, се създава нов и се попълва с данни от подадените параметри, а ако съществува – данните се актуализират с подадените.



Този подход позволява опростяване на програмния код, тъй като за добавяне и актуализиране, се използва един и същи код.

SPM Solution


Имплементацията на проекта започва с генерирането на нов solution с помощта на Smart Client Guidance Package. За целта се посочва директория и име на Root namespace-а, указват се и директориите на Enterprise Library-то и CAB. Guidance package-a ще създаде директории и ‘Solution Items’ и ‘Source’, като в директорията ‘Source’ ще създаде два проекта – ‘Common’ и ‘Shell’. Тези два проекта представляват скелета на CAB.

‘Shell’ проектът е стартиращият проект и генерира изпълнимия файл, който стартира системата. Той съдържа и главната форма както и основните workspace-и.

‘Common’ проектът съдържа всички общи за модулите части като константи, дефиниции на event handler-и и т.н. Всеки модул, генериран със Guidance Package автоматично добавя референция към този проект.

След като имам създадена основната структура на проекта, я добавям към системата за сорс контрол – в случая – Visual Source Safe – това ще позволи съхранението на различни версии на програмата. Трябва да се има предвид, че в системата за сорс контрол трябва винаги да се слагат версии, които са компилируеми.


Проект Common


Проектът Common съдържа класове, които са общи за цялата програма. Към този проект автоматично се добавя референция при създаване на нов модул. Тук ще се добавят класове, които предоставят някаква базова функционалност, достъпна за всички модули или константи. Може да се гледа на него като на мини-фреймуърк за цялата система.

В Common проекта на SPM системата ще добавя и поддиректории за бизнес обектите и за услугите, които ще са общи за модулите. За целта създавам два класа – BusinessObjec и BaseService, които ще бъдат наследени от всички бизнес обекти и услуги:

public class BusinessObject

{

}


[Service]

public class BaseService

{

}

Атрибутът [Service] указва, че класът е услуга и при наличие на същия атрибут върху свойство на клас от MVP шаблона, CAB ще го инициализира вместо нас, като ще създаде инстанция на самия service, ако няма или ще използва вече създадената инстанция.



Тъй като нашата система ползва база данни, ще създам още един клас за бизнес обекти, които оперират върху данни от базата. Този клас има следната дефиниция:

public class DataBusinesObject : BusinessObject

{

public DataSet Data



{

get{ … }


}
public BindingSource BindingSource

{

get{ … }



}
public string BindingSourceDataMember

{

get{ … }



}
public DataBusinesObject( DataSet dataSet, string bindingDataMember )

{



}

}

Този клас получава в конструктора си DataSet, който съдържа данните с които оперира бизнес обектът. Вторият параметър служи за задаване име на обекта, който да бъде свързан към BindingSource-а. Този BindingSource служи за свързване на визуалните контроли с бизнес обекта. Той е особено полезен при бизнес обекти, които по същество са списъци, тъй като пази информация за текущо избрания елемент от списъка.



Този проект съдържа и дефиниция на клас, който отговаря за връзката с базата данни на приложението, и се нарича DataService. А ето и неговата дефиниция:

public class DataService

{

private static DataService instance;


public Database Database …
public static DataService Instance

{

get



{

if( instance == null )

instance = new DataService();
return instance;

}

}


private DataService()

{

this.database = DatabaseFactory.CreateDatabase( "SPM" );



}

}

Интересното тук е, че този клас имплементира шаблона Singleton. Този шаблон се използва в случите когато искаме да имаме само една инстанция от даден клас. Тук това е постигнато чрез статичното свойство Instance и скрит конструктор.



В самия конструктор се създава и инстанция от наследник на класа Database, който е от Data Access блока. Кой наследник да се създаде зависи от използваната база от данни, а това се записва във конфигурационния файл на приложението:


name="System.Data.SqlClient" />






providerName="System.Data.SqlClient" />



Тук се вижда как се регистрира поддръжката на дадена база, а по-надолу и конкретното използване, както и задаването на параметрите, необходими за установяване на връзка с конкретната база.


Проект Shell


Това е входната точка на системата. Компилацията на този проект дава и изпълнимия файл. Тук се създава основната форма на приложението и се регистрират основните елементи на всяко Windows базирано приложение – MainMenu, ToolBar, StatusBar. В основната форма се дефинират и основните области от екрана, където ще се интегрират графичните елементи от различните модули.

В моя случай основната област е разделена на две области, наречени MainLeftWorkspace и MainContentWorkspace.

MainLeftWorkspace е от тип DeckWorkspace и е предназначена да съдържа контроли, които са общи за програмата, независимо от модула, с който се работи.

MainContentWorkspace е от тип TabWorkspace. В тази област се показват различни view-та от заредените модули, като за всяко view се създава отделна страница – tabpage.

Важен е и файлът ‘ProfileCatalog.xml’, който съдържа конфигурацията на системата . В него се описват модулите, които ще бъдат заредени, както и други опции, които могат да бъдат прочетени от приложението.

Проект CommonModule


При създаването на нов модул, Smart Client Guidance Pack-а генерира автоматично нов проект по зададено от нас име и добавя следните класове:

  • Module – наследникът на класа ModuleInit от CAB фреймуърка. Този клас е входната точка на модула и той ще бъде инстанциран при стартиране на приложението;

  • ModuleController – наследникът на класа WorkItemController. Съдържа методи за инициализиране на модула като: services, елементи на потребителския интерфейс и т.н.;

  • Класове в поддиректория ‘Constants’ – в тази поддиректория се съхраняват класове с различни константи – за събитията, за командите, за иментата на Workstpaces и др.

Тези класове и тази базова структура се следва от всички имплементирани в системата модули.
Бизнес обекти

Към вече създадения модул създавам поддиректория с името BusinesEntries, която ще съдържа дефинициите на моите бизнес обекти . В тази директория добавям следните класове:

PortfolioList

Отговаря за работата с наличните в системата портфейли- добавяне, модифициране и изтриване на портфейли.

Portfolio

Портфейл

PortdolioDetails

Отговаря за детайлите на даден портфейл – изчислява печалбата/загубата, показва наличностите по активи и т.н.

PortfolioOrderList

Списък със сключените сделки за даден портфейл.

MarketMinder

Дава функционалността на Market Minder-а

Всички те са наследници на класа, дефиниран в Common модула DataBusinessObject.

Освен тези обекти в директорията има още два специфични класа-PortfolioOverviewCalculator и PortfolioOverviewCalculatorList. Първият клас е абстрактен и дефинира интерфейс за калкулатори, които ще се визуализират от PortfolioOverview SmartPart-a. Тези калкулатори имат за цел да изчисляват някакъв показател:

public abstract class PortfolioOverviewCalculator

{

public PortfolioOverviewCalculatorList CalculatorList



{

get{ … }


}
public abstract string Name

{

get;



}
public abstract void Calculate();
public abstract string ResultAsString

{

get;



}
public PortfolioOverviewCalculator( PortfolioOverviewCalculatorList calculatorList )

{



}

}

Наследниците на този клас трябва да имплементират абстрактните свойства и методи. Свойството Name дава името на показателя, който се изчислява от калкулатора, методът Calculate() извършва изчислението, a чрез свойството ResultAsString се взима стойността.


Услуги (Services)

В директорията Services се намират услугите, които се използват от SmartParts, имплементирани в модула. Тези услуги имат за цел да предоставят методи за зареждане на данните от базата и за записване на промените. За целта имат метод Load, който връща обект от тип DataSet, съдържащ съответните таблици, и метод Save, който записва в базата направените промени в DataSet, върнат от Load метода.

Тези услуги използват инстанцията на DataService, който е имплементиран в Common модула, и предоставя връзка към базата. Една имплементация на метод за зареждане на данни е дадена по-долу:

public DataSet Load( Guid portfolioId )

{

DbCommand command;


if( portfolioId == Guid.Empty )

command = DataService.Instance.Database.GetSqlStringCommand(

string.Format( "SELECT * FROM {0}", TableNames.PortfolioOrderView ) );

else


{

command = DataService.Instance.Database.GetSqlStringCommand(

string.Format( "SELECT * FROM {0} WHERE PORTFOLIO_ID = @PORTFOLIO_ID", TableNames.PortfolioOrderView ) );

DataService.Instance.Database.AddInParameter( command, "@PORTFOLIO_ID", DbType.Guid, portfolioId );

}
DataSet dataSet = DataService.Instance.Database.ExecuteDataSet( command );

dataSet.Tables[ 0 ].TableName = TableNames.PortfolioOrderView;

return dataSet;

}

Първо се създава обект от тип DbCommand, който пресъздава заявка към базата. Проверява се стойността на подадения параметър – portfolioID и ако е валиден идентификатор, данните се ограничават по този идентификатор - иначе се зареждат без никакво условие за ограничение. Резултатът от изпълнението на командата е DataSet, който съдържа една таблица, на която указвам име, след което връщам DataSet-а като резултат.



По подобен начин става и записването на промените, като се създават три команди –за добавените, за изтритите и за модифицираните редове от таблицата в DataSet-a.
SmartParts

В базовия модул се съдържа имплементацията на основната функционалност на системата, имплементирана в няколко SmartParts. Всеки SmartPart имплементира Model-View-Presenter шаблона.

Чрез MVP шаблона се постига разделение на отговорностите по визуалното представяне и обработката на събитията в отделни класове. Класът, който е отговорен за визуалното представяне, се нарича View, а класът, който обработва събитията, се нарича Presenter. View класът предава събитията на Presenter класа, който има логиката за обработка на конкретните събития, и в резултат може да промени състоянието на View класа. От своя страна, Presenter класът използва ‘модела’ (често това е бизнес обектът), за да отговори на дадено събитие:





Фиг. 15

За удобство, Guidance Package-ът създава за нас View клас, наследник на UserControl, Presenter клас, както и интерфейс, който се използва за комуникация помежду им:

[SmartPart]

public partial class OrdersView : UserControl, IOrdersView

{



}



public class OrdersViewPresenter : IDisposable

{


}

public interface IOrdersView

{


}

PortfolioSelectorView


Това view служи за визуализация на наличните в базата портфейли. Чрез него потребителят избира с кой портфейл да работи. Тук също така се намират и имплементациите на командите за добавяне на нов портфейл, редактиране и изтриване на съществуващ.

Тук под команда се разбира имплементация на шаблона command. В нашия случай това става като се имплементира event-handler, на който се маркира със специалния атрибут [CommandHandler( )] и който в конструктора си приема параметър името на командата. Когато на presenter класа на това view се инициализира с инстанция на WorkItem, се извиква метода ExtendMenu(), който добавя в менюто на workitem-a командите за добавяне, редактиране и изтриване на портфейл:

public class PortfolioSelectorViewPresenter: IDisposable

{



private void ExtendMenu()

{

ToolStripMenuItem item = new ToolStripMenuItem( Properties.Resources.Portfolios );



_workItem.UIExtensionSites[ UIExtensionSiteNames.MainMenu ].Add( item);

_workItem.UIExtensionSites.RegisterSite( UIExtensionSiteNames.MainMenu_Portfolios, item.DropDownItems );


item = new ToolStripMenuItem( Properties.Resources.Add );

_workItem.Commands[ CommandNames.AddPortfolio ].AddInvoker( item, "Click" );

_workItem.UIExtensionSites[ UIExtensionSiteNames.MainMenu_Portfolios ].Add( item );

}



}

Първо се създава менюто, което ще съдържа подменюта, изпълняващи командите. След това се създава menu item за всяка една команда, извиква се методът AddInvoker на командата, като подаваме новосъздадения menu item и събитието, което да предизвика изпълнение на командата – в случая Click.



Presenter класът публикува и събитие PortfolioChanged, което настъпва винаги, когато потребителят смени активния портфейл. Това позволява на другите модули да се регистрират за това събитие и когато то настъпи, да актуализират своите данни.

Публикуването на събитие става по следния начин:

[EventPublication( EventTopicNames.PortfolioChanged, PublicationScope.Global )]

public event EventHandler> PortfolioChanged;

Отново това публикуване става с помощта на атрибут и това е атрибутът [EventPublication]. Като параметри на неговия конструктор се подават името, под което искаме да бъде публикувано това събитие в системата, както и обхвата – за цялата програма, за наследниците или за WorkItem-а.

При визуализиране на портфейлите се използва контролата DataGridView, която се свързва към BindingManager свойството на обекта PortfolioList, който играе ролята на бизнес обект. Текущо активираният портфейл е маркиран в плътно синьо.





Фиг. 16
PortfolioOverView

Това view съдържа обобщената информация за портфейла. Тя се базира на различните регистрирани в системата калкулатори, които изчисляват различни показатели на база портфейл. Самото view е предназначено само за разглеждане (readonly). Тъй като това view зависи пряко от текущия портфейл, Market Minder-a и сделките, то се актуализира при промяна на данните във всяко едно от тези view-та посредством публикуваните от тях събития за промяна. Самото view изглежда така:



Фиг. 17
PortfolioMarketMinderView

PortfolioMarketMinderView визуализира данните за текущите цени на активите в портфейла. В него в табличен вид са показани цените ‘купува’ и ‘продава’, както и цената, при която е сключена последната сделка. Под последна сделка имам предвид не за портфейла, а за пазара, на който се търгува конкретният актив.



Фиг. 18

Тук потребителят може сам да въведе информацията за цените на активите,които ще бъдат съхранени в базата. Това позволява, дори и при отсъствието на модул за актуализиране на цените в реално време, потребителят да въвежда сам актуалните цени, които се използват при изчисление на параметрите в останалите view-та – това с обобщената информация и детайлите. Естествено, за да стане това, има публикувано събитие, за което са се регистрирали и двата модула.


PortfolioLeftPanelView

Предназначението на това view е да бъде контейнер на PortfolioSelectorView, PortfolioOverView и PortfolioMarketMinderView, като го подрежда в този ред. От своя страна, то е вградено в MainLeftWorkspace на главната форма.
PortfolioDetailsView

Тук се показват детайлизирано данните за портфейла. Това са данни за всички активи, които са или са били част от портфейла. Тук потребителят може да види за всеки актив какво количество е било закупено, какво има в наличност, средно претеглена цена на придобиване, реализираната печалба и т.н.

Тези данни са базирани на данните за цена на последната сключена сделка, регистрирана в Market Minder-а, и от сключените сделки се актуализира при всяка промяна в цената или в списъка със сделките.

Потребителят има още възможност за филтриране по актив, както и да сортира данните. Това view е само за четене и изглежда по следния начин:



Фиг. 19

За улеснение първата колонка – кода на актива, е фиксирана и при хоризонтално скролиране тя остава винаги видима. Потребителят може да сортира данните по всяка една колона, като кликне с левия бутон на мишката върху името на колонката, по която иска да се извърши сортировка.


PortfolioOrdersView

Сключените сделки се показват в това view. Потребителят има възможност за сортировка, както и за добавяне, редактиране и изтриване на сделки.



Фиг. 20
PortfolioContextView

Също както и PortfolioLeftPanelView, това view е композитно и съдържа PortfolioDetailsView и PortfolioOrdersView. Визуализира се в MainContentWorkspace на главната форма.

При стартиране на програмата се отваря главният прозорец, който изглежда така:





Фиг. 21

News Module


Този модул служи за доставяне на новини, които се получават от web-service. За целта потребителят трябва да е абониран за тази услуга, която обикновено се хоства на сървъра на инвестиционния посредник, на когото той е клиент.

Този модул всъщност се състои от няколко части – сървърна част, web service и клиентски модул.

Сървърната част служи за въвеждане на новините в базата на посредника. Тази част е подобна на клиентския модул с разликата, че позволява въвеждане на новина. Тъй като разликата от клиентския модул е само наличието на форма за въвеждане на новина, която по същество не се различава от останалите форми за редактиране на данни в останалите модули, тази част няма да бъде разглеждана. Ще отделя повече внимание на самия процес, не на получаване на новините.

Проект NewsModulwWebService

Web service частта предоставя услугата за получаване на новини, като се публикува на web сървър. След като се публикува, услугата е достъпна за клиентския модул, който на определен интервал от време запитва за наличието на новини, и ако има новини с дата по-голяма от датата на последната новина в базата на клиента, се прави заявка за новите новини.

За да създам web услуга, в средата на Visual Studio използвам шаблон за проект от типа ASP.NET Web Service, който изгражда инфраструктурата на самата услуга. В резултат имам генериран файл Service.cs, който съдържа кода, файл Service.asmxm който указва типа и файла с имплементацията на услугата. Чрез този файл компилаторът знае къде се намира класът с имплементация на услугата и го компилира при първата заявка към услугата. Във версия 2.0 на ASP.NET не е нужно да се слагат компилирани версии на web приложенията. Достатъчно е да се сложат файловете с кода и сървърът сам ще ги компилира.

В случая Service.asmx файла съдържа:

<%@ WebService Language="C#" CodeBehind="~Service.cs" Class="NewsModuleService" %>

За да имплементираме web service-а, средата създава за нас наследник на класа WebService, който е базов клас за XML web service и предоставя достъп до основни обекти от ASP.NET като сесия и т.н. Към този клас добавям два метода - HasNews и Search.

Методът HasNews приема за параметър дата и час и проверява в базата за наличието на новини с дата и час по-големи от зададената. Ако има такива, връща true иначе false:

[WebMethod]

public bool HasNews( DateTime dateTime )

{

return new NewsService().HasNews( dateTime );



}

Вижда се, че се извиква метод на NewService, който използвах и в модула NewsModule. А ето и имплементацията на NewsService.HasNews() метода:

public bool HasNews( DateTime dateTime )

{

DbCommand command = DataService.Instance.Database.GetSqlStringCommand(



string.Format( "SELECT COUNT(*) FROM {0} WHERE REC_DATE > @REC_DATE", TableNames.NewsView ) );

DataService.Instance.Database.AddInParameter( command, "@REC_DATE",

DbType.DateTime, dateTime );
int result = (int)DataService.Instance.Database.ExecuteScalar( command );

return result > 0;

}

Тук с помощта на Data Access Application Block създавам команда, инициализирам я със заявка, която да ни върне броя на новините с дата по-голяма от подадената като параметър. Извиквам метода ExecuteScalar, който взима резултата от първия ред – първата колона и ми го връща като обект, който в моя случай е от тип integer. Ако резултатът е по-голям от 0, тогава имам налични новини и връщам true.



Другият метод на услугата е методът Search и има следната имплементация:

[WebMethod]

public DataSet Search( DateTime fromDate, DateTime toDate, string code,

string category, string source, string subject, string fullText)

{

object fromDate_param = DBNull.Value;



object toDate_param = DBNull.Value;

object code_param = DBNull.Value;

object category_param = DBNull.Value;

object source_param = DBNull.Value;

object subject_param = DBNull.Value;

object fullText_param = DBNull.Value;


if(fromDate != DateTime.MinValue)

fromDate_param = fromDate;


if(toDate != DateTime.MinValue)

toDate_param = toDate;


if(code != string.Empty)

code_param = code;


if(category != string.Empty)

category_param = code;


if(source != string.Empty)

source_param = code;


if(subject != string.Empty)

subject_param = code;


if(fullText != string.Empty)

fullText_param = code;


NewsService service = new NewsService();

return service.Search( fromDate_param, toDate_param, code_param,

category_param, source_param, subject_param, fullText_param );

}

Тъй като при подаване на параметрите се използва сериализация, съм избрал те да са типизирани. Имплементацията на метода Search от NewsService приема същите параметри като семантика, но типът им е object. Това е така защото мога да задам стойност на параметъра DBNULL.Value което съответства на NULL в базата данни, а DB>NULL.VALUE не е съвместим със string или DateTime. От друга страна, при сериализацията на параметрите има проблем при нетипизираните параметри – параметри от тип object. Ето и самото търсене:



public DataSet Search( object fromDate, object toDate, object code, object category, object source, object subject, object fullText )

{

DbCommand command = DataService.Instance.Database.GetSqlStringCommand(



string.Format(

"SELECT * " +

"FROM {0} " +

"WHERE (@FROM_DATE IS NULL OR @FROM_DATE <= REC_DATE) AND "+

" (@TO_DATE IS NULL OR REC_DATE <= @TO_DATE) AND "+

" (CODE LIKE ISNULL(@CODE, N'%')) AND " +

" (CATEGORY LIKE ISNULL(@CATEGORY, N'%')) AND " +

" (SOURCE LIKE ISNULL(@SOURCE, N'%')) AND " +

" (SUBJECT LIKE ISNULL(@SUBJECT, N'%')) AND " +

" (FULL_TEXT LIKE ISNULL(@FULL_TEXT, N'%'))", TableNames.NewsView ));


DataService.Instance.Database.AddInParameter( command, "@FROM_DATE",

DbType.DateTime, fromDate );

DataService.Instance.Database.AddInParameter( command, "@TO_DATE",

DbType.DateTime, toDate );

DataService.Instance.Database.AddInParameter( command, "@CODE",

DbType.String, code );

DataService.Instance.Database.AddInParameter( command, "@CATEGORY",

DbType.String, category );

DataService.Instance.Database.AddInParameter( command, "@SOURCE",

DbType.String, source );

DataService.Instance.Database.AddInParameter( command, "@SUBJECT",

DbType.String, subject );

DataService.Instance.Database.AddInParameter( command, "@FULL_TEXT",

DbType.String, fullText );


DataSet dataSet = DataService.Instance.Database.ExecuteDataSet( command );

dataSet.Tables[ 0 ].TableName = TableNames.NewsView;

return dataSet;

}

Тук най-интересното е самата заявка към базата. За да се игнорират параметрите, които имат стойност NULL, правя следното:



@PARAMETER_1 IS NULL OR [] operator @PARAMETER_1

Тук идеята е да се направи проверка за NULL и с ‘OR’ да се направи същинската филтрация по параметъра. Така, ако стойността е NULL, тогава изразът връща true и параметърът се игнорира. Ако пък има стойност, различна от NULL, се прилага втората част от израза. Тук ‘operator’ е всякякъв валиден оператор за сравнение.

Както е видно от имплементацията на методите в web service-а, методите, които искаме да бъдат публикувани, се маркират с атрибута [WebMetod]. Той прави видим за XML метод видим за отдалечените web клиенти на услугата.

Проект NewsModule

Този проект представлява клиентската част, който се интегрира във SPM системата и която прави запитвания към услугата за новини.

За целта е имплементиран service – NewsUpdateService, който на определен интервал от време проверява за наличието на новини, и ако има такива, актуализира клиентската база. А ето и дефиницията на класа:

public class NewsUpdateService

{

public NewsUpdateService(){…}



public void Start(){…}

public void Stop(){…}

public bool HasNews(){…}

public bool Update()

{

if( this.HasNews() == false )



return false;
NewsService newsService = new NewsService();

DataSet dataSet = newsModuleserver.Search(

newsService.GetLastNewsDate(),

DateTime.MinValue, string.Empty,

string.Empty, string.Empty, string.Empty, string.Empty );
newsService.ForceSave( dataSet );
return true;

}

}



Класът съдържа базова функционалност, необходима за управление на синхронизацията – методи за стартиране и спиране на проверката за новини и метода Update за актуализиране.

За да може да извиквам методи на web service-а, съм създал web reference към самия service. За мен, средата на Visual Studio генерира proxy, което има интерфейса на истинския web service. По този начин се улеснява работата с web service, тъй като за мен той по нищо не се различава от обикновен клас.

В метода Update първо извиквам метода HasNews, който извиква метода HasNews на web service-а. Ако в резултат имам true, тогава се извиква и методът Search на web service-a с параметър датата и часа на последната новина в базата на клиента. Тази дата се взима чрез метода GetLastNewsDate.

В резултат получавам DataSet, който съдържа в себе си една таблица с новините. Тези новини сега трябва да се запишат в клиентската база. За да направя това, е нужно да извикам съхранена процедура за всеки ред от таблицата в DataSet-a. Тук не мога да приложа стандартния подход, в който създавам команди за insert, update и delete, защото данните в таблицата не са променяни и следователно няма как да бъдат записани в базата.

За тази цел съм имплементирал метода ForceSave, който има следния вид:

public void ForceSave( DataSet dataSet )

{

DbCommand insertUpdateCommand = this.GetInsertUpdateCommand();


foreach( DataRow row in dataSet.Tables[ TableNames.NewsView ].Rows )

{

insertUpdateCommand.Parameters["@ID"].Value = row["ID"];



insertUpdateCommand.Parameters["@REC_DATE"].Value = row["REC_DATE"];

insertUpdateCommand.Parameters["@CODE"].Value = row["CODE"];

insertUpdateCommand.Parameters["@CATEGORY"].Value = row["CATEGORY"];

insertUpdateCommand.Parameters["@SOURCE"].Value = row["SOURCE"];

insertUpdateCommand.Parameters["@SUBJECT"].Value = row["SUBJECT"];

insertUpdateCommand.Parameters["@FULL_TEXT"].Value = row["FULL_TEXT"];

DataService.Instance.Database.ExecuteNonQuery( insertUpdateCommand );

}

}



Вижда се как в цикъл се изпълнява команда, получена чрез извикване на метода GetInsertUpdateCommand(). На командата се указват стойности на параметрите от конкретния ред от таблицата, след което тя се изпълнява. Самата команда се създава така:

private DbCommand GetInsertUpdateCommand()

{

DbCommand insertUpdateCommand =



DataService.Instance.Database.GetSqlStringCommand(

StoredProcedureNames.AddUpdateNews );


insertUpdateCommand.CommandType = CommandType.StoredProcedure;
DataService.Instance.Database.AddInParameter( insertUpdateCommand, "@ID",

DbType.Guid, "ID", DataRowVersion.Current );

DataService.Instance.Database.AddInParameter( insertUpdateCommand, "@REC_DATE",

DbType.DateTime, "REC_DATE", DataRowVersion.Current );

DataService.Instance.Database.AddInParameter( insertUpdateCommand, "@CODE",

DbType.String, "CODE", DataRowVersion.Current );

DataService.Instance.Database.AddInParameter( insertUpdateCommand, "@CATEGORY",

DbType.String, "CATEGORY", DataRowVersion.Current );

DataService.Instance.Database.AddInParameter( insertUpdateCommand, "@SOURCE",

DbType.String, "SOURCE", DataRowVersion.Current );

DataService.Instance.Database.AddInParameter( insertUpdateCommand, "@SUBJECT",

DbType.String, "SUBJECT", DataRowVersion.Current );

DataService.Instance.Database.AddInParameter( insertUpdateCommand, @FULL_TEXT",

DbType.String, "FULL_TEXT", DataRowVersion.Current );

return insertUpdateCommand;

}

Командата, върната като резултат от метода GetInsertUpdateCommand, е базирана на съхранената процедура SP_NWS_ADD_UPDATE_NEWS. Тази съхранена процедура има следния вид:



CREATE PROCEDURE SP_NWS_ADD_UPDATE_NEWS(@ID D_ID,

@REC_DATE D_DATETIME, @CODE D_CODE, @CATEGORY D_NAME,

@SOURCE D_DESCRIPTION, @SUBJECT D_DESCRIPTION,

@FULL_TEXT D_MEMO ) AS


IF NOT EXISTS (SELECT ID FROM NWS_NEWS WHERE ID = @ID)

BEGIN


INSERT INTO NWS_NEWS

(ID, REC_DATE, CODE, CATEGORY, SOURCE, SUBJECT, FULL_TEXT)

VALUES

(@ID, @REC_DATE, @CODE, @CATEGORY, @SOURCE, @SUBJECT, @FULL_TEXT)



END

ELSE


BEGIN

UPDATE NWS_NEWS

SET

REC_DATE = ISNULL(@REC_DATE, REC_DATE),



CODE = ISNULL(@CODE, CODE),

CATEGORY = ISNULL(@CATEGORY, CATEGORY),

SOURCE = ISNULL(@SOURCE, SOURCE),

SUBJECT = ISNULL(@SUBJECT, SUBJECT),

FULL_TEXT = ISNULL(@FULL_TEXT, FULL_TEXT)

WHERE ID = @ID

END

В процедурата, както се вижда от кода, първо се проверява дали вече не съществува новина със зададеното @ID. Ако такава новина няма, тогава се добавя в базата нов запис. При наличие на новина със същата стойност на първичния ключ, се актуализират нейните полета.


Заключение


Така създадената система, дори и в базовата си конфигурация, предоставя на потребителя достатъчна функционалност, за да може той да бъде информиран за текущото състояние на активите си. Системата също така подпомага и инвестиционните посредници при управлението на доверителните портфейли. Тъй като програмата не е ограничена до даден набор от активи, тя е приложима за всякакъв вид инвестиции.

Имплементирана на модулен принцип на практика означава, че във всеки момент могат да бъдат добавени неограничен брой модули, което означава и нова функционалност

Конфигурирането на база модули позволява да се удовлетворят желанията на различните потребители.

Библиография


Попов. Д.. Ценни Книжа и фондови борси. София, СИЕЛА – Софт анд пъблишинг, 2000, 367 с.

Левинсън, М. Финансови пазари – пътеводител. София, Класика и Стил, 2004, 326 с.

Бенсингър, Р. Нови идеи в техническия анализ. София, Класика и Стил, 2004, 310 с.

Фаулър, М. UML основи – кратко ръководство за стандартния език за обектно моделиране. София, СофтПрес, 2004, 204 с.

Гама, Е., Хелм, Р., Джонсън, Р., Влисидес, Д. Design Patterns., София, СофтПрес, 2005, 464

Интернет източници


Уикипедия, свободната енциклопедия - www.wikipedia.org

Инвестор БГ - www.investor.bg



www.investopedia.com

Вестник Капитал - www.capital.bg

Вестник Дневник - www.dnewnik.bg

Вестник Сега - www.segabg.com

Инвестиционен посредник Карол - www.karoll.net

Инвестиционен посредник Елана - www.elana.net

Microsoft Developer Network - msdn.microsoft.com

Microsoft Developer Network/Pattern&Practices - msdn.microsoft.com/practices





1 Попов Д, ‘Ценни книжа и фондови борси, СИЕЛА софт анд паблишинг, 2000г

1 www.wikipedia.org

2 www.segabg.com

1 www.segabg.com

2 http://www.investor.bg/?id=37033

3 Диверсификация – разпределяне на риска, като се инвестира в различни насоки – акции, облигации, недвижима имущество или различни индустрии.

1 CAB Help documentation

1 http://msdn.microsoft.com/practices/apptype/smartclient/default.aspx?pull=/library/en-us/dnpag2/html/cab.asp

2 Пак там

1 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag2/html/entlibjan2006_dataaccessappblock.asp

София, 2006


Сподели с приятели:
1   2   3   4   5




©obuch.info 2024
отнасят до администрацията

    Начална страница