为 ADO.NET 数据服务在客户端生成自己的代理






4.75/5 (3投票s)
ADO.NET 数据生成的代理
引言
首先,请原谅我蹩脚的英语。 :)
本文讨论了为满足自身需求而创建实体代理类。
问题
从 ADO.NET 数据服务生成的代理类很简单,仅能满足标准的 CRUD 场景。但一旦您想实现更复杂或特殊的功能,就会发现一些限制。如果您需要此代理中不存在的某些功能,可以通过添加一个新的部分类(partial class)到您的项目中或使用反射来实现。您可以轻松地做到这一点,以添加诸如 `static` 方法或简单的实用方法之类的功能。但如果您必须实现某个在所有实体中共享的功能,或者某个依赖于实体语义的功能,则无法使用此方法。
您需要使用自动生成代理类,该类包含您需要的功能。这就是我们将要做的:创建一套新的部分类来完成代理现有的部分类,以满足我们的需求,或者创建整套代理类。
我们的解决方案
为了生成我们自己的类,我们将不得不从 Web 数据服务读取元数据。
生成过程将使用 Microsoft 的 `msxsl.exe` 工具:它从 XML 文件和 XSLT 文件生成结果。 “结果”将是我们的代理类,“XML”将是 Web 数据服务的 $metadata 结果,“XSLT”是我们在此处需要完成的工作。
创建数据库
这是一个非常简单的步骤。Microsoft 提供了一些 T-Sql 脚本来生成示例数据库。您可以在此处找到它。运行脚本以创建您自己的数据库。
解决方案
我们在此不是为了学习如何创建这些项目。所以只需下载本文附带的 zip 文件即可获得整个解决方案。您将在其中找到
- 一个带有 ADO.NET 数据服务的 Web 项目
- 一个带有 edmx 的实体项目
- 一个客户端项目
在解决方案目录中,我添加了一个 `Tools` 目录,您可以在其中找到 `msxsl.exe` 工具
在启动服务之前,我们必须在 edmx 和数据库之间建立一个链接。
在 `web.config` 文件中,更改连接字符串的末尾,以连接到 school 数据库
<add name="SchoolEntities"
connectionString="metadata=res://*/SchoolModel.csdl|res://*/
SchoolModel.ssdl|res://*/SchoolModel.msl;
provider=System.Data.SqlClient;provider
connection string="Data Source=localhost\sqlexpress;
Initial Catalog=School;Integrated Security=True;MultipleActiveResultSets=True""
providerName="System.Data.EntityClient" />
通过在解决方案资源管理器中右键单击 `WebDataService.svc` 文件并选择“在浏览器中打开”来启动 Web 数据服务。您将看到类似这样的内容

我们的服务已准备就绪。
生成需要元数据(XML 部分)
我们需要一个包含关于 school 模型实体及其关系的全部信息的 XML。只需将 `$metadata` 关键字添加到 Web 数据服务 URL 的末尾,如下所示

现在您可以看到 school 模型的完整描述
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<edmx:Edmx>
<edmx:DataServices>
<Schema>
<EntityType Name="Course">
<Key>
<PropertyRef Name="CourseID" />
</Key>
<Property Name="CourseID" Type="Edm.Int32" Nullable="false" />
<Property Name="Title" Type="Edm.String" Nullable="false"
MaxLength="100" Unicode="true" FixedLength="false" />
<Property Name="Credits" Type="Edm.Int32" Nullable="false" />
<NavigationProperty Name="Department"
Relationship="SchoolModel.FK_Course_Department"
FromRole="Course" ToRole="Department" />
<NavigationProperty Name="CourseGrade"
Relationship="SchoolModel.FK_CourseGrade_Course"
FromRole="Course" ToRole="CourseGrade" />
<NavigationProperty Name="OnlineCourse"
Relationship="SchoolModel.FK_OnlineCourse_Course"
FromRole="Course" ToRole="OnlineCourse" />
<NavigationProperty Name="OnsiteCourse"
Relationship="SchoolModel.FK_OnsiteCourse_Course"
FromRole="Course" ToRole="OnsiteCourse" />
<NavigationProperty Name="Person"
Relationship="SchoolModel.CourseInstructor"
FromRole="Course" ToRole="Person" />
</EntityType>
<EntityType Name="CourseGrade">
<Key>
<PropertyRef Name="EnrollmentID" />
</Key>
<Property Name="EnrollmentID" Type="Edm.Int32" Nullable="false" />
<Property Name="Grade" Type="Edm.Decimal"
`Nullable="true" Precision="3" Scale="2" />
<NavigationProperty Name="Course"
Relationship="SchoolModel.FK_CourseGrade_Course"
FromRole="CourseGrade" ToRole="Course" />
<NavigationProperty Name="Person"
Relationship="SchoolModel.FK_CourseGrade_Student"
FromRole="CourseGrade" ToRole="Person" />
</EntityType>
<EntityType Name="Department">
<Key>
<PropertyRef Name="DepartmentID" />
</Key>
<Property Name="DepartmentID" Type="Edm.Int32" Nullable="false" />
<Property Name="Name" Type="Edm.String" Nullable="false"
MaxLength="50" Unicode="true" FixedLength="false" />
<Property Name="Budget" Type="Edm.Decimal"
Nullable="false" Precision="19" Scale="4" />
<Property Name="StartDate" Type="Edm.DateTime" Nullable="false" />
<Property Name="Administrator" Type="Edm.Int32" Nullable="true" />
<NavigationProperty Name="Course"
Relationship="SchoolModel.FK_Course_Department"
FromRole="Department" ToRole="Course" />
</EntityType>
<EntityType Name="OfficeAssignment">
<Key>
<PropertyRef Name="InstructorID" />
</Key>
<Property Name="InstructorID" Type="Edm.Int32" Nullable="false" />
<Property Name="Location" Type="Edm.String" Nullable="false"
MaxLength="50" Unicode="true" FixedLength="false" />
<Property Name="Timestamp" Type="Edm.Binary" Nullable="false"
MaxLength="8" FixedLength="true" />
<NavigationProperty Name="Person"
Relationship="SchoolModel.FK_OfficeAssignment_Person"
FromRole="OfficeAssignment" ToRole="Person" />
</EntityType>
<EntityType Name="OnlineCourse">
<Key>
<PropertyRef Name="CourseID" />
</Key>
<Property Name="CourseID" Type="Edm.Int32" Nullable="false" />
<Property Name="URL" Type="Edm.String" Nullable="false"
MaxLength="100" Unicode="true" FixedLength="false" />
<NavigationProperty Name="Course"
Relationship="SchoolModel.FK_OnlineCourse_Course"
FromRole="OnlineCourse" ToRole="Course" />
</EntityType>
<EntityType Name="OnsiteCourse">
<Key>
<PropertyRef Name="CourseID" />
</Key>
<Property Name="CourseID" Type="Edm.Int32" Nullable="false" />
<Property Name="Location" Type="Edm.String" Nullable="false"
MaxLength="50" Unicode="true" FixedLength="false" />
<Property Name="Days" Type="Edm.String" Nullable="false"
MaxLength="50" Unicode="true" FixedLength="false" />
<Property Name="Time" Type="Edm.DateTime" Nullable="false" />
<NavigationProperty Name="Course"
Relationship="SchoolModel.FK_OnsiteCourse_Course"
FromRole="OnsiteCourse" ToRole="Course" />
</EntityType>
<EntityType Name="Person">
<Key>
<PropertyRef Name="PersonID" />
</Key>
<Property Name="PersonID" Type="Edm.Int32" Nullable="false" />
<Property Name="LastName" Type="Edm.String" Nullable="false"
MaxLength="50" Unicode="true" FixedLength="false" />
<Property Name="FirstName" Type="Edm.String" Nullable="false"
MaxLength="50" Unicode="true" FixedLength="false" />
<Property Name="HireDate" Type="Edm.DateTime" Nullable="true" />
<Property Name="EnrollmentDate" Type="Edm.DateTime" Nullable="true" />
<NavigationProperty Name="CourseGrade"
Relationship="SchoolModel.FK_CourseGrade_Student"
FromRole="Person" ToRole="CourseGrade" />
<NavigationProperty Name="OfficeAssignment"
Relationship="SchoolModel.FK_OfficeAssignment_Person" FromRole="Person"
ToRole="OfficeAssignment" />
<NavigationProperty Name="Course"
Relationship="SchoolModel.CourseInstructor" FromRole="Person" ToRole="Course" />
</EntityType>
<Association Name="FK_Course_Department">
<End Role="Department" Type="SchoolModel.Department" Multiplicity="1" />
<End Role="Course" Type="SchoolModel.Course" Multiplicity="*" />
</Association>
<Association Name="FK_CourseGrade_Course">
<End Role="Course" Type="SchoolModel.Course" Multiplicity="1" />
<End Role="CourseGrade" Type="SchoolModel.CourseGrade" Multiplicity="*" />
</Association>
<Association Name="FK_OnlineCourse_Course">
<End Role="Course" Type="SchoolModel.Course" Multiplicity="1" />
<End Role="OnlineCourse" Type="SchoolModel.OnlineCourse" Multiplicity="0..1" />
<ReferentialConstraint>
<Principal Role="Course">
<PropertyRef Name="CourseID" />
</Principal>
<Dependent Role="OnlineCourse">
<PropertyRef Name="CourseID" />
</Dependent>
</ReferentialConstraint>
</Association>
<Association Name="FK_OnsiteCourse_Course">
<End Role="Course" Type="SchoolModel.Course" Multiplicity="1" />
<End Role="OnsiteCourse" Type="SchoolModel.OnsiteCourse" Multiplicity="0..1" />
<ReferentialConstraint>
<Principal Role="Course">
<PropertyRef Name="CourseID" />
</Principal>
<Dependent Role="OnsiteCourse">
<PropertyRef Name="CourseID" />
</Dependent>
</ReferentialConstraint>
</Association>
<Association Name="FK_CourseGrade_Student">
<End Role="Person" Type="SchoolModel.Person" Multiplicity="1" />
<End Role="CourseGrade" Type="SchoolModel.CourseGrade" Multiplicity="*" />
</Association>
<Association Name="FK_OfficeAssignment_Person">
<End Role="Person" Type="SchoolModel.Person" Multiplicity="1" />
<End Role="OfficeAssignment" Type="SchoolModel.OfficeAssignment" Multiplicity="0..1" />
<ReferentialConstraint>
<Principal Role="Person">
<PropertyRef Name="PersonID" />
</Principal>
<Dependent Role="OfficeAssignment">
<PropertyRef Name="InstructorID" />
</Dependent>
</ReferentialConstraint>
</Association>
<Association Name="CourseInstructor">
<End Role="Course" Type="SchoolModel.Course" Multiplicity="*" />
<End Role="Person" Type="SchoolModel.Person" Multiplicity="*" />
</Association>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
代理类将从此 XML 生成。
XSLT 文件
XSLT 文件将生成实体类代理。这是 XSLT 文件的内容
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet>
<xsl:output method="text"/>
<!-- ROOT OF DOCUMENT -->
<xsl:template match="/edmx:Edmx">
<xsl:apply-templates select="edmx:DataServices" />
<xsl:apply-templates select="edmx:Queries">
</xsl:apply-templates>
</xsl:template>
<!--SERVICES-->
<xsl:template match="edmx:DataServices">
<xsl:text>[assembly:
global::System.Data.Objects.DataClasses.EdmSchemaAttribute()] </xsl:text>
<xsl:apply-templates select="schema:Schema">
<xsl:sort select="@Namespace"/>
</xsl:apply-templates>
</xsl:template>
<!-- NAMESPACE -->
<xsl:template match="schema:Schema">
<xsl:apply-templates select="schema:EntityContainer" mode="AssemblyMetadatas"/>
<xsl:text>
</xsl:text>
<xsl:text>namespace </xsl:text>
<xsl:value-of select="@Namespace"/>
<xsl:text>
{</xsl:text>
<xsl:apply-templates select="schema:EntityContainer" mode="EntitiesContext">
<xsl:with-param name="NamespaceName" select="@Namespace"></xsl:with-param>
</xsl:apply-templates>
<xsl:apply-templates select="schema:EntityType">
<xsl:sort select="@Name"/>
</xsl:apply-templates>
<xsl:text> } </xsl:text>
</xsl:template>
<!--CONTEXT-->
<xsl:template match="schema:EntityContainer" mode="AssemblyMetadatas">
<xsl:apply-templates select="schema:AssociationSet">
<xsl:sort select="@Name"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="schema:EntityContainer" mode="EntitiesContext">
<xsl:param name="NamespaceName" select="NamespaceName"></xsl:param>
<xsl:text> 	public partial class </xsl:text>
<xsl:value-of select="@Name"></xsl:value-of>
<xsl:text> : global::System.Data.Services.Client.DataServiceContext </xsl:text>
<xsl:text>	{ </xsl:text>
<xsl:text> 		public </xsl:text>
<xsl:value-of select="@Name"></xsl:value-of>
<xsl:text>(global::System.Uri serviceRoot) : base(serviceRoot) </xsl:text>
<xsl:text>		{ </xsl:text>
<xsl:text>			this.OnContextCreated(); </xsl:text>
<xsl:text>		} </xsl:text>
<xsl:text>		partial void OnContextCreated(); </xsl:text>
<xsl:apply-templates select="schema:EntitySet" mode="Properties">
<xsl:sort select="@Name"/>
<xsl:with-param name="NamespaceName" select="$NamespaceName"></xsl:with-param>
</xsl:apply-templates>
<xsl:apply-templates select="schema:EntitySet" mode="Add">
<xsl:sort select="@Name"/>
<xsl:with-param name="NamespaceName" select="$NamespaceName"></xsl:with-param>
</xsl:apply-templates>
<xsl:text> 	} </xsl:text>
</xsl:template>
<!--ENTITIES QUERIES-->
<xsl:template match="schema:EntitySet" mode="Properties">
<xsl:param name="NamespaceName" select="NamespaceName"></xsl:param>
<xsl:text> 		
public System.Data.Services.Client.DataServiceQuery<</xsl:text>
<xsl:value-of select="substring-after(@EntityType, concat($NamespaceName, '.'))"/>
<xsl:text>> </xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text> 		{ </xsl:text>
<xsl:text>			get </xsl:text>
<xsl:text>			{ </xsl:text>
<xsl:text>				if (this._</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text> == null) </xsl:text>
<xsl:text>				{ </xsl:text>
<xsl:text>					this._</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text> = base.CreateQuery<</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>>("[</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>]"); </xsl:text>
<xsl:text>				} </xsl:text>
<xsl:text>				return this._</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>; </xsl:text>
<xsl:text>			} </xsl:text>
<xsl:text>		} </xsl:text>
<xsl:text>		private global::System.Data.Services.Client.DataServiceQuery<
</xsl:text>
<xsl:value-of select="substring-after(@EntityType, concat($NamespaceName, '.'))"/>
<xsl:text>> _</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>; </xsl:text>
</xsl:template>
<xsl:template match="schema:EntitySet" mode="Add">
<xsl:param name="NamespaceName" select="NamespaceName"></xsl:param>
<xsl:text> 		public void AddTo</xsl:text>
<xsl:value-of select="substring-after(@EntityType, concat($NamespaceName, '.'))"/>
<xsl:text>(</xsl:text>
<xsl:value-of select="substring-after(@EntityType, concat($NamespaceName, '.'))"/>
<xsl:text> @</xsl:text>
<xsl:value-of select="substring-after(@EntityType, concat($NamespaceName, '.'))"/>
<xsl:text>) </xsl:text>
<xsl:text>		{ </xsl:text>
<xsl:text>			base.AddObject("</xsl:text>
<xsl:value-of select="substring-after(@EntityType, concat($NamespaceName, '.'))"/>
<xsl:text> ", @</xsl:text>
<xsl:value-of select="substring-after(@EntityType, concat($NamespaceName, '.'))"/>
<xsl:text>); </xsl:text>
<xsl:text>		} </xsl:text>
</xsl:template>
<!--ASSEMBLY METADATAS-->
<xsl:template match="schema:AssociationSet">
<xsl:param name="NamespaceName" select="NamespaceName"></xsl:param>
<xsl:variable name="AssociationName" select="@Name"></xsl:variable>
<xsl:variable name="FirstRole" select="schema:End[1]/@Role"></xsl:variable>
<xsl:variable name="SecondRole" select="schema:End[2]/@Role"></xsl:variable>
<xsl:variable name="AssociationFirstType"
select="../../schema:Association[@Name=$AssociationName]/
schema:End[@Role=$FirstRole]/@Type"></xsl:variable>
<xsl:variable name="AssociationFirstMultiplicity"
select="../../schema:Association[@Name=$AssociationName]/
schema:End[@Role=$FirstRole]/@Multiplicity"></xsl:variable>
<xsl:variable name="AssociationSecondType"
select="../../schema:Association[@Name=$AssociationName]/
schema:End[@Role=$SecondRole]/@Type"></xsl:variable>
<xsl:variable name="AssociationSecondMultiplicity"
select="../../schema:Association[@Name=$AssociationName]/
schema:End[@Role=$SecondRole]/@Multiplicity"></xsl:variable>
<xsl:text>[assembly: global::System.Data.Objects.DataClasses.EdmRelationshipAttribute
("</xsl:text>
<xsl:value-of select="$NamespaceName"/>
<xsl:text>", "</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>", "</xsl:text>
<xsl:value-of select="$FirstRole"/>
<xsl:text>", global::System.Data.Metadata.Edm.RelationshipMultiplicity.</xsl:text>
<xsl:choose>
<xsl:when test="$AssociationFirstMultiplicity='*'">
<xsl:text>Many</xsl:text>
</xsl:when>
<xsl:when test="$AssociationFirstMultiplicity='1'">
<xsl:text>One</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>ZeroOrOne</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:text>, typeof(</xsl:text>
<xsl:value-of select="$AssociationFirstType"/>
<xsl:text>), "</xsl:text>
<xsl:value-of select="$SecondRole"/>
<xsl:text>", global::System.Data.Metadata.Edm.RelationshipMultiplicity.</xsl:text>
<xsl:choose>
<xsl:when test="$AssociationSecondMultiplicity='*'">
<xsl:text>Many</xsl:text>
</xsl:when>
<xsl:when test="$AssociationSecondMultiplicity='1'">
<xsl:text>One</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>ZeroOrOne</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:text>, typeof(</xsl:text>
<xsl:value-of select="$AssociationSecondType"/>
<xsl:text>))] </xsl:text>
</xsl:template>
<!--CLASSES-->
<xsl:template match="schema:EntityType">
<xsl:text> </xsl:text>
<xsl:text>	#region class </xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text> </xsl:text>
<xsl:text>	[global::System.Serializable()]</xsl:text>
<xsl:text>	[global::System.Data.Services.Common.DataServiceKeyAttribute(</xsl:text>
<xsl:for-each select="schema:Key/schema:PropertyRef">
<xsl:text>"</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>"</xsl:text>
</xsl:for-each>
<xsl:text>)] </xsl:text>
<xsl:text>	public partial class </xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text> : global::System.ComponentModel.INotifyPropertyChanged
	{ </xsl:text>
<!-- FIELDS -->
<xsl:text> 		#region Fields </xsl:text>
<xsl:apply-templates select="schema:Property" mode="FieldsDeclaration">
<xsl:sort select="@Name"/>
</xsl:apply-templates>
<xsl:apply-templates select="schema:NavigationProperty" mode="FieldsDeclaration">
<xsl:sort select="@Name"/>
</xsl:apply-templates>
<xsl:text> 		#endregion //Fields </xsl:text>
<!-- PROPERTIES -->
<xsl:text> 		#region Properties </xsl:text>
<xsl:apply-templates select="schema:Property" mode="PropertiesDeclaration">
<xsl:sort select="@Name"/>
</xsl:apply-templates>
<xsl:apply-templates select="schema:NavigationProperty" mode="PropertiesDeclaration">
<xsl:sort select="@Name"/>
</xsl:apply-templates>
<xsl:text> 		#endregion //Property </xsl:text>
<xsl:text> 		#region INotifyPropertyChanged Membres
		public event
System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
		protected virtual void OnPropertyChanged(string property)
		{
			if (this.PropertyChanged != null)
			{
				this.PropertyChanged
(this, new System.ComponentModel.PropertyChangedEventArgs(property));
			}
		}
		#endregion </xsl:text>
<xsl:text> 	} </xsl:text>
<xsl:text> 	#endregion //class </xsl:text>
</xsl:template>
<!--FIELDS-->
<xsl:template match="schema:Property" mode="FieldsDeclaration">
<xsl:text>		[global::System.Diagnostics.DebuggerBrowsable
(System.Diagnostics.DebuggerBrowsableState.Never)] </xsl:text>
<xsl:text>		private</xsl:text>
<xsl:choose>
<xsl:when test="(@Nullable='true') and @Type != 'Edm.String'">
<xsl:text> global::System.Nullable<global::System.</xsl:text>
<xsl:value-of select="substring-after(@Type, '.')"/>
<xsl:text>></xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text> global::System.</xsl:text>
<xsl:value-of select="substring-after(@Type, '.')"/>
</xsl:otherwise>
</xsl:choose>
<xsl:text> _</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>; </xsl:text>
</xsl:template>
<xsl:template match="schema:NavigationProperty" mode="FieldsDeclaration">
<xsl:text>		
[global::System.Diagnostics.DebuggerBrowsable
(System.Diagnostics.DebuggerBrowsableState.Never)] </xsl:text>
<xsl:text>		private </xsl:text>
<xsl:variable name="NamespaceName" select="../../@Namespace"></xsl:variable>
<xsl:variable name="RelationShipName" select="@Relationship"></xsl:variable>
<xsl:variable name="ValueName" select="@ToRole"></xsl:variable>
<xsl:choose>
<xsl:when test="$Multiplicity = '0..1'">
<xsl:value-of select="substring-after($Type, concat($NamespaceName, '.'))"/>
</xsl:when>
<xsl:when test="$Multiplicity = '1'">
<xsl:value-of select="substring-after($Type, concat($NamespaceName, '.'))"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>global::System.Collections.ObjectModel.Collection<</xsl:text>
<xsl:value-of select="substring-after($Type, concat($NamespaceName, '.'))"/>
<xsl:text>></xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:text> _</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:choose>
<xsl:when test="$Multiplicity = '*'">
<xsl:text> = new global::System.Collections.ObjectModel.Collection<</xsl:text>
<xsl:value-of select="substring-after($Type, concat($NamespaceName, '.'))"/>
<xsl:text>>(); </xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>; </xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--PROPERTY-->
<xsl:template match="schema:Property" mode="PropertiesDeclaration">
<xsl:text>		public </xsl:text>
<xsl:choose>
<xsl:when test="(@Nullable='true') and @Type != 'Edm.String'">
<xsl:text> global::System.Nullable<global::System.</xsl:text>
<xsl:value-of select="substring-after(@Type, '.')"/>
<xsl:text>></xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text> global::System.</xsl:text>
<xsl:value-of select="substring-after(@Type, '.')"/>
</xsl:otherwise>
</xsl:choose>
<xsl:text> </xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text> 		{</xsl:text>
<xsl:text> 			get</xsl:text>
<xsl:text> 			{</xsl:text>
<xsl:text> 				return this._</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>;</xsl:text>
<xsl:text> 			}</xsl:text>
<xsl:text> 			set</xsl:text>
<xsl:text> 			{</xsl:text>
<xsl:text> 				if (this._</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text> != value)</xsl:text>
<xsl:text> 				{</xsl:text>
<xsl:text> 					this._</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text> = value;</xsl:text>
<xsl:text> 					this.OnPropertyChanged("</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>");</xsl:text>
<xsl:text> 				}</xsl:text>
<xsl:text> 			}</xsl:text>
<xsl:text> 		} </xsl:text>
</xsl:template>
<xsl:template match="schema:NavigationProperty" mode="PropertiesDeclaration">
<xsl:text>		[global::System.Xml.Serialization.XmlIgnoreAttribute()]
		[global::System.Xml.Serialization.SoapIgnoreAttribute()]
		public </xsl:text>
<xsl:variable name="NamespaceName" select="../../@Namespace"></xsl:variable>
<xsl:variable name="RelationShipName" select="@Relationship"></xsl:variable>
<xsl:variable name="ValueName" select="@ToRole"></xsl:variable>
<xsl:choose>
<xsl:when test="$Multiplicity = '0..1'">
<xsl:value-of select="substring-after($Type, concat($NamespaceName, '.'))"/>
</xsl:when>
<xsl:when test="$Multiplicity = '1'">
<xsl:value-of select="substring-after($Type, concat($NamespaceName, '.'))"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>global::System.Collections.ObjectModel.Collection<</xsl:text>
<xsl:value-of select="substring-after($Type, concat($NamespaceName, '.'))"/>
<xsl:text>></xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:text> </xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text> 		{</xsl:text>
<xsl:text> 			get</xsl:text>
<xsl:text> 			{</xsl:text>
<xsl:text> 				return this._</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>;</xsl:text>
<xsl:text> 			}</xsl:text>
<xsl:text> 			set</xsl:text>
<xsl:text> 			{</xsl:text>
<xsl:text> 				if (this._</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text> != value)</xsl:text>
<xsl:text> 				{</xsl:text>
<xsl:text> 					this._</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text> = value;</xsl:text>
<xsl:text> 					this.OnPropertyChanged("</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>");</xsl:text>
<xsl:text> 				}</xsl:text>
<xsl:text> 			}</xsl:text>
<xsl:text> 		} </xsl:text>
</xsl:template>
<xsl:template match="edmx:Classes">
<xsl:text> 	public sealed class ClassNames { </xsl:text>
<xsl:for-each select="edmx:Class">
<xsl:text>		public const string </xsl:text>
<xsl:value-of select="@LogicalName" />
<xsl:text> = "</xsl:text>
<xsl:value-of select="@LogicalName" />
<xsl:text>"; </xsl:text>
</xsl:for-each>
<xsl:text>	} </xsl:text>
</xsl:template>
<xsl:template match="edmx:Class" mode="AttributesDeclaration">
<xsl:text>namespace ANPE.Annuaire.ReglesMetier.</xsl:text>
<xsl:value-of select="@LogicalName" />
<xsl:text>s { </xsl:text>
<xsl:text> 	public sealed class </xsl:text>
<xsl:value-of select="@LogicalName" />
<xsl:text>Attributes { </xsl:text>
<xsl:for-each select="edmx:Attributes/edmx:add">
<xsl:text>		///<summary> </xsl:text>
<xsl:value-of select="@LogicalName" />
<xsl:text></summary> </xsl:text>
<xsl:text>		public const string </xsl:text>
<xsl:value-of select="@Name" />
<xsl:text> = "</xsl:text>
<xsl:value-of select="@Name" />
<xsl:text>"; </xsl:text>
<xsl:if test="@MinLength">
<xsl:text>		public const int </xsl:text>
<xsl:value-of select="@Name" />
<xsl:text>_MinLength = </xsl:text>
<xsl:value-of select="@MinLength" />
<xsl:text>; </xsl:text>
</xsl:if>
<xsl:if test="@MaxLength">
<xsl:text>		public const int </xsl:text>
<xsl:value-of select="@Name" />
<xsl:text>_MaxLength = </xsl:text>
<xsl:value-of select="@MaxLength" />
<xsl:text>; </xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>	} </xsl:text>
<xsl:text>} </xsl:text>
</xsl:template>
<xsl:template match="edmx:Class" mode="ClassesDeclaration">
<xsl:text>namespace ANPE.Annuaire.ReglesMetier.</xsl:text>
<xsl:value-of select="@LogicalName" />
<xsl:text>s { </xsl:text>
<xsl:text> 	[Serializable]</xsl:text>
<xsl:text> 	[DirectoryClass(ClassNames.</xsl:text>
<xsl:value-of select="@LogicalName" />
<xsl:text>)]</xsl:text>
<xsl:text> 	public partial class </xsl:text>
<xsl:value-of select="@LogicalName" />
<xsl:text> : System.ICloneable { </xsl:text>
<!-- fields declaration -->
<xsl:for-each select="edmx:Attributes/edmx:add">
<xsl:text>		private	</xsl:text>
<xsl:choose>
<xsl:when test="@IsSingleValued='false'">
<xsl:choose>
<xsl:when test="contains(@DataType,',')">
<xsl:value-of select="substring-before(@DataType,',')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@DataType" />
</xsl:otherwise>
</xsl:choose>
<xsl:text>[]</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="contains(@DataType,',')">
<xsl:value-of select="substring-before(@DataType,',')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@DataType" />
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
<xsl:text>	_</xsl:text>
<xsl:value-of select="@Name" />
<xsl:text>; </xsl:text>
</xsl:for-each>
<xsl:text> </xsl:text>
<!-- properties declaration -->
<xsl:for-each select="edmx:Attributes/edmx:add">
<xsl:text>		[DirectoryAttribute(</xsl:text>
<xsl:value-of select="../../@LogicalName" />
<xsl:text>Attributes.</xsl:text>
<xsl:value-of select="@Name" />
<xsl:text>)] </xsl:text>
<xsl:text>		public	</xsl:text>
<xsl:choose>
<xsl:when test="@IsSingleValued='false'">
<xsl:choose>
<xsl:when test="contains(@DataType,',')">
<xsl:value-of select="substring-before(@DataType,',')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@DataType" />
</xsl:otherwise>
</xsl:choose>
<xsl:text>[]</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="contains(@DataType,',')">
<xsl:value-of select="substring-before(@DataType,',')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@DataType" />
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
<xsl:text>	</xsl:text>
<xsl:value-of select="@LogicalName" />
<xsl:text> </xsl:text>
<xsl:text>		{ </xsl:text>
<xsl:text>			get { return _</xsl:text>
<xsl:value-of select="@Name" />
<xsl:text>; } </xsl:text>
<xsl:text>			set { _</xsl:text>
<xsl:value-of select="@Name" />
<xsl:text>= value; } </xsl:text>
<xsl:text>		} </xsl:text>
</xsl:for-each>
<xsl:text>		public Object Clone() </xsl:text>
<xsl:text>		{ </xsl:text>
<xsl:text>			return MemberwiseClone (); </xsl:text>
<xsl:text>		} </xsl:text>
<xsl:text>	} </xsl:text>
<xsl:text>} </xsl:text>
</xsl:template>
<xsl:template match="edmx:Queries">
<xsl:text> 	public class QueryNames { </xsl:text>
<xsl:for-each select="edmx:Query">
<xsl:text>		public const string </xsl:text>
<xsl:value-of select="@Name" />
<xsl:text> = "</xsl:text>
<xsl:value-of select="@Name" />
<xsl:text>"; </xsl:text>
</xsl:for-each>
<xsl:text>	} </xsl:text>
</xsl:template>
</xsl:stylesheet>
即使您对 XSLT 语言一无所知,也可以阅读此文件。您可以在此处找到所有匹配 Web 数据服务 $metadata 结果节点的模板。您可以非常轻松地更改它。
在客户端项目的属性中,选择生成事件。在预生成事件中,您可以看到以下命令
$(SolutionDir)Tools\msxsl.exe
https://:3932/BackOfficeServices/WebDataService.svc/$metadata
$(ProjectDir)EntitiesGeneratorXSLTFile.xslt -o $(ProjectDir)EntitiesCustom.cs
它调用 `msxsl.exe` 工具,以通过转换我们的服务和指定的 XSLT 文件来生成一个名为 `entitiescustom.cs` 的文件。每次构建解决方案时,都会生成 `EntitiesCustom.cs` 文件,但 `msxsl.exe` 工具将使用从 Web 数据服务的 $metadata 响应中提取的 XML 以及前面看到的 XSLT。您可以在此命令前加上“REM”关键字以注释掉该行。
结果(代理类)
结果类似于 Microsoft 生成的代理类。我添加了一些调试器属性来清理类外观和一些 `#region`。这是生成的实体类之一
#region class Course
[global::System.Serializable()]
[global::System.Data.Services.Common.DataServiceKeyAttribute("CourseID")]
public partial class Course : global::System.ComponentModel.INotifyPropertyChanged
{
#region Fields
[global::System.Diagnostics.DebuggerBrowsable
System.Diagnostics.DebuggerBrowsableState.Never)]
private global::System.Int32 _CourseID;
[global::System.Diagnostics.DebuggerBrowsable
System.Diagnostics.DebuggerBrowsableState.Never)]
private global::System.Int32 _Credits;
[global::System.Diagnostics.DebuggerBrowsable
System.Diagnostics.DebuggerBrowsableState.Never)]
private global::System.String _Title;
[global::System.Diagnostics.DebuggerBrowsable
System.Diagnostics.DebuggerBrowsableState.Never)]
private global::System.Collections.ObjectModel.Collection<CourseGrade> _CourseGrade =
new global::System.Collections.ObjectModel.Collection<CourseGrade>();
[global::System.Diagnostics.DebuggerBrowsable
System.Diagnostics.DebuggerBrowsableState.Never)]
private Department _Department;
[global::System.Diagnostics.DebuggerBrowsable
System.Diagnostics.DebuggerBrowsableState.Never)]
private OnlineCourse _OnlineCourse;
[global::System.Diagnostics.DebuggerBrowsable
System.Diagnostics.DebuggerBrowsableState.Never)]
private OnsiteCourse _OnsiteCourse;
[global::System.Diagnostics.DebuggerBrowsable
System.Diagnostics.DebuggerBrowsableState.Never)]
private global::System.Collections.ObjectModel.Collection<Person> _Person =
new global::System.Collections.ObjectModel.Collection<Person>();
#endregion //Fields
#region Properties
public global::System.Int32 CourseID
{
get
{
return this._CourseID;
}
set
{
if (this._CourseID != value)
{
this._CourseID = value;
this.OnPropertyChanged("CourseID");
}
}
}
public global::System.Int32 Credits
{
get
{
return this._Credits;
}
set
{
if (this._Credits != value)
{
this._Credits = value;
this.OnPropertyChanged("Credits");
}
}
}
public global::System.String Title
{
get
{
return this._Title;
}
set
{
if (this._Title != value)
{
this._Title = value;
this.OnPropertyChanged("Title");
}
}
}
[global::System.Xml.Serialization.XmlIgnoreAttribute()]
[global::System.Xml.Serialization.SoapIgnoreAttribute()]
public global::System.Collections.ObjectModel.Collection<CourseGrade> CourseGrade
{
get
{
return this._CourseGrade;
}
set
{
if (this._CourseGrade != value)
{
this._CourseGrade = value;
this.OnPropertyChanged("CourseGrade");
}
}
}
[global::System.Xml.Serialization.XmlIgnoreAttribute()]
[global::System.Xml.Serialization.SoapIgnoreAttribute()]
public Department Department
{
get
{
return this._Department;
}
set
{
if (this._Department != value)
{
this._Department = value;
this.OnPropertyChanged("Department");
}
}
}
[global::System.Xml.Serialization.XmlIgnoreAttribute()]
[global::System.Xml.Serialization.SoapIgnoreAttribute()]
public OnlineCourse OnlineCourse
{
get
{
return this._OnlineCourse;
}
set
{
if (this._OnlineCourse != value)
{
this._OnlineCourse = value;
this.OnPropertyChanged("OnlineCourse");
}
}
}
[global::System.Xml.Serialization.XmlIgnoreAttribute()]
[global::System.Xml.Serialization.SoapIgnoreAttribute()]
public OnsiteCourse OnsiteCourse
{
get
{
return this._OnsiteCourse;
}
set
{
if (this._OnsiteCourse != value)
{
this._OnsiteCourse = value;
this.OnPropertyChanged("OnsiteCourse");
}
}
}
[global::System.Xml.Serialization.XmlIgnoreAttribute()]
[global::System.Xml.Serialization.SoapIgnoreAttribute()]
public global::System.Collections.ObjectModel.Collection<Person> Person
{
get
{
return this._Person;
}
set
{
if (this._Person != value)
{
this._Person = value;
this.OnPropertyChanged("Person");
}
}
}
#endregion //Property
#region INotifyPropertyChanged Membres
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string property)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this,
new System.ComponentModel.PropertyChangedEventArgs(property));
}
}
#endregion
}
#endregion //class
现在我们可以测试我们的代理了。
测试
我们将尝试检索一个 `Person` 并更改其名称。
这是添加到客户端项目 `Program.cs` 文件中的代码。
var query = from u in entities.Person
where u.LastName == "Abercrombie"
select u;
foreach (Person person in query)
{
Console.Out.WriteLine("A Person finded :");
Console.Out.WriteLine(string.Format("{0} {1}", person.FirstName, person.LastName));
Console.Out.WriteLine("Why not changing its name ?");
Console.Out.WriteLine("Please specifies a new first name :");
string firstName = Console.In.ReadLine();
person.FirstName = firstName;
entities.UpdateObject(person);
entities.SaveChanges();
Console.Out.WriteLine("New name saved");
}
我们的代理类工作正常!
结论
使用此方法,您可以轻松地为您的实体类添加功能,并享受 ADO.NET 数据服务。只需更改 XSLT 并添加您自己的功能。
我没有在很多场景中测试 XSLT。如果存在任何问题,请随时询问我。
历史
- 2008 年 9 月 19 日:初始帖子