不用第三方之C#自己实现Visual SourceSafe 管理Oracle的程序

还是自己写的东西顺手些,至少启动时没有怪叫。。。

下面向大家介绍如何使用VSS管理Oracle中写的代码。

一、简介

    Visual SourceSafe(以下简称VSS) 是微软公司推出的一款版本管理软件,可以很方便的管理源代码的版本和处理多方协作,目前相似的软件有很多,而且有很多也是相当不错的,但是在本文所描述的功能中,VSS足以完成我们要求的任务。

    Oracle是一款大型的数据库软件,号称管理着世界上60%的数据库,在这里不对其强大的功能进行更多的描述。在Oracle中,可以使用PL/SQL语言编写函数(Function),存储过程(Procedure),及包(Package),一般情况下,这些程序都是以源代码的形式存储于数据库中(特别的加密情况除外)。

    在程序经常需要修改的时候,版本管理显的特别重要,本人在使用过程中就经常为此事烦恼。以往的作法是在每次修改程序之前或之后,都把存储过程之类的存成一个文件,在文件名后加一个日期,表示程序的版本,然后放入专用的目录中,以便历史追溯,这样就会造成一个程序会存在大量的文件,给后期的维护工作带来很大的麻烦。后来又尝试把生成的文件放入VSS中管理,但是使用很烦琐,需要先存成文件,然后在VSS客户端中checkin,如果需要把很多的程序checkin到VSS中,可是个不小的工作量,在使用Oracle11i系统的时候,apps用户下的package达到3万多个,源代码加起来有1G的数量级,如果用上面的工作方式来checkin,恐怕日子不好过了。

    因此,为了解决以上问题,本人用C#开发了一个专用的程序,暂且命名为“Visual SourceSafe for Oracle”,也即是说,它是专门为Oracle服务的VSS客户端,如果需要使用此程序,有个前提条件,就是所在的机器必须装有Oracle的客户端软件和VSS的客户端软件,因为程序中要用到它们相应的组件。

    首先需要看的是如果利用自己的程序向VSS中checkin内容,这是本程序最关键的地方,在经过资料查找后,发现可以利用VSS中的SSAPI.DLL文件来实现这个功能,这个DLL是VSS提供的专门用于处理VSS事物的对外的API接口,在vs中可以直接引用,vs会自动生成一个托管的类,名字为Interop.SourceSafeTypeLib.dll,在这个API中,有我们所需要的一切的API函数,包括checkin checkout additem等,在后面会有介绍。

    说完VSS这边,要再来说一下Oracle这边,我们必须把程序从数据库里取出来才能写到VSS中,否则VSS也是无用武之地了。Oracle的Function/Procedure/Package(Package Header/Package Body)全部存储于视图USER_SOURCE中,在这个视图中,几个字段的含义如下:

    NAME:程序的名字,可以是包的名字,也可以是存储过程的程序

    TYPE:程序的类型,如包或存储过程等

    LINE:行号,每条记录只存储程序的一行的值

    TEXT:程序中每行的内容

    根据以上结构,要找出一个存储过程的具体内容应该是找到NAME=存储过程名的所有TEXT的字段的值,并且按LINE字段来排序,如下所示:

select text from USER_SOURCE where name='XXXX' order by line

    至于如何把这些行变成一个字符串,所有编程语言都应该是差不多的,只要依次把所有行的内容简单的加起来就行了,当然,不要忘记在每行的中间加一个换行符,这样程序才像以前的样子,否则就都成一行了,难看又难懂。

二、实现

    1.登录

    在程序实现方面,第一部分就是要制作一个登录界面,这个登录与VSS登录不同,与Oracle登录也不同,因为在同一个登录界面里,我同时处理了登录Oracle和VSS两者,界面如下图:

clip_image002

    根据界面上的字面意思,可以很容易知道这个界面如何使用。

    在登录程序里,使用了一个小技巧,就是利用了注册表,在每次登录成功后,会把当前的数据库登录用户名,数据库连接串,VSS登录用户名,VSS配置文件名写入到注册表中,下次登录的时候,直接从注册表中取出,可以提高程序的使用效率。

    读取注册表的代码部分如下:

Microsoft.Win32.RegistryKey key =

Microsoft.Win32.Registry.CurrentUser.OpenSubKey(REGISTRY_KEY, false);

if (key != null)

{

txtOracleUserName.Text = key.GetValue("ORACLE_USER_NAME").ToString();

txtOracleDB.Text = key.GetValue("ORACLE_CONNECTION_STRING").ToString();

txtSSUserName.Text = key.GetValue("SS_USER_NAME").ToString();

txtSSDB.Text = key.GetValue("SS_CONNECTION_STRING").ToString();

}

    其中REGISTRY_KEY= "SOFTWARE\\VSS4Oracle\\Login",表示在注册表中存储的路径。

    登录成功后写注册表的代码如下:

Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(REGISTRY_KEY, true);

if (key == null)

{

key = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(REGISTRY_KEY);

}

key.SetValue("ORACLE_USER_NAME",txtOracleUserName.Text);

key.SetValue("ORACLE_CONNECTION_STRING",txtOracleDB.Text);

key.SetValue("SS_USER_NAME",txtSSUserName.Text);

key.SetValue("SS_CONNECTION_STRING",txtSSDB.Text);

    本模块只处理简单的登录,登录后把窗体上的各个参数传给主窗体,数据库用户的校验和VSS用户的校验由主窗体来完成。为了把参数传给主窗体,需要给本窗体增加几个属性,用来表示窗体上各个文件框控件的值,之所以不直接使用控件的值而增加属性来表示,是因为控件默认状态是私有变量,不提倡把私有变量变成公有变量。属性如下:

public string OracleUserName

{

get{return txtOracleUserName.Text;}

}

public string OraclePassword

{

get{ return txtOraclePassword.Text;}

}

public string OracleDB

{

get{

if (txtOracleDB.Text == "")

{

return "(local)";

}

else

{

return txtOracleDB.Text;

}

}

}

public string SSUserName

{

get{ return txtSSUserName.Text;}

}

public string SSPassword

{

get{ return txtSSPassword.Text;}

}

public string SSDB

{

get{return txtSSDB.Text;}

}

2.列出VSS中的项目及内容列表

    由登录窗体得到各种需要的参数后,就转到主窗体,主窗体在初始状态下,最主要的任何是把VSS中相关的项目列出来供使用。

    您的VSS中可能会存储Oracle以外的程序,但是在本程序中,只对我们相关的Oracle程序部分进行显示,其它内容不做处理,VSS中显示的项目结构如下:

$/Oracle/连接串名字/Oracle用户名/

Function

Package

Procedure

Sequence

Synonym

Table

Trigger

View

    以上几类在Oracle中都是以可以找到它们的源代码,如果您需要增加您新的类型,可以直接在后面的程序中进行修改。

主程序的界面如下:

clip_image004

系统在每次登录的时候,都会检查VSS中是否包括所需要的上述的项目结构,如果不存在,则自动创建。过程如下:

在主窗体的代码中要添加一个新的命名空间的引用:using SourceSafeTypeLib;

然后在其它操作之前要把它VSS的数据库,代码如下:

db = new VSSDatabaseClass();

db.Open(ssDB,ssUserName,ssPassword);

其中db是一个私有变量,类型就是VSSDatabaseClass

打开数据库后,开始创建所需要的项目,代码如下:

string root = "$/Oracle/" + oracleDB + "/" + oracleUserName ;

CreateProjectTree(root);

string[] items = new string[8];

items[0] = "Table";

items[1] = "View";

items[2] = "Trigger";

items[3] = "Function";

items[4] = "Procedure";

items[5] = "Package";

items[6] = "Sequence";

items[7] = "Synonym";

VSSItem item = db.get_VSSItem("$/",false);

for(int i = 0; i < items.GetLength(0); i++)

{

try

{item.NewSubproject(root + "/" + items[i],"created");}

catch(Exception ex)

{System.Diagnostics.Debug.WriteLine(ex.Message);}

}

    请注意创建的时候用的是try/catch结构,也就是当需要创建的项目是存在的时候,就跳过去直接处理其它的事物,因为我没有发现db中有一个函数来检测是否存在一个项目,所以只能用此种变通的方式来处理。

    经过上面的创建工作,在VSS中就已经存在了需要的结构了,您可以直接使用VSS的客户端去查看,如果是第一次使用,那么每个项目的里面是没有任何内容的,当然了,是因为您还没有进行任何的checkin操作。

   在创建了VSS结构后,主窗体中有一个TreeView控件,用来显示这个结构,代码如下:

rtbMessages.AppendText("Building Source Safe project hierarchy ... \n");

Application.DoEvents();

Cursor = Cursors.WaitCursor;

tvProject.Nodes.Clear();

try

{

string root = "$/Oracle/" + oracleDB + "/" + oracleUserName;

tvProject.Nodes.Add(root);

VSSItem vssProj = db.get_VSSItem(root , false);

IVSSItems items = vssProj.get_Items(false);

foreach(VSSItem item in items)

{

tvProject.Nodes[0].Nodes.Add(item.Name);

}

tvProject.Nodes[0].Expand();

}

catch(Exception ex)

{

rtbMessages.AppendText("\n\n" + ex.ToString());

}

finally

{

Cursor = Cursors.Default;

rtbMessages.AppendText("\n\n... Done");

}

    上述功能只是显示一个树的结构,并不包括具体的内容,比如具体有哪些函数,有哪些的存储过程,在上述代码中还没有体现。这部分的显示功能放在左侧的树的after_selected事件中,即选择任何一个项目,就显示该项目的具体情况。

clip_image006

    根据上图可以看出,系统中有两个package,根据图标可以看出两个package有所不同,第一种图标表示该对象已经存储于VSS中,而第二个图标表示此对象在VSS中系统还从没有被签入过。

3.签入(checkin)

    checkin是指把数据库中的对象的代码导入到VSS系统中,签入有几种方式,在对象上点右键,或在左侧的目录树中点右键都会弹出签入的对话框,作用是相同的,但是作用的范围有所不同,如果在对象上点签入,就是只针对所选择的对象进行签入(可以是单选也可以是多选),如果在左侧的目录树上点签入,会整个目录进行签入,比如在Package上签入,会把系统的所有的Package签入,如果系统中Package较多,而实际更新的并不多,这种作法将很费时间;如果在$/Oracle/(local)/scott这样的根结点上点右键,则会把所有的Function/Procedur/Package/…等所有的对象全部签入,在一个大系统中,这将是一个非常费时间的过程,要根据实际情况选择使用,而如果是一个比较小的系统,经常采用这种方式,会把一些丢掉没有签入的对象补到VSS中,是一个比较好的办法。

    签入的的对话框我根据我当前的需求,只设计了一个文本框,就是check的comment,如下图:

clip_image008

    用于记录本次签入的注释。

    在签入之前,我们需要得到Oracle对象的具体内容,如Package,我们要得到具体的代码才可以,具体的取法在上面已经提到。

    因为VSS的特性决定,我们在得到代码后,必须生成一个实际的物理文件,才可以被VSS使用,因为使用StreamWriter对象把所得到的内容生成一个文件,如下:

string fileName = tempDir + "\\" + contentName;

if (File.Exists(fileName))

{

File.SetAttributes(fileName,FileAttributes.Normal);

File.Delete(fileName);

}

StreamWriter writer = new StreamWriter(fileName,false,System.Text.Encoding.Default);

writer.Write(content);

writer.Close();

    在上面的代码中,contentName表示对象名,也将用作文件名,tempDir可以是一个临时目录,也可以是您自己强行指定的一个目录,content是从DB中生成的对象的实际的代码。

    在Oracle的命名规则中,”$” 这个字符是合法的,而在VSS中,这个字符表示VSS的根路径,所以变为不合法的字符,也就是如果某个包的名字包括这个字符,将不能正常checkin,在Oralce11i系统中,这个字符被大量的用在package的名字中。为了解决这个问题,需要把$字符替换成别的字符,并且在生成文件的时候,需要操作系统也能正常识别,经过测试,选择了“@”这个字符,因此上述的代码改成如下:

    string fileName = tempDir + "\\" + contentName.Replace("$","@");

    在最后的签入的步骤中,直接使用VSSItem的checkin方法或Add方法就可以了。

posted on 2010-04-13 16:49  Aicken(李鸣)  阅读(3557)  评论(5编辑  收藏  举报