第四章 Websharp ORMWord文件下载.docx
《第四章 Websharp ORMWord文件下载.docx》由会员分享,可在线阅读,更多相关《第四章 Websharp ORMWord文件下载.docx(14页珍藏版)》请在冰豆网上搜索。
元数据是抽象概念,具有上下文,在开发环境中有多种用途。
元数据是抽象概念
当人们描述现实世界的现象时,就会产生抽象信息,这些抽象信息便可以看作是元数据。
例如,在描述风、雨和阳光这些自然现象时,就需要使用"
天气"
这类抽象概念。
还可以通过定义温度、降水量和湿度等概念对天气作进一步的抽象概括。
在数据设计过程中,也使用抽象术语描述现实世界的各种现象。
人们把人物、地点、事物和数字组织或指定为职员、顾客或产品数据。
在软件设计过程中,代表数据或存储数据的应用程序和数据库结构可以概括为开发和设计人员能够理解的元数据分类方案。
表或表单由对象派生出来,而对象又由类派生。
在元数据中有多个抽象概念级别。
可以描述一个数据实例,然后对该描述本身进行描述,接着再对后一个描述进行描述,这样不断重复,直到达到某个实际限度而无法继续描述为止。
通常情况下,软件开发中使用的元数据描述可扩展为二至三级的抽象概念。
比如"
loantable"
数据实例可以描述为数据库表名。
数据库表又可以描述为数据库表对象。
最后,数据库表对象可以用一个抽象类描述,该抽象类确定所有派生对象都必须符合的固定特征集合。
元数据具有上下文
人们通常把数据和元数据的区别称为类型/实例区别。
模型设计人员表述的是类型(如各种类或关系),而软件开发人员表述的是实例(如Table类或TableHasColumns关系)。
实例和类型的区别是上下文相关的。
在一个方案中的元数据将在另一个方案中变为数据。
例如,在典型的关系型DBMS中,系统目录将描述包含数据的表和列。
这就意味着系统目录描述数据定义,因而可以认为其中的数据是元数据。
但只要使用正确的软件工具,仍然可以象操作其它数据一样对这些元数据进行操作。
操作元数据的示例包括:
查看数据沿袭或表的版本控制信息,或通过搜索具有货币数据类型的列来识别所有表示财务数据的表。
在此方案中,如系统目录这样的标准元数据变为可操作的数据。
元数据有多种用途
可以像使用任何类型的应用程序或数据设计元素一样使用元数据类型和实例信息。
将设计信息表达为元数据,特别是标准元数据,可以为再次使用、共享和多工具支持提供更多的可能性。
例如,将数据对象定义为元数据使您得以看到它们是如何构造和进行版本控制的。
版本控制支持提供一种查看、衍生或检索任何特定DTS包或数据仓库定义的历史版本的方法。
开发基于元数据的代码时,可以一次性定义结构,然后重复使用该结构创建可作为特定工具和应用程序的不同版本的多个实例。
还可以在现有元数据类型之间创建新关系,以支持新的应用程序设计。
.Net下的元数据
在.Net环境下,按照微软的说明,元数据是一种二进制信息,用以对存储在公共语言运行库可移植可执行文件(PE)文件或存储在内存中的程序进行描述。
我们将代码编译为PE文件时,便会将元数据插入到该文件的一部分中,而将代码转换为Microsoft中间语言(MSIL)并将其插入到该文件的另一部分中。
在模块或程序集中定义和引用的每个类型和成员都将在元数据中进行说明。
当执行代码时,运行库将元数据加载到内存中,并引用它来发现有关代码的类、成员、继承等信息。
元数据以非特定语言的方式描述在代码中定义的每一类型和成员。
元数据存储以下信息:
∙程序集的说明。
o标识(名称、版本、区域性、公钥)。
o导出的类型。
o该程序集所依赖的其他程序集。
o运行所需的安全权限。
∙类型的说明。
o名称、可见性、基类和实现的接口。
o成员(方法、字段、属性、事件、嵌套的类型)。
∙特性。
o修饰类型和成员的其他说明性元素。
对于一种更简单的编程模型来说,元数据是关键,该模型不再需要接口定义语言(IDL)文件、头文件或任何外部组件引用方法。
元数据允许.NET语言自动以非特定语言的方式对其自身进行描述,而这是开发人员和用户都无法看见的。
另外,通过使用属性,可以对元数据进行扩展。
元数据具有以下主要优点:
自描述文件。
公共语言运行库模块和程序集是自描述的。
模块的元数据包含与另一个模块进行交互所需的全部信息。
元数据自动提供COM中IDL的功能,允许将一个文件同时用于定义和实现。
运行库模块和程序集甚至不需要向操作系统注册。
结果,运行库使用的说明始终反映编译文件中的实际代码,从而提高应用程序的可靠性。
语言互用性和更简单的基于组件的设计。
元数据提供所有必需的有关已编译代码的信息,以供您从用不同语言编写的PE文件中继承类。
您可以创建用任何托管语言(任何面向公共语言运行库的语言)编写的任何类的实例,而不用担心显式封送处理或使用自定义的互用代码。
特性。
.NETFramework允许我们在编译文件中声明特定种类的元数据(称为特性Attribute),来批注编程元素,如类型、字段、方法和属性。
在整个.NETFramework中到处都可以发现特性的存在,特性用于更精确地控制运行时您的程序如何工作。
另外,可以通过用户定义的自定义特性向.NETFramework文件发出自己的自定义元数据。
为运行库编译代码时,特性代码被转换为Microsoft中间语言(MSIL),并同编译器生成的元数据一起被放到可移植可执行(PE)文件的内部。
特性使我们得以向元数据中放置额外的描述性信息,并可使用运行库反射服务提取该信息。
.NETFramework出于多种原因使用特性并通过它们解决若干问题。
特性可以描述如何序列化数据,指定用于强制安全性的特性,以及限制实时(JIT)编译器的优化以使代码易于调试。
特性还可以记录文件名或代码作者,或在窗体开发阶段控制控件和成员的可见性。
可使用特性以几乎所有可能的方式描述代码,并以富有创造性的新方式影响运行库行为。
在.Net中,特性是从System.Attribute派生的特殊的类的。
3.特性和Websharp的ORM
在Websharp中,对象/关系映射通过Attribute来进行。
Websharp定义了以下用来完成映射的Attribute:
TableMapAttribute
ColumnMapAttribute
ReferenceObjectAttribute
XmlFileMapAttribute
WebsharpEntityIncludeAttribute
TableMapAttribute指明相关实体类所对应的数据库表,包含两个主要属性:
TableName和PrimaryKeys。
TableName指明表名,PrimaryKeys指明相关的关键字。
其定义如下:
[AttributeUsage(AttributeTargets.Class)]
publicclassTableMapAttribute:
Attribute
{
privatestringtableName;
privatestring[]primaryKeys;
publicTableMapAttribute(stringtableName,paramsstring[]primaryKeys)
this.tableName=tableName;
this.primaryKeys=primaryKeys;
}
publicstringTableName
get{returntableName;
}
set{tableName=value;
publicstring[]PrimaryKeys
get{returnprimaryKeys;
set{primaryKeys=value;
ColumnMapAttribute指明实体类的某个属性相对应的数据库字段,包含以下主要属性:
ColumnName:
相关的数据库字段名
DbType:
相关的数据库字段的数据类型
DefaultValue:
相关的数据库字段的默认值。
其代码这里就不列出了,请参见源代码。
ReferenceObjectAttribute的作用是,说明实体类的该属性引用了另外一个实体类,应该将该属性映射成另外一个实体类,而不是某个数据库字段。
XmlFileMapAttribute说明该实体类同数据库的映射通过XML文件来进行。
这是Websharp支持的另外一种O/R映射的方法。
WebsharpEntityIncludeAttribute用于实体类在不同的层之间序列化传递。
当使用Webservice来传递某个实体类的时候,必须给该实体类加上该特性。
4.动态代码生成及编译
使用Websharp开发系统,开发人员只需要对实体类定义一个抽象类,然后调用EntityManager的CreateObject方法,Websharp会在运行时刻动态为这个抽象类生成一个实现类,如前面的章节所演示的。
其中用到的技术就是动态代码生成。
在.Net中,这个工作是通过CodeDom来完成的。
4.1.关于CodeDom
.NET推崇这样一种思想:
相对于框架而言,语言处于从属、次要的地位。
CodeDom名称空间中包含的类是这一思想的集中体现。
.NETFramework中包含一个名为“代码文档对象模型”(CodeDOM)的机制,该机制使编写源代码的程序的开发人员可以在运行时,根据表示所呈现代码的单一模型,用多种编程语言生成源代码。
为表示源代码,CodeDOM元素相互链接以形成一个数据结构(称为CodeDOM图),它以某种源代码的结构为模型。
System.CodeDom命名空间定义可以表示源代码的逻辑结构(与具体的编程语言无关)的类型。
System.CodeDom.Compiler命名空间定义从CodeDOM图生成源代码的类型,和在受支持的语言中管理源代码编译的类型。
编译器供应商或开发人员可以扩展受支持语言的集合。
当程序需要用多种语言为程序模型或者为不确定的目标语言生成源代码时,与语言无关的源代码建模很有价值。
例如,如果语言的CodeDOM支持可用,则一些设计器将CodeDOM用作语言抽象接口,以用正确的编程语言生成源代码。
.NETFramework中包含C#、JScript和VisualBasic的代码生成器和代码编译器。
在Websharp中使用的是C#的代码生成器和代码编译器。
使用CodeDom生成类,其一般的操作是:
声明编译单元:
CodeCompileUnitcompileUnit=newCodeCompileUnit();
初始化名称空间:
CodeNameSpaceCurrentNameSpace=newCodeNamespace(Name);
创建类:
CodeTypeDeclarationctd=newCodeTypeDeclaration(Name);
创建方法:
CodeEntryPointMethodmethod=newCodeEntryPointMethod();
装配出树结构:
compileUnit.Namespaces.Add(CurrentNameSpace);
CurrentNameSpace.Types.Add(ctd);
ctd.Members.Add(mtd);
编译代码:
CodeDomProviderprovider=newCSharpCodeProvider();
ICodeCompilercompiler=provider.CreateCompiler();
CompilerResultsresults=compiler.CompileAssemblyFromDom(cp,compileUnit);
AssemblycreatedAssembly=results.CompiledAssembly;
4.2.Websharp的实现
代码实现在EntityClassGenerator类中。
首先,初始化代码生成需要的环境,包括CompileUnit、NameSpace等:
compileUnit=newCodeCompileUnit();
tempdAssemblyNameSpace=newCodeNamespace(EntityClassGenerator.TempEntityImpNamespace);
tempdAssemblyNameSpace.Imports.Add(newCodeNamespaceImport("
System.Data"
));
className="
_Impl_"
+entityType.Name;
referencedAssemblies=newStringCollection();
referencedAssemblies.Add("
System.dll"
);
System.Data.dll"
System.XML.dll"
referencedAssemblies.Add(entityType.Assembly.Location);
然后,申明要生成的类:
CodeTypeDeclarationgenerateClass=CreateClass();
tempdAssemblyNameSpace.Types.Add(generateClass);
privateCodeTypeDeclarationCreateClass()
{
CodeTypeDeclarationctd=newCodeTypeDeclaration(className);
ctd.IsClass=true;
ctd.Attributes=MemberAttributes.Public|MemberAttributes.Final;
ctd.BaseTypes.Add(entityTypeInterface);
ctd.CustomAttributes.Add(newCodeAttributeDeclaration("
Serializable"
……
然后,按照要生成的类的每个属性,生成类的各个属性:
foreach(PropertyInfopInfoinentityTypeInterface.GetProperties())
if(entityTypeInterface.IsInterface||(entityTypeInterface.IsAbstract&
&
pInfo.GetGetMethod().IsAbstract))
CodeMemberPropertyproperty=CreateProperty(pInfo,ctd);
if(property!
=null)
ctd.Members.Add(property);
更多具体的实现代码,请参照源代码。
因类的生成和编译是一项比较耗费资源的工作,因此,有必要在类生成以后,将其进行缓存,这样,在第二次需要创建相应的对象的时候,只需要直接从已生成的类中创建。
这个工作是通过EntityManager来完成的。
在实际应用中,我们通常通过如下方式来获取某的实体对象:
Schduleschdule=EntityManager.CreateObject(typeof(Schdule))asSchdule;
其中,EntityManager.CreateObject方法的实现如下:
publicstaticobjectCreateObject(TypeentityType)
if(!
HasCachedEntityType(entityType.FullName))
EntityClassGeneratorgen=newEntityClassGenerator();
Typet=gen.GenerateEntityType(entityType);
AddCachedEntityType(entityType.FullName,t);
returnActivator.CreateInstance(GetCachedEntityType(entityType.FullName));
可以看到,其中使用了缓存处理。
在这里,缓存处理的机制很简单,使用了一个静态的HashTable来保存这些缓存信息。
5.同数据库的交互
当需要同数据库进行交互,例如,需要将一个新的对象保存到数据库中时,通过PersistenceManager接口来实现。
PersistenceManager是对象同数据库交互的主要接口,其基本使用方法在前面已经给出,下面是他的定义:
publicinterfacePersistenceManager:
IDisposable
voidClose();
boolIsClosed{get;
TransactionCurrentTransaction{
get;
boolIgnoreCache{get;
set;
voidPersistNewObject(EntityDataentity);
voidPersistNewObject(PersistenceCapablepc);
voidUpdateObject(EntityDataentity);
voidUpdateObject(PersistenceCapablepc);
voidUpdateObject(EntityDataentity,paramsstring[]properties);
voidUpdateObject(PersistenceCapablepc,paramsstring[]properties);
voidDeleteObject(EntityDataentity);
voidDeleteObject(PersistenceCapablepc);
voidReload(EntityDataentity);
voidReload(PersistenceCapablepc);
voidEvict(objectpc);
voidEvictAll(object[]pcs);
voidEvictAll(ICollectionpcs);
voidEvictAll();
EntityDataFindEntityDataByPrimaryKey(objectid,stringentityTypeName);
EntityDataFindEntityDataByPrimaryKey(objectid,EntityDataentity);
PersistenceCapableFindObjectByPrimaryKey(objectid,PersistenceCapablepc);
PersistenceCapableFindObjectByPrimaryKey(objectid,TypeentityType);
QueryNewQuery();
QueryNewQuery(stringentityTypeName);
QueryNewQuery(stringentityTypeName,stringfilter);
QueryNewQuery(stringentityTypeName,stringfilter,QueryParameterCollectionparamColletion);
QueryNewQuery(TypeentityType);
QueryNewQuery(TypeentityType,stringfilter);
QueryNewQuery(TypeentityType,stringfilter,QueryParameterCollectionparamColletio