王朝网络
分享
 
 
 

dotText源码阅读(4)--DTO和数据访问

王朝other·作者佚名  2006-05-06
宽屏版  字体: |||超大  

Dottext中的DTO是一个是怎么实现的呢?这些是作为3层体系的实现内容。DTO的使用很多高人都有自己的看法,争论也不少。不过,我在这里要说的是dottext为什么要用DTO,我理解作者是想通过DTO确保维护3层体系,目的是解耦合各层之间的相互依赖,为各层之间的更新升级预留足够的维护空间。

Dottext.Framework.Data. IDTOProvider 定义了关于DTO的接口,这个接口涉及到的对象机器操作有:

Entry 也就是blog中发表的文章,其实体、对象的声明在Components目录下的Entry.cs中。需要注意的是该类继承了IblogIdentifier 接口,并且声明了[Serializable]属性。几乎定义的实体类型都类似该类。

Links 收藏的链接

Categories 类别,注意blog的系统分类和每一个博客的分类一起存储的,通过-1的blog来区分系统定义的分类。

Stats 统计信息

Configuration 配置类

KeyWords blog关键字

Images 相册

Archives 文章归档

ScheduledEvents 调度事件

Logger 日志对象

Rate 点击统计

Security 身份验证

MailNotify 邮件

IblogIdentifier接口是规定了该类必须要有一个归属哪一个blogID的,这容易理解,因为无论是文章还是统计信息、个人连结、收藏、相夹都属于私人的。

除了实体类外,还实现了相应实体类的收集类。对于数据绑定来说,很多人喜欢采用实体收集类来代替DataSet等,我也是这类人。这些实体收集类也标记了[Serializable]属性,也可以利用序列化进行配置

关于这些实体类的操作,dottext定义了IDTOProvider的接口,来定义对DTO的操作,具体在Dottext.Framework.Data目录下,这个接口需要其他具体类实现,但是体现了作者的设计思路:就是不把实现根据体的数据层死死捆绑,这个可以为我们借鉴。

为了进一步实现自己的思路,dottext还特意夹了一个数据层的抽象,IDbProvider ,此接口实现了对于各个DTC实体的数据访问,但是都是通过定义返回IdataReader和DataSet来实现对于具体数据库的封装。Dottext的作者在书写代码时候,做了大量的分类注释。关于数据层的具体实现,我的这个版本是基于SQL server的,所有具体的数据操作在Data目录下的SqlDataProvider.cs,这个类实现了IdbProvider,但是我们看到的几乎都是存储过程调用,而dottext的Sql版本大约110多个,所以要仔细阅读这些数据层访问细节,会花费很多时间。但是理清了这些头绪,我们可以知道如何去阅读甚至去实现修改了。

。那系统是如何实现数据访问的灵活配置呢?这个举一个例子,发表文章的操作,最终的操作是落在admin\ UserControls\ EntryEditor.ascx上(详细分析可能后面会补充),其中的代码如下:

private void UpdatePost()

{

if(Page.IsValid)

{

string successMessage = Constants.RES_SUCCESSNEW;

try

{

Entry entry = new Entry(EntryType);

entry.Title = txbTitle.Text;

……

entry.BlogID = Config.CurrentBlog(Context).BlogID;

if (PostID > 0)

{

successMessage = Constants.RES_SUCCESSEDIT;

entry.DateUpdated = DateTime.Now; entry.EntryID = PostID; entry.Link=Dottext.Framework.Configuration.Config.CurrentBlog().UrlFormats.EntryUrl(entry);

if(chkIsMoveTo.Checked)

{

entry.PostType=entry.PostType^((PostType)3);

}

Entries.Update(entry);

……

if (PostID > 0)

{

//LinkCollection lc = new LinkCollection();

ArrayList al = new ArrayList();

int count = cklCategories.Items.Count;

if(chkIsMoveTo.Checked)

{

count=0;

}

//文章分类

for (int i =0; i<count;i++)

{

if (cklCategories.Items[i].Selected)

{

al.Add(Int32.Parse(cklCategories.Items[i].Value));

}

}

//网站分类

。。。。。。

}

catch(Exception ex)

{ this.Messages.ShowError(String.Format(Constants.RES_EXCEPTION,

Constants.RES_FAILUREEDIT, ex.Message));

}

finally

{

Results.Collapsible = false;

}

}

}

这里的Entry属于DTO类型,在Components 下有解释。如果是第一次新发表的文章,那么会执行:

Entries.Update(entry);

此时会执行:

public static int Create(Entry entry)

{

return Create(entry,null);

}

静态方法,最终调用

public static int Create(Entry entry, int[] CategoryIDs)

{

HandlerManager.PreCommit(entry,ProcessAction.Insert);

int result = DTOProvider.Instance().Create(entry,CategoryIDs);

if(result > 0)

{

HandlerManager.PostCommit(entry,ProcessAction.Insert);

}

return result;

}

我们主要集中看看

int result = DTOProvider.Instance().Create(entry,CategoryIDs);

该语句执行的需要好好揣摩:

DTOProvider 的声明在providers目录下:

他有一个静态的声明构造函数

static DTOProvider()

{

DTOProviderConfiguration dtoPC = Config.Settings.BlogProviders.DTOProvider;

idto = (IDTOProvider)dtoPC.Instance();

}

用于在静态调用该类的方法之前执行构造(这里相当于使用了单例模式)。此时会利用前民提到的配置体系获取DTOProvider配置。DTOProviderConfiguration 具有[XmlRoot("DTOProvider")]属性,从<BlogProviders>节获得的XML片断中得到DTOProviderConfiguration,而需要注意这里的DTOProviderConfiguration继承自一个抽象类BaseProvider。

Config.Settings.BlogProviders 通过反序列化得到了具体的BlogProvider类,但是我们仅仅想获取DTOProvider的属性,而在我手中的版本该处的配置是Dottext.Framework.Data.DataDTOProvider(注意该类的实现了IDTOProvider接口)。DTOProviderConfiguration类型实际上是抽象类BaseProvider的具体实现,但是加上了[XmlRoot("DTOProvider")]属性(可以反序列化),这样将得到了一个provider类型。

idto = (IDTOProvider)dtoPC.Instance();

dtoPC.Instance();会调用BaseProvider(注意是抽象类)中的

public object Instance()

{

return Activator.CreateInstance(System.Type.GetType(this.ProviderType));

}

大家看到,这又是一个动态产生类型的方法,也是采用了反射原理。该类的属性ProviderType声明是这样的

[XmlAttribute("type")]

public string ProviderType

{

get { return _type; }

set { _type = value; }

}

可以看到,这是从配置文件中读取到的Type值,具体到我察看的工程值是:Dottext.Framework.Data.DataDTOProvider, Dottext.Framework

这样会实例化DataDTOProvider类,而DataDTOProvider实现了IDTOProvider接口。通过这样的“复杂”的过程,DTOProvider静态构造了一个可以访问数据库层的接口(DTOProvider.Instance()语句)IDTOProvider,而IDTOProvider中正好可以实现了接口函数

int Create(Entry entry, int[] CategoryIDs);这里相对于SQL数据层的细节如下(见得到的是实现类DataDTOProvider):

public int Create(Entry entry, int[] CategoryIDs)

{

if(entry.PostType == PostType.PingTrack)

{

return DbProvider.Instance().InsertPingTrackEntry(entry);// DbProvider稍后解释

}

FormatEntry(ref entry);

if(entry is CategoryEntry)

{

entry.EntryID = DbProvider.Instance().InsertCategoryEntry(((CategoryEntry)entry));

}

else

{

entry.EntryID = DbProvider.Instance().InsertEntry(entry);

if(CategoryIDs != null)

{

DbProvider.Instance().SetEntryCategoryList(entry.EntryID,CategoryIDs);

}

}

if(entry.EntryID > -1)// && Config.Settings.Tracking.UseTrackingServices)

{

entry.Link = Dottext.Framework.Configuration.Config.CurrentBlog().UrlFormats.EntryUrl(entry);

Config.CurrentBlog().LastUpdated = entry.DateCreated;

}

else

{

//we need to fail here to stop the PostCommits?

throw new BlogFailedPostException("Your entry could not be added to the datastore");

}

return entry.EntryID;

}

在这里,实现了对于Entry实体的实体化存储操作。其中的DbProvider又是值得关注的

static DbProvider()

{

DbProviderConfiguration dpc = Config.Settings.BlogProviders.DbProvider;

dp = (IDbProvider)dpc.Instance();

dp.ConnectionString = dpc.ConnectionString;

}

看到没有,跟DTOProvider又是一样的静态化构造。通过序列化的到具体的DB存储操作实体对象Dottext.Framework.Data.SqlDataProvider, Dottext.Framework。在创建一个Entry对象的DB操作中有如下代码:

entry.EntryID = DbProvider.Instance().InsertEntry(entry);

SqlDataProvider的InsertEntry操作细节如下:

public int InsertEntry(Entry entry)

{

SqlParameter[] p =

{

SqlHelper.MakeInParam("@Title", SqlDbType.NVarChar,255,entry.Title),

SqlHelper.MakeInParam("@TitleUrl", SqlDbType.NVarChar,255,DataHelper.CheckNull(entry.TitleUrl)),

SqlHelper.MakeInParam("@Text",SqlDbType.NText,0,entry.Body),

SqlHelper.MakeInParam("@SourceUrl",SqlDbType.NVarChar,200,DataHelper.CheckNull(entry.SourceUrl)),

SqlHelper.MakeInParam("@PostType",SqlDbType.Int,4,entry.PostType),

SqlHelper.MakeInParam("@Author",SqlDbType.NVarChar,50,DataHelper.CheckNull(entry.Author)),

SqlHelper.MakeInParam("@Email",SqlDbType.NVarChar,50,DataHelper.CheckNull(entry.Email)),

SqlHelper.MakeInParam("@Description",SqlDbType.NVarChar,500,DataHelper.CheckNull(entry.Description)),

SqlHelper.MakeInParam("@SourceName",SqlDbType.NVarChar,200,DataHelper.CheckNull(entry.SourceName)),

SqlHelper.MakeInParam("@DateAdded",SqlDbType.DateTime,8,entry.DateCreated),

SqlHelper.MakeInParam("@PostConfig",SqlDbType.Int,4,entry.PostConfig),

SqlHelper.MakeInParam("@ParentID",SqlDbType.Int,4,entry.ParentID),

SqlHelper.MakeInParam("@EntryName",SqlDbType.NVarChar,150,DataHelper.CheckNull(entry.EntryName)),

BlogIDParam,

SqlHelper.MakeOutParam("@ID",SqlDbType.Int,4)

};

NonQueryInt("blog_InsertEntry",p);

return (int)p[14].Value;

}

看到否,这是一个具体的sql存储过程调用代码。

就这样,利用配置文件我们指定了BlogProviders 的DTOProvider 和DbProvider 具体实例,而根据不同的配置,他们是可以替换成不同的实例,譬如可以将DB层换成Mysql.或者Orcal的具体表操作。

以上阅读,大家需要注意:

1、 静态构造函数

2、 Activator.CreateInstance(System.Type.GetType(this.ProviderType)); 这种利用反射创建对象实例的方法。

另外就是需要理解,dottext采用配置文件来动态指定DTO和DB操作的精巧设计(虽然有些让人懵头)。

遗憾的是,我发现博客园的版本似乎有问题,很多操作,他们直接操纵了数据库,这样可能会学让我这种不熟悉源版本的人会产生一定的误解。以上分析,希望能够排除大家的疑问,我可是熬了个通宵哦:)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有