65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (3投票s)

2008年9月19日

CPOL

4分钟阅读

viewsIcon

32394

downloadIcon

192

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 的实体项目
  • 一个客户端项目

gd01.png

在解决方案目录中,我添加了一个 `Tools` 目录,您可以在其中找到 `msxsl.exe` 工具

gd02.png

在启动服务之前,我们必须在 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=&quot;Data Source=localhost\sqlexpress;
Initial Catalog=School;Integrated Security=True;MultipleActiveResultSets=True&quot;"
providerName="System.Data.EntityClient" />

通过在解决方案资源管理器中右键单击 `WebDataService.svc` 文件并选择“在浏览器中打开”来启动 Web 数据服务。您将看到类似这样的内容

gd03.png

我们的服务已准备就绪。

生成需要元数据(XML 部分)

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

gd04.png

现在您可以看到 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()]&#10;</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>
&#10;&#10;</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>&#10;}&#10;</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>&#10;&#9;public partial class </xsl:text>
<xsl:value-of select="@Name"></xsl:value-of>
<xsl:text> : global::System.Data.Services.Client.DataServiceContext&#10;</xsl:text>
<xsl:text>&#9;{&#10;</xsl:text>
<xsl:text>&#10;&#9;&#9;public </xsl:text>
<xsl:value-of select="@Name"></xsl:value-of>
<xsl:text>(global::System.Uri serviceRoot) : base(serviceRoot)&#10;</xsl:text>
<xsl:text>&#9;&#9;{&#10;</xsl:text>
<xsl:text>&#9;&#9;&#9;this.OnContextCreated();&#10;</xsl:text>
<xsl:text>&#9;&#9;}&#10;</xsl:text>
<xsl:text>&#9;&#9;partial void OnContextCreated();&#10;</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>&#10;&#9;}&#10;&#10;</xsl:text>
</xsl:template>
<!--ENTITIES QUERIES-->
<xsl:template match="schema:EntitySet" mode="Properties">
<xsl:param name="NamespaceName" select="NamespaceName"></xsl:param>
<xsl:text>&#10;&#9;&#9;
	public System.Data.Services.Client.DataServiceQuery&lt;</xsl:text>
<xsl:value-of select="substring-after(@EntityType, concat($NamespaceName, '.'))"/>
<xsl:text>&gt; </xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>&#10;&#9;&#9;{&#10;</xsl:text>
<xsl:text>&#9;&#9;&#9;get&#10;</xsl:text>
<xsl:text>&#9;&#9;&#9;{&#10;</xsl:text>
<xsl:text>&#9;&#9;&#9;&#9;if (this._</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text> == null)&#10;</xsl:text>
<xsl:text>&#9;&#9;&#9;&#9;{&#10;</xsl:text>
<xsl:text>&#9;&#9;&#9;&#9;&#9;this._</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text> = base.CreateQuery&lt;</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>&gt;("[</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>]");&#10;</xsl:text>
<xsl:text>&#9;&#9;&#9;&#9;}&#10;</xsl:text>
<xsl:text>&#9;&#9;&#9;&#9;return this._</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>;&#10;</xsl:text>
<xsl:text>&#9;&#9;&#9;}&#10;</xsl:text>
<xsl:text>&#9;&#9;}&#10;</xsl:text>
<xsl:text>&#9;&#9;private global::System.Data.Services.Client.DataServiceQuery&lt;
</xsl:text>
<xsl:value-of select="substring-after(@EntityType, concat($NamespaceName, '.'))"/>
<xsl:text>&gt; _</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>;&#10;</xsl:text>
</xsl:template>
<xsl:template match="schema:EntitySet" mode="Add">
<xsl:param name="NamespaceName" select="NamespaceName"></xsl:param>
<xsl:text>&#10;&#9;&#9;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>)&#10;</xsl:text>
<xsl:text>&#9;&#9;{&#10;</xsl:text>
<xsl:text>&#9;&#9;&#9;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>);&#10;</xsl:text>
<xsl:text>&#9;&#9;}&#10;</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>))]&#10;</xsl:text>
</xsl:template>
<!--CLASSES-->
<xsl:template match="schema:EntityType">
<xsl:text>&#10;&#10;</xsl:text>
<xsl:text>&#9;#region class </xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>&#10;&#10;</xsl:text>
<xsl:text>&#9;[global::System.Serializable()]</xsl:text>
<xsl:text>&#9;[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>)]&#10;</xsl:text>
<xsl:text>&#9;public partial class </xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text> : global::System.ComponentModel.INotifyPropertyChanged
&#9;{&#10;</xsl:text>
<!-- FIELDS -->
<xsl:text>&#10;&#9;&#9;#region Fields&#10;&#10;</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>&#10;&#9;&#9;#endregion //Fields&#10;</xsl:text>
<!-- PROPERTIES -->
<xsl:text>&#10;&#10;&#9;&#9;#region Properties&#10;&#10;</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>&#10;&#9;&#9;#endregion //Property&#10;</xsl:text>
<xsl:text>&#10;&#10;&#9;&#9;#region INotifyPropertyChanged Membres
&#10;&#9;&#9;public event 
	System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
&#9;&#9;protected virtual void OnPropertyChanged(string property)
&#9;&#9;{
&#9;&#9;&#9;if (this.PropertyChanged != null)
&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;this.PropertyChanged
	(this, new System.ComponentModel.PropertyChangedEventArgs(property));
&#9;&#9;&#9;}
&#9;&#9;}
&#9;&#9;#endregion&#10;</xsl:text>
<xsl:text>&#10;&#9;}&#10;</xsl:text>
<xsl:text>&#10;&#10;&#9;#endregion //class&#10;</xsl:text>
</xsl:template>
<!--FIELDS-->
<xsl:template match="schema:Property" mode="FieldsDeclaration">
<xsl:text>&#9;&#9;[global::System.Diagnostics.DebuggerBrowsable
	(System.Diagnostics.DebuggerBrowsableState.Never)]&#10;</xsl:text>
<xsl:text>&#9;&#9;private</xsl:text>
<xsl:choose>
<xsl:when test="(@Nullable='true') and @Type != 'Edm.String'">
<xsl:text> global::System.Nullable&lt;global::System.</xsl:text>
<xsl:value-of select="substring-after(@Type, '.')"/>
<xsl:text>&gt;</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>;&#10;</xsl:text>
</xsl:template>
<xsl:template match="schema:NavigationProperty" mode="FieldsDeclaration">
<xsl:text>&#9;&#9;
[global::System.Diagnostics.DebuggerBrowsable
(System.Diagnostics.DebuggerBrowsableState.Never)]&#10;</xsl:text>
<xsl:text>&#9;&#9;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&lt;</xsl:text>
<xsl:value-of select="substring-after($Type, concat($NamespaceName, '.'))"/>
<xsl:text>&gt;</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&lt;</xsl:text>
<xsl:value-of select="substring-after($Type, concat($NamespaceName, '.'))"/>
<xsl:text>&gt;();&#10;</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>;&#10;</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--PROPERTY-->
<xsl:template match="schema:Property" mode="PropertiesDeclaration">
<xsl:text>&#9;&#9;public </xsl:text>
<xsl:choose>
<xsl:when test="(@Nullable='true') and @Type != 'Edm.String'">
<xsl:text> global::System.Nullable&lt;global::System.</xsl:text>
<xsl:value-of select="substring-after(@Type, '.')"/>
<xsl:text>&gt;</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>&#10;&#9;&#9;{</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;get</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;{</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;&#9;return this._</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>;</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;}</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;set</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;{</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;&#9;if (this._</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text> != value)</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;&#9;{</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;&#9;&#9;this._</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text> = value;</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;&#9;&#9;this.OnPropertyChanged("</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>");</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;&#9;}</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;}</xsl:text>
<xsl:text>&#10;&#9;&#9;}&#10;&#10;</xsl:text>
</xsl:template>
<xsl:template match="schema:NavigationProperty" mode="PropertiesDeclaration">
<xsl:text>&#9;&#9;[global::System.Xml.Serialization.XmlIgnoreAttribute()]
&#9;&#9;[global::System.Xml.Serialization.SoapIgnoreAttribute()]
&#9;&#9;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&lt;</xsl:text>
<xsl:value-of select="substring-after($Type, concat($NamespaceName, '.'))"/>
<xsl:text>&gt;</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:text> </xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>&#10;&#9;&#9;{</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;get</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;{</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;&#9;return this._</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>;</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;}</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;set</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;{</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;&#9;if (this._</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text> != value)</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;&#9;{</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;&#9;&#9;this._</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text> = value;</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;&#9;&#9;this.OnPropertyChanged("</xsl:text>
<xsl:value-of select="@Name"/>
<xsl:text>");</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;&#9;}</xsl:text>
<xsl:text>&#10;&#9;&#9;&#9;}</xsl:text>
<xsl:text>&#10;&#9;&#9;}&#10;&#10;</xsl:text>
</xsl:template>
<xsl:template match="edmx:Classes">
<xsl:text>&#10;&#9;public sealed class ClassNames {&#10;&#10;</xsl:text>
<xsl:for-each select="edmx:Class">
<xsl:text>&#9;&#9;public const string </xsl:text>
<xsl:value-of select="@LogicalName" />
<xsl:text> = "</xsl:text>
<xsl:value-of select="@LogicalName" />
<xsl:text>";&#10;</xsl:text>
</xsl:for-each>
<xsl:text>&#9;}&#10;</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 {&#10;</xsl:text>
<xsl:text>&#10;&#9;public sealed class </xsl:text>
<xsl:value-of select="@LogicalName" />
<xsl:text>Attributes {&#10;&#10;</xsl:text>
<xsl:for-each select="edmx:Attributes/edmx:add">
<xsl:text>&#9;&#9;///&lt;summary&gt; </xsl:text>
<xsl:value-of select="@LogicalName" />
<xsl:text>&lt;/summary&gt;&#10;</xsl:text>
<xsl:text>&#9;&#9;public const string </xsl:text>
<xsl:value-of select="@Name" />
<xsl:text> = "</xsl:text>
<xsl:value-of select="@Name" />
<xsl:text>";&#10;</xsl:text>
<xsl:if test="@MinLength">
<xsl:text>&#9;&#9;public const int </xsl:text>
<xsl:value-of select="@Name" />
<xsl:text>_MinLength = </xsl:text>
<xsl:value-of select="@MinLength" />
<xsl:text>;&#10;</xsl:text>
</xsl:if>
<xsl:if test="@MaxLength">
<xsl:text>&#9;&#9;public const int </xsl:text>
<xsl:value-of select="@Name" />
<xsl:text>_MaxLength = </xsl:text>
<xsl:value-of select="@MaxLength" />
<xsl:text>;&#10;</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>&#9;}&#10;</xsl:text>
<xsl:text>}&#10;</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 {&#10;</xsl:text>
<xsl:text>&#10;&#9;[Serializable]</xsl:text>
<xsl:text>&#10;&#9;[DirectoryClass(ClassNames.</xsl:text>
<xsl:value-of select="@LogicalName" />
<xsl:text>)]</xsl:text>
<xsl:text>&#10;&#9;public partial class </xsl:text>
<xsl:value-of select="@LogicalName" />
<xsl:text> : System.ICloneable {&#10;&#10;</xsl:text>
<!-- fields declaration -->
<xsl:for-each select="edmx:Attributes/edmx:add">
<xsl:text>&#9;&#9;private&#9;</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>&#9;_</xsl:text>
<xsl:value-of select="@Name" />
<xsl:text>;&#10;</xsl:text>
</xsl:for-each>
<xsl:text>&#10;</xsl:text>
<!-- properties declaration -->
<xsl:for-each select="edmx:Attributes/edmx:add">
<xsl:text>&#9;&#9;[DirectoryAttribute(</xsl:text>
<xsl:value-of select="../../@LogicalName" />
<xsl:text>Attributes.</xsl:text>
<xsl:value-of select="@Name" />
<xsl:text>)]&#10;</xsl:text>
<xsl:text>&#9;&#9;public&#9;</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>&#9;</xsl:text>
<xsl:value-of select="@LogicalName" />
<xsl:text>&#10;</xsl:text>
<xsl:text>&#9;&#9;{&#10;</xsl:text>
<xsl:text>&#9;&#9;&#9;get { return _</xsl:text>
<xsl:value-of select="@Name" />
<xsl:text>; }&#10;</xsl:text>
<xsl:text>&#9;&#9;&#9;set { _</xsl:text>
<xsl:value-of select="@Name" />
<xsl:text>= value; }&#10;</xsl:text>
<xsl:text>&#9;&#9;}&#10;&#10;</xsl:text>
</xsl:for-each>
<xsl:text>&#9;&#9;public Object Clone()&#10;</xsl:text>
<xsl:text>&#9;&#9;{&#10;</xsl:text>
<xsl:text>&#9;&#9;&#9;return MemberwiseClone ();&#10;</xsl:text>
<xsl:text>&#9;&#9;}&#10;</xsl:text>
<xsl:text>&#9;}&#10;</xsl:text>
<xsl:text>}&#10;</xsl:text>
</xsl:template>
<xsl:template match="edmx:Queries">
<xsl:text>&#10;&#9;public class QueryNames {&#10;&#10;</xsl:text>
<xsl:for-each select="edmx:Query">
<xsl:text>&#9;&#9;public const string </xsl:text>
<xsl:value-of select="@Name" />
<xsl:text> = "</xsl:text>
<xsl:value-of select="@Name" />
<xsl:text>";&#10;</xsl:text>
</xsl:for-each>
<xsl:text>&#9;}&#10;</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");
}

gd06.png

我们的代理类工作正常!

结论

使用此方法,您可以轻松地为您的实体类添加功能,并享受 ADO.NET 数据服务。只需更改 XSLT 并添加您自己的功能。

我没有在很多场景中测试 XSLT。如果存在任何问题,请随时询问我。

历史

  • 2008 年 9 月 19 日:初始帖子
© . All rights reserved.