ORM组件XCode(动手)

Coordinator
Apr 15, 2011 at 2:13 AM

    本篇才真正是XCode教程第一篇。《速览》是为了以最简洁的语言最短小的篇幅去吸引开发者;《简介》则是对XCode组件和XCode开发模式的一个整 体介绍,让开发者从宏观的角度去理解XCode;《共舞》把XCode提到了一个新的高度,让开发者感受到它的贵族血统!

    先抛出三篇来吸引人,再出《动手》,其实就是吊人胃口。如果到这里你还没有想试一试XCode的念头冲动,好吧,我承认是我的失败,不过你可以欺骗我,可别欺骗你自己!

 

    XCode开发模式建议先有数据库再有实体模型,然后借助代码生成器生成实体代码;当然你要反过来先做实体模型也是可以的,XCode之下的实体,支持反向生成数据库结构。

下面以《速览》中的UserMember为例,建立数据表:

数据表名: 用户 (UserMember)

 

 

中文名 

英文名 

数据类型 

大小 

是否主键 

是否唯一 

是否必填 

默认值 

编号

ID

Int32

10

 


账号

Account

String

50

 

 

 

 


显示名

DisplayName

String

50

 

 

 

 


 

wps_clip_image-26120

数据库命名规范:

 

² 名称必须使用通俗易懂的英文单词全拼,常用的缩略词(如ID)除外

² 使用驼峰命名规则,每个单词首字母大写,其它小写

² 名称必须简洁明了,不要加多余的前缀(如表名前加tbl),字段名也不要加表名前缀

² 不得使用SQL关键字或C#关键字作为表名或字段名

² 布尔型字段名称必须是IsAbb的形式

² 字符串类型统一使用nvarchar,大文本使用ntext,除非特殊情况,否则不用其它文本类型

² 建议给每张表建立一个自增的ID字段并作为主键,以利于数据分页管理

² 建议给每张表和每个字段加上说明

使用代码生成器生成代码(先看代码,待会讲过程):

代码
/// <summary>
/// 用户
/// </summary>
[Serializable]
[DataObject]
[Description(
"用户")]
[BindTable(
"UserMember", Description = "用户", ConnName = "Test")]
public partial class UserMember
{
    
#region 属性
    
private Int32 _ID;
    
/// <summary>
    
/// 编号
    
/// </summary>
    [Description("编号")]
    [DataObjectField(
truetruefalse10)]
    [BindColumn(
"ID", Description = "编号", DefaultValue = "", Order = 1)]
    
public Int32 ID
    {
        
get { return _ID; }
        
set { if (OnPropertyChange("ID", value)) _ID = value; }
    }
    
    
private String _Account;
    
/// <summary>
    
/// 账号
    
/// </summary>
    [Description("账号")]
    [DataObjectField(
falsefalsetrue50)]
    [BindColumn(
"Account", Description = "账号", DefaultValue = "", Order = 2)]
    
public String Account
    {
        
get { return _Account; }
        
set { if (OnPropertyChange("Account", value)) _Account = value; }
    }
    
    
private String _DisplayName;
    
/// <summary>
    
/// 显示名
    
/// </summary>
    [Description("显示名")]
    [DataObjectField(
falsefalsetrue50)]
    [BindColumn(
"DisplayName", Description = "显示名", DefaultValue = "", Order = 3)]
    
public String DisplayName
    {
        
get { return _DisplayName; }
        
set { if (OnPropertyChange("DisplayName", value)) _DisplayName = value; }
    }
    
#endregion

    
#region 获取/设置 字段值
    
/// <summary>
    
/// 获取/设置 字段值。
    
/// 一个索引,基类使用反射实现。
    
/// 派生实体类可重写该索引,以避免反射带来的性能损耗
    
/// </summary>
    
/// <param name="name">字段名</param>
    
/// <returns></returns>
    public override Object this[String name]
    {
        
get
        {
            
switch (name)
            {
                
case "ID"return ID;
                
case "Account"return Account;
                
case "DisplayName"return DisplayName;
                
defaultreturn base[name];
            }
        }
        
set
        {
            
switch (name)
            {
                
case "ID": _ID = Convert.ToInt32(value); break;
                
case "Account": _Account = Convert.ToString(value); break;
                
case "DisplayName": _DisplayName = Convert.ToString(value); break;
                
defaultbase[name] = value; break;
            }
        }
    }
    
#endregion

    
#region 字段名
    
/// <summary>
    
/// 取得字段名的快捷方式
    
/// </summary>
    public class _
    {
        
/// <summary>
        
/// 编号
        
/// </summary>
        public const String ID = "ID";
        
        
/// <summary>
        
/// 账号
        
/// </summary>
        public const String Account = "Account";
        
        
/// <summary>
        
/// 显示名
        
/// </summary>
        public const String DisplayName = "DisplayName";
    }
    
#endregion
}

 

    代码不多,分为属性、索引器和嵌套类三大块,其中后两块还不是必须的,所以即使是手工编码也不会太麻烦。

    所使用的代码生成器XCoder,是一个基于XCode的模版标签替换生成器。XCode提供数据库结构信息,用户设计模版,XCoder根据模版标签进 行替换。上面的代码还有数据字典表格,都是XCoder生成的,只是所使用的模版不同而已。有兴趣的朋友完全可以定制自己的代码生成器,DAL类的 Tables属性可以取得该连接的表架构信息,如DAL.Create("Test").Tables可以取得连接名为Test的数据库的架构信息。

    XCoder的使用很简单,打开配置文件XCoder.exe.config,增加一个连接字符串:

代码
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  
<connectionStrings>
    
<add name="Test" connectionString="Data Source=.;Initial Catalog=Test;Integrated Security=True;" providerName="System.Data.SqlClient"/>
  
</connectionStrings>
</configuration>

 

运行XCoder.exe,可以在连接下拉框选择连接字符串

wps_clip_image-4220

点击连接,列出该库所有表和视图

wps_clip_image-15353

设置命名空间、输出目录和连接名等信息,选择“数据”模版,点击生成

wps_clip_image-19110

因为XCode是充血模型,使用的时候是不需要指定数据库连接的,所以实体类里面默认指定连接名。

XCode模型追求简单实用,所以没有区分数据层和业务层。但是XCode实体类有数据类和业务类的说法,刚才上面的“数据”模版生成的就是数据类,下面生成业务类

wps_clip_image-30238

可以看出,数据类和业务类其实就是同一类,只不过使用了分部类partial,把一个类分拆到两个文件里面 去。数据类记录表结构信息,基本上依靠于生成;业务类第一次生成后只有一些注释,用于引导开发者如何实现自己想要的功能。业务代码等人工编写的代码,都要 求卸载业务类里面,当表结构改变需要重新生成代码时,仅生成数据类即可,人工编写的代码保留在业务类中,不至于被覆盖。

XCoder在输出目录生成了代码文件,复制到vs里面去

wps_clip_image-3181wps_clip_image-25038

再看看例子代码

 

代码
//新增数据,Save等效于Insert
UserMember user = new UserMember();
user.Account 
= "asdf";
user.Save();
//user.Insert();

//ID作为自增字段,保存后自动设为新值
Console.WriteLine(user.ID);

//查找数据,等效
user = UserMember.Find("Account""asdf");
user 
= UserMember.Find(UserMember._.Account, "asdf");

user 
= UserMember.FindByAccount("asdf");
user.DisplayName 
= "测试数据";

//读取成员数据,等效
String str = (String)user["DisplayName"];
str 
= user.DisplayName;

//给成员赋值,等效
user.DisplayName = "测试数据";
user[
"DisplayName"= "测试数据";

//保存数据,等效
user.Save();
//user.Update();

Console.WriteLine(user.ID);

user.Delete();

 

 

编译,提示user = UserMember.FindByAccount("asdf")这句报错,没有FindByAccout方法。忘了这是我手工写的了

 

public static UserMember FindByAccount(String account)
{
    
return Find(_.Account, account);
}

 

 

这个方法是根据账号查找用户。一般建议,FindByXxxx表示根据某个条件查询一个对象,FindAllByXxxx表示查询符合某个条件的所有对象的集合。

再次编译,通过。运行

wps_clip_image-12655

又忘了,我们还没有设置连接字符串呢。增加连接字符串

 

<add name="Test" connectionString="Data Source=.;Initial Catalog=Test;Integrated Security=True;" providerName="System.Data.SqlClient" />

 

 

这里要求连接名必须是Test,因为生成数据类的时候,指定了连接名为Test,所以才有刚才的异常,提示 设置Test连接字符串。后面就是标准的连接字符串了,当然,这个时候是可以修改为Access、Oracle、MySql等连接字符串的,尽管我们开始 的时候是在SqlServer中建立表结构。因为实体类已经建立完成,它与具体数据库无关,只有在运行时探测是哪一种数据库,再根据情况生成相应的查询/ 操作SQL。

XCode除了能获取数据库架构信息外,还能设置数据库架构,也就是能够根据实体类自动进行建表或者修改表结构。 所以,不用担心修改连接字符串指向别的数据库后,会因为没有数据表而报错。这个小功能有个好处,比如生产环境是Oracle数据库,而开发环境比较差,跑 不起Oracle,完全可以在开发环境用Access进行设计,部署到生成环境再修改连接字符串,XCode会尽其所能的屏蔽数据库操作差异。

打开XCode的OrmDebug开关(用于输出SQL语句),再次运行

wps_clip_image-18734

跟上面的代码进行比对,可以加深理解。OrmDebug开关对于学习XCode和解决问题非常有用。

上面是控制台的例子,下面看看Web的例子。

在生成实体类代码的时候,可以看到还有两个模版“列表页”和“表单”,取消“中文文件名”选择,分别生成这两个模版的代码。新建一个网站,把它们复制进去

wps_clip_image-19515wps_clip_image-2470

设置连接字符串,预览UserMember.aspx

wps_clip_image-19811

回到刚才的控制台代码,我们另外写一段插入测试数据的代码

 

for (int i = 0; i < 176; i++)
{
    UserMember user 
= new UserMember();
    user.Account 
= "User" + i;
    user.DisplayName 
= "用户" + i;
    user.Insert();
}

 

 

这段代码将会向数据库插入176行数据。刷新UserMember.aspx页面

wps_clip_image-24351

wps_clip_image-24647wps_clip_image-4652

wps_clip_image-4881

wps_clip_image-26768

列表显示、分页、排序、编辑、删除等功能都有了。

我们对这个页面做一点修改,添加一个到UserMemberForm.aspx的链接,并且把GridView里面的账号列改为超链接,也链接到UserMemberForm.aspx,并且带上ID作为参数。

wps_clip_image-19387

wps_clip_image-29363

点击“添加用户”

wps_clip_image-23060wps_clip_image-26119

添加一个用户

wps_clip_image-14110

点击账号aaa,进入表单编辑页面,注意地址栏的ID=179

wps_clip_image-29559

又一次,我们没有编写代码!

其实这些都是一些非常简单的功能,列表页就是GridView+ObjectDataSource +实体类,表单页就是FormView+ObjectDataSource+实体类,没有传递ID的时候为添加状态。

Web的例子就到这里,详细的用法可以回过头看看《与ObjectDataSource共舞》,里面提到的批量生产正是本篇所使用的代码生成器生成列表页和表单页。

 

 

大石头

新生命开发团队

2010-09-07 03:57