OpenAccess常见问题

导读:本文以我自己个人的实际使用方式和经历,列举一些常遇到的问题的解决方案。我在这里继续以Oracle为实例数据库。

创建数据库和表

 SQL Code 
1
2
3
4
5
6
7
CREATE TABLE "Person"
(
    
"PersonId" NUMBER (100NOT NULL,
    
"PersonName" VARCHAR2 (36NOT NULL,
    
"Type" NUMBER (10NOT NULL,
    
PRIMARY KEY ("PersonId")
);

创建项目

  数据库创建后,根据OpenAccess基本操作介绍的方法创建项目,然后打开EntitiesModel.rlinq

常见问题汇总

字段的默认值

  在程序中指定属性默认值的最简单方式是在构造函数中给对应的属性赋相应的值。

 C# Code 
1
2
3
4
5
6
7
8
9
10
namespace Sunny.Model
{
    partial 
class Person
    {
        
public Person()
        {
            
this.PersonName = "Sunny";
        }
    }
}
自增主键

  在Oracle里,一般是通过触发器,使流水号插入主键值。具体操作步骤是:首先,在数据库中创建触序列(Sequence)和触发器。

 SQL Code 
1
2
3
4
5
6
7
8
/*创建序列*/
CREATE SEQUENCE "PersonId" MINVALUE 1 MAXVALUE 999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE;
/*创建触发器*/
CREATE
OR REPLACE TRIGGER "Trigger_Person_Insert" BEFORE INSERT ON "Person" REFERENCING OLD AS "OLD" NEW AS "NEW" FOR EACH ROW
BEGIN
    
SELECT "PersonId".nextval INTO : NEW ."PersonId" FROM dual ;
END;

  然后在Person的属性窗口中,把Identity Mechanism属性设置成DatabaseServerCalculated。

  这样一来,当对象插入到数据库后,就可以通过Person.PersonId属性获取对象的主键值。如果你不需要在这个时候获取这个主键值,也可以不设置Identity Mechanism属性。

  如果你希望在对象插入到数据库之前获取主键值,那么只能在对象插入到数据库前,通过调用序列(Sequence)获取。

  OpenAccess没有对Sequence进行映射封装的机制,只能之前通过Sql获取。

 C# Code 
1
2
3
Sunny.Model.EntitiesModel model = new Sunny.Model.EntitiesModel();
Sunny.Model.Person person = 
new Model.Person();
person.PersonId = model.ExecuteScalar<
int>("SELECT \"PersonId\".nextval FROM dual");
枚举映射

  在Oracle中,字段类型Number是映射到C#中的decimal类型的,但也可以映射到枚举类型。以下介绍操作步骤:

  首先,定义一个枚举。

 C# Code 
1
2
3
4
5
6
7
8
namespace Sunny.Model
{
    
public enum PersonType
    {
        Worker,
        Farmer
    }
}

  然后设置Person中Type的类型为刚刚定义的属性PersonType。

  最后我们可以通过枚举类型查询数据库。

 C# Code 
1
2
3
4
Sunny.Model.EntitiesModel model = new Sunny.Model.EntitiesModel();
foreach (Sunny.Model.Person farmer in model.People.Where(p => p.Type == Sunny.Model.PersonType.Farmer))
{
}
Oracle.DataAccess.dll版本问题

  OpenAccess默认是使用Oracle 10g的Oracle.DataAccess.dll操作数据库的,但实际上也可以使用更高版本(例如是11g)的Oracle.DataAccess.dll来操作数据库。

  具体操作步骤是在项目中引用11g的Oracle.DataAccess.dll后,在web.config或app.config中增加system.data节点指定Oracle.DataAccess.dll的版本。

 XML Code 
1
2
3
4
5
6
7
<configuration>
  
<system.data>
    
<DbProviderFactories>
      
<add name="Oracle Data Provider for .NET" invariant="Oracle.DataAccess.Client" description="Oracle Data Provider for .NET" type="Oracle.DataAccess.Client.OracleClientFactory,Oracle.DataAccess,Version=2.112.3.0,Culture=neutral,PublicKeyToken=89b483f429c47342"/>
    
</DbProviderFactories>
  
</system.data>
</configuration>

OpenAccess类之间的关联

导读:本文以实际操作为例,介绍在OpenAccess的编辑器中,如何建立两个类之间的关联。我在这里继续以Oracle为实例数据库。

创建数据库和表

 SQL Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CREATE TABLE "Person"
(
    
"PersonId" RAW (16),
    
"PersonName" VARCHAR2 (36NOT NULL,
    
PRIMARY KEY ("PersonId")
);
CREATE TABLE "Child"
(
    
"ChildId" RAW (16),
    
"PersonId" RAW (16NOT NULL,
    
"ChildName" VARCHAR2 (20NOT NULL,
    
PRIMARY KEY ("ChildId")
);
CREATE TABLE "Worker"
(
    
"PersonId" RAW (16),
    
"Department" VARCHAR2 (20NOT NULL,
    
PRIMARY KEY ("PersonId")
);

创建项目

  数据库创建后,根据OpenAccess基本操作介绍的方法创建项目,然后打开EntitiesModel.rlinq

1对多关系

  在工具箱中选择Association,从Child连线到Person,编辑器会弹出对话框。

 

  选择Child.PersonId关联到Person.PersonId,然后点击OK关闭对话框。

 

  这样,Child和Person之间的1对多关联就建立了。可以看见编辑器为Person类添加了一个Children属性,为Child类添加了一个Person属性。

 

  最后,可以在程序中使用这两个属性,访问被关联的对象。

 C# Code 
1
2
3
4
Sunny.Model.EntitiesModel model = new Model.EntitiesModel();
foreach(Sunny.Model.Person person in model.People)
    
foreach (Sunny.Model.Child child in person.Children)
        System.Console.Write(
"Parent Name:{0} Child Name:{1}"child.Person.PersonName, child.ChildName);

1对1关系

  在工具箱中选择Association,从Child连线到Person,编辑器会弹出对话框。

 

  选择Worker.PersonId关联到Person.PersonId,然后点击OK关闭对话框。

 

  这样,Worker和Person之间的1对1关联就建立了。可以看见编辑器为Person类添加了一个Worker属性,为Worker类添加了一个Person属性。

 

  最后,可以在程序中使用这两个属性,访问被关联的对象。

 C# Code 
1
2
3
4
5
Sunny.Model.EntitiesModel model = new Model.EntitiesModel();
foreach (Sunny.Model.Person person in model.People)
    System.Console.Write(
"Person:{0} Department:{1}", person.PersonName, person.Worker == null ? "" : person.Worker.Department);
foreach (Sunny.Model.Worker worker in model.Workers)
    System.Console.Write(
"Person:{0} Department:{1}", worker.Person.PersonName, worker.Department);

  在OpenAccess里,强制规定主键和主键之间的关联是1对1,其他字段和主键的关联是1对多,并且目标类(Taget Class)中的关联字段必须是主键。这种强制不能更改的硬性规则,我自己感觉上不知道会不会有失灵活性。

  除了1对n关联外,OpenAccess还有一种通过中间表关联两个类的多对多关联方式,可以在上面提及的“关联编辑器(Association Editor)”窗口中的“关系视图(Relational View)”选项卡中作这种多对多关联的设置。这里为了保持本文的简洁性不作详述,有兴趣可以自己尝试体会。

继承

  对于Perosn和Worker来说,通过继承来关联,似乎是比1对1关联更加合理的选择。下面演示操作步骤:

 

  首先,删除Worker的PersonId属性,在工具箱中选择Inheritance,从Child连线到Person。

 

  接着在“映射明细编辑器(Mapping Details Editor)”中,选择“继承策略(Inheritance Strategy)”为“纵向(Vertical)”

  除了Vertical,OpenAccess还有Flat和Horizontal两种继承方式。Flat表示派生类和基类都存储在同一张表,Horizontal表示最顶层的基类没有映射到数据库中的任何表。由于这两种继承方式比较少用到,本文不作详述,有兴趣可以的话可以自己尝试一下。

 

  如果Worker没有映射到表,重新选择映射到Worker。

  不知道是不是OpenAccess的Bug,每次建立继承关联的时候,派生类的映射表都会被清空。

 

  最后在属性窗口中,把Worker的Concurrency Mode属性设为Default。

  不这样设的话操作数据库的时候会出错,至于为什么会这样这里就不作详述了,有兴趣可以自己探索一下。

 

  这样,两个类之间的继承关联就设置完毕了。如果在程序中使Worker对象往数据库中添加数据,OpenAccess会同时在Person和Worker两张表中添加数据。

 C# Code 
1
2
3
4
5
6
7
Sunny.Model.EntitiesModel model = new Model.EntitiesModel();
Sunny.Model.Worker worker = 
new Model.Worker();
worker.PersonId = System.Guid.NewGuid();
worker.PersonName = 
"Sunny";
worker.Department = 
"Development";
model.Add(worker);
model.SaveChanges();

OpenAccess基本操作

导读:本文以一个实例介绍OpenAccess如何对数据库中的表,做最基本的查询、添加、编辑、删除操作

为什么选择OpenAccess?

据我所知,.Net平台上的ORM框架主要有Entity Framework、Linq to Sql、OpenAccess、dotConnect、XPO几种,我在其中选择OpenAccess的首要原因在于根据Goolge上的搜索结果,OpenAccess比其他ORM优胜。

其次,Entity Framework的最新版已经不支持.Net Framework 3.5了,但OpenAccess仍然继续支持。因为我们公司的主流.Net开发工具是VS.Net 2008,这意味着OpenAccess是一个更为合适的选择。

最后,Entity Framework虽然得到了各大厂商的官方支持,但也有美中不足之处。以Oracle为例,Oracle在Oracle Data Access Component 11中增加了对Entity Framework的支持,但Oracle 11的客户端是不能连接9的数据库的。我们公司现时主要使用的数据库是Oracle 9i,这意味着OpenAccess是一个更为合适的选择。

另外,OpenAccess的开发商是Telerik,我们可以从这家公司的产品线上看到它的实力。

我想OpenAccess相对来说最大缺点莫过于没有中文版,纠结于英文的人可能比较适用使用Entity Framework。

创建本实例的先决条件

首先,从官方的下载网页,点击“Automatic Installation”下载OpenAccess。

http://www.telerik.com/account/your-products/product-versions.aspx?pid=639

这个页面是需要注册才能进入的,如果没有账号,要先注册一个。

其次,OpenAccess是通过调用Oracle.DataAccess.dll操作数据库的,所以需要下载并安装Oracle Data Access Component 10

http://download.oracle.com/otn/other/ole-oo4o/ODAC1020221.exe

  OpenAccess支持的数据库有很多,但由于我们公司现在使用的是Oracle数据库,所以本文主要针对的还是Oracle数据库。

本实例创建的步骤

在数据库中创建一个表

 C# Code
1

2

3

4

5

6

CREATE TABLE “Person”

(

“PersonId” CHAR(36) NOT NULL,

“PersonName” VARCHAR2(36) NOT NULL,

primary key (“PersonId”)

)

在VS.Net中创建解决方案。

在解决方案中添加“Telerik OpenAccess Class Library”项目。

在上图对应的操作窗口中点击“确定”后,将会弹出以下操作向导。

我这里直接填写连接字符串来连接数据库,也可以通过点击“Add New Connection”按钮来连接。

“Connection String Name”是程序的config文件中,configuration/connectionStrings节点下,存放连接字符串的add节点的name属性的值。

 XML Code
1

2

3

4

5

<configuration>

<connectionStrings>

<add name=“Model” connectionString=“Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.1.95)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TEST)));User ID=TEST;Password=0;Enlist=false” providerName=“Oracle.DataAccess.Client”/>

</connectionStrings>

</configuration>

选择要生成的对象。

这里是生成的变量的命名规则,默认即可。

点击“Browser…”按钮,创建并选择“Generate”作为存放生成文件的目录。

在上图对应的操作窗口中点击“完成”后,打开EntitiesModel.rlinq,更改PersonId的类型为Guid。

更改Person的Identity Mechanism为Guid,大概意思是添加数据的时候,主键插入一个新的Guid。

更改Person的Concurrency Mode为None。

  Concurrency Mode是并发冲突的处理方式,处理并发冲突会增加编辑、删除数据时的性能负荷,我觉得绝大多数情况都不需要它。所以我建议把所有对象的Concurrency Mode都设成None,如果想了解更多关于“并发冲突”的概念,可以查阅网上相关资料。

最后在解决方案中,添加一个“Web Application应用程序”项目,并在项目中添加对Model项目的引用。

可能出现的问题

不知道是不是我机上只安装了Oracle Data Access Component,但没有安装Oracle客户端的原因,在OpenAccess的设计器中有时间会出现找不到Oracle.DataAccess的错误(程序中没有出现这个错误,仅限于设计器)。

解决方法是,如果在上述选择对象的界面中出现这个错误,点击“Next”跳过这一个步骤。

然后有点怪异的是,点击一下“Add OpenAccess Service…”这个问题就会消失了。

最后打开EntitiesModel.rlinq重新选择对象。

关于整个实例

本实例通过本文的附件下载,解决方案是VS.Net 2008格式。由于代码的注释都在源文件当中,这里就不重复叙述了。

实例中演示了对数据库中的Person表做最简单的查询(包括分页)、添加、编辑、删除操作。

Web项目中List.aspx是列表页面,包括查询、删除功能。Edit是编辑页面,包括添加和修改功能。

两个项目中都有一个.config文件,OpenAccess设计器在访问数据库的时候,读取的是Model项目中的config文件中的连接字符串所指向的数据库。但实际上程序运行的时候,OpenAccess读取的时候调用它的那个程序的config文件,即Web项目中的config文件。

要运行这个本实例,在数据库中建表后,修改config文件中的连接字符车即可。

关于数据类型的映射

在Sql Server数据库中,字段的每种数据类型在C#里都有明确对应的变量类型。但在Oracle当中,情况又不一样。例如Oracle建议统一使用Number作为数字类型,而不要使用Integer、Int等类型。Oracle的Number默认对应C#的类型是decimal,但更多时候,我们需要的是int。所以很多时候需要在设计器中更改属性的类型。

有鉴于此,在这里列出.Net和Oracle对应的数据类型作参考。

.NET

Oracle data types

System.Boolean NUMBER(1)
System.Byte NUMBER(3)
System.Byte[] BLOB
System.DateTime TIMESTAMP
System.DateTimeOffset TIMESTAMP WITH TIME ZONE
System.Decimal NUMBER
System.Double NUMBER
System.Guid CHAR(36)/RAW(16)
System.Int16 NUMBER(5)
System.Int32 NUMBER(10)
System.Int64 NUMBER(19)
System.SByte NUMBER(3)
System.Single NUMBER(15,5)
System.String VARCHAR2
System.TimeSpan INTERVAL DAY TO SECOND

下一篇日志,将讨论在实际中OpenAccess更多细节上的使用技巧。

本文附件:OpenAccessBasic

附件实际上是.zip文件,因为博客系统的限制,改成了doc,下载后改回zip扩展名即可打开。

Linq基础简介

导读:本文旨在以最简单的方式描述Linq的最基本和常用的使用方法,如果你希望对相关知识有更深层次的了解,可以在网上查阅其它相关资料。

什么是Linq?

  细心的人应该已经发现,在VS.Net 2008(或以上本版)新建一个WebForm(*.aspx)或其他类文件后,已经在该.cs文件的using部分已经添加了对Linq命名空间的引用:

 C# Code
1
using System.Linq;

  现在我们在这个.cs文件里的一个方法中,定义一个集合对象,然后通过VS.Net的智能提示浏览它包含的方法:


  标示了“下箭头”的方法就是System.Linq命名空间下定义的扩展方法。如果移除了“using System.Linq;”这一行,也就看不到这些方法了:


 根据我自己的理解,Linq就是定义在System.Linq命名空间下的一组针对集合对象的扩展方法。

  我所说的“集合对象”是指继承了System.Collections.IEnumerable接口的类对象,最常见集合对象的包括Array(string[]和int[]等)和List等,当然还包括这里的主角System.Linq.Queryable。但本文并不讨论Queryable,因为它涉及Linq深一层次的工作原理,不在本文的讨论范围内,如果你对此感兴趣,可以查询网上相关资料。

几个最常用的Linq方法

Where

  与Sql中的where类似,Linq中的Where返回集合中符合条件的记录。

 C# Code 
1
2
3
4
int[] numbers = { 0123456789 };
var smallNumbers = numbers.Where(n => n < 
5);
foreach (int number in smallNumbers)
    System.Console.WriteLine(
"小于5的数字:" + number);

  以上代码筛选出集合里面小于5的元素。

关于以上代码的解释:

  在C#里,var关键字表示隐式声明类型,类型由“=”右面的方法返回的类型决定。跟java script不同,C#里的var关键字声明的变量实际上是有明确的类型的,只是在代码中省略不标明,并且在声明后不能更改他的类型。在可以不使用var的情况下,我建议还是写明变量的类型,以增强代码的可读性。例如上“var smallNumbers = numbers.Where(n => n < 5)”等同于“System.Collections.Generic.IEnumerable<int> smallNumbers = numbers.Where(n => n < 5)”

  “n => n < 5”是一个Lambda表达式,Lambda表达式在C#里面是匿名委托/delegate(初学者可以暂时理解为匿名方法)的简写形式,n是方法的参数,“n <
5”是返回值(“<”返回bool),实际上“n => n < 5”也可以用一个委托代替。如果你想了解更多,可以查阅网上关于C# Lambda表达式的相关资料。

Any

  Any判断集合内是否存在符合条件的元素。

 C# Code 
1
2
3
4
int[] numbers = { 0123456789 };
if (numbers.Any(n => n < 5))
{
}
OrderBy和OrderByDescending

  OrderBy和OrderByDescending这两个方法的功能和Sql中的order by x和order by x desc类似。如果根据多个字段排序,后面再接上ThenBy或ThenByDescending。

 C# Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class FullName
{
    
/// <summary>
    /// 姓
    /// </summary>
    public string FirstName;
 
    
/// <summary>
    /// 名
    /// </summary>
    public string LastName;
 
    
public FullName(string firstName, string lastName)
    {
        
this.FirstName = firstName;
        
this.LastName = lastName;
    }
}
 
public void OrderTest()
{
    FullName[] names = { 
new FullName("张""三"), new FullName("李""四") };
    var orderedNames = names.OrderBy(n => n.FirstName).ThenByDescending(n => n.LastName);
}
Contains

  Contains方法在查询数据库时,功能和Sql中的like语句类似,查询字段是否包含指定的字符串。

 C# Code 
1
2
string[] names = { "Terry""Andy""King" };
var yNames = names.Contains(
"y");

  另外,Contains方法在查询数据库时,也有和Sql中的in语句类似的功能。

 C# Code 
1
2
3
FullName[] fullNames = { new FullName("张""三"), new FullName("李""四") };
string[] firstNames = { "张""黄""何" };
var names = fullNames.Where(n => firstNames.Contains(n.FirstName));
Select

  Select返回该参数内返回的对象集合:

 C# Code 
1
2
FullName[] names = { new FullName("张""三"), new FullName("李""四") };
var orderedNames = names.Select(n => 
new { n.FirstName, n.LastName });

  以上代码在姓名的集合中,取出FirstName和LastName两个属性组成一个新的集合。

关于以上代码的解释:

  “new{}”在C#中的作用是定义一个匿名对象,匿名对象使用前是不需要声明的,所以声明匿名对象的变量,只通过var关键字。如果我们想在匿名对象中使用别名,可以使用“=”操作符,例如“new { n.FirstName, n.LastName }”可以写成“new { FirstName =n.FirstName, LastName =n.LastName }”。针对这段代码,如果我们想把整个FullName对象取出来,可以把“n => new { n.FirstName, n.LastName }”写成“n => n”

Count

  与Sql中的count类似,Linq中的Count返回集合中的元素个数:

 C# Code 
1
2
int[] numbers = { 0123456789 };
int totalCount = numbers.Count();

Count方法也可以带条件参数:

 C# Code 
1
2
int[] numbers = { 0123456789 };
int smallNumberCount = numbers.Count(n => n < 5);

  在Linq中,Count方法常用于分页,告诉分页控件一共有多少条记录。

Skip和Take

  Skip方法的功能是跳过集合里指定数量个数的元素,取往后的元素。

  Take方法的功能是取集合里指定个数的元素。

  这两个方法在Linq里的最主要作用是用作分页:

 C# Code 
1
2
3
4
int[] numbers = { 0123456789 };
int pageSize = 3;
int pageIndxe = 1;
var secondPageNumbers = numbers.Skip(pageSize *pageIndxe).Take(pageSize);
First

  Frist返回集合内符合条件的第一个元素,在数据库操作中,常用于根据主键返回对象:

 C# Code 
1
Parent parent = parents.First(p = > p.ParentId == 1);
FirstOrDefault

  FirstOrDefault的功能和First一样,但First在集合内没有符合条件的元素的时候会抛出异常,而FirstOrDefault在这种情况下返回null。

C#在语法级别上对Linq的支持

  C#专门为Linq制定了一些的关键字来使Linq的表达方式更加优雅:

 C# Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/// <summary>
/// 父母
/// </summary>
public class Parent
{
    
/// <summary>
    /// 编号
    /// </summary>
    public int ParentId;
 
    
/// <summary>
    /// 姓名
    /// </summary>
    public string ParentName;
 
    
public Parent(int id, string name)
    {
        
this.ParentId = id;
        
this.ParentName = name;
    }
}
 
/// <summary>
/// 子女
/// </summary>
public class Child
{
    
/// <summary>
    /// 子女编号
    /// </summary>
    public int ChildId;
 
    
/// <summary>
    /// 父母编号
    /// </summary>
    public int ParentId;
 
    
/// <summary>
    /// 姓名
    /// </summary>
    public string ChildName;
 
    
public Child(int childId, int parentId, string name)
    {
        
this.ChildId = childId;
        
this.ParentId = parentId;
        
this.ChildName = name;
    }
}
 
Parent[] parents = { 
new Parent(1"张三"), new Parent(2"李四") };
Child[] children = { 
new Child(11"小红"), new Child(22"小明") };
 
var people1 = from child 
in children
              join parent 
in parents on child.ParentId equals parent.ParentId
              where child.ParentId > 
0 && child.ParentId < 100
              orderby child.ParentId, child.ChildId
              select 
new
              {
                  child.ChildName, parent.ParentName
              };
//等同于
var people2 = children.Where(c => c.ParentId > 0 && c.ParentId < 100)
              .OrderBy(c => c.ParentId)
              .ThenBy(c => c.ChildId)
              .Join(parents, c => c.ParentId, p => p.ParentId, (c, p) => 
new { c.ChildName, p.ParentName });

  从以上代码可以看出,使用关键字的查询语句比使用函数的表达方式更简洁和优雅,但使用函数式在多行代码拼凑组合时比较灵活。我自己觉得美中不足的是,很多Linq方法都没有制定成关键字,例如Count、Skip和Take等。

其他查询句法

  文本介绍的查询句法,在大多数场合中已经够用。但某些场合,我们会用到另外一些查询句法(我想特别是聚合查询),如果你对此感兴趣,可以查阅网上的相关资料,否则可以在用到的时候再去查:

http://www.cnblogs.com/zoupeiyang/archive/2011/06/25/2090004.html

Linq的缺陷

  我自己觉得Linq的缺陷主要在于Sql中的right outer join和full outer join在Linq里面并没有对应的查询方法,我不知道其他人或者微软有没有把这些看成缺陷,有没有打算在将来添加这两个功能。

善欲人見的動機

  右手行善莫讓左手知曉,新約聖經如是說。

  中國人向來講究施恩不求回報,為善不欲人知。朱子治家格言:善欲人見,不是真善;惡恐人知,便是大惡。

  陳光標傾囊助窮,贏得身前身後名,既收獲了風光美名,也種上了偽善惡名。台灣的星雲法師便看破其中的玄機,派人送金剛經給陳光標,其中之喻,就是勸陳光標不僅要看破利之所誘,更要看透名之所惑,真正做到無欲則剛。星雲法師贈經之舉,一切盡在不言中,求名心切的陳光標是否能頓悟其中真意呢?

  陳光標高調行善,島內不少民眾跪求紅包。為了錢,不少台灣人甚麼事都做得出來,不吃嗟來之食的古訓早已拋之一邊。陳光標所到之處,簡直就像「中標」現象,跪的跪,倒的倒,哭的哭,喊的喊,甚至有些青年男子不顧「男兒膝下有千金」的尊嚴,在街頭攔跪,還有一名婦人居然為流浪犬跪求小車一輛,這些場面看得讓人觸目驚心。

  陳光標今次行善騷,成就了陳光標一人之名,卻讓中華民族付出了巨大代價,更讓外國人看盡笑話。有錢能使鬼推磨,中國人在錢面前永遠站不起來,挺直不了腰桿。

最知名的日本人物

  據了解,目前在中國最知名的日本人物,既不是菅直人,也不是小泉純一郎,而是AV女優蒼井空和鈴木杏里。前者去年在中國一家網站開通微博,短短十四個小時內,便受到十九萬五千五百九十九位中國粉絲追捧。期間她僅僅發布了一條禮節性的微博,卻獲得近四萬五千次轉發和近三萬條評論。

  蒼井小姐曾多次來華演出,均引起巨大轟動,堪稱高潮迭起,很多人尊稱她「蒼老師」。雖然她僅二十七歲芳齡,但在人體動作技術方面確實達到了爐火純青的境界,許多男孩子都是因為欣賞她的表演才變成男人的;成年人同樣感到大開眼界,並嘆息以前都是白活。去年四月,蒼井空在微博上發起為玉樹地震賑災募捐後,被網友冠上「德藝雙馨」的牌匾,稱讚她「不僅有美麗的身體,還有美麗的心靈」。據說她正在灌錄一首中文歌曲《毛衣》,準備獻給最最最愛她的中國人。

  而鈴木杏里亦與中國有深厚的友誼,她上大學時從事歷史專業學習,畢業論文為《日本侵略中國》。她自我表白稱,自己是能夠正視日本侵略中國的人,所以日常生活裏經常免費和中國留學生做愛。「我總覺得這樣子是替過去的侵華做一種心理上的補償,不過坦白說,中國留學生比起日本人來也比較溫柔,可以讓我很舒服!」鈴木杏里親華情結,使其在中國人氣極旺。

Firework提示“无法运行脚本,这个命令需要一个活动文档。”的解决办法

  首先引发这个问题的原因我自己也不是很清楚,问题的症状是Firework有时候打开一个图片的时候,工具栏所有按钮都会变成不可用,做某些操作(例如保存)的时候会弹出提示“无法运行脚本,这个命令需要一个活动文档。”错误提示框。

  这个问题的解决方法也很简单。首先对打开的图片做一个可引起撤销的操作(例如移动图层),然后关闭图片,最后在弹出的退出确认框里面点击“取消”按钮。这样工具栏变回转会可用,所有操作恢复正常。