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

从 POCO 类自动生成 WCF 的 DataContracts

starIconstarIconstarIconstarIconstarIcon

5.00/5 (6投票s)

2016年2月26日

CPOL

10分钟阅读

viewsIcon

21679

downloadIcon

427

将 POCO 类库转换为 WCF 使用的数据契约,而无需直接在 POCO 类中编写 DataContract 和 DataMember 属性。

引言

在之前一篇题为《使用 EF Power Tool 生成 EF POCO 类》的文章中,我们使用 EF Power Tool 从现有 SQL Server 数据库自动生成了一个包含 POCO 类的类库。本文旨在演示如何将这个包含 POCO 类的类库转换为 DataContracts 以供 WCF 服务应用程序使用,而无需手动修改自动生成的 POCO 类。为了完整起见,还简要介绍了一下如何从非 EF Power Tool 创建的 POCO 类库创建 DataContract

背景

在使用 EF Power Tool 从现有数据库生成 POCO 类、映射和 DBContext 以编写使用 Code First 方法的应用程序时,可能会出现需要将部分或全部 POCO 类用于 WCF 服务的情况。为了做到这一点,有必要手动修改 POCO 类,并添加 DataContractDataMember 属性。然而,下次执行 EF Power Tool 刷新数据库模式中的任何更改时,这些更改都将被覆盖。以一种不手动更改 POCO 类的方式,从现有的 POCO 类生成 WCF 服务 DataContracts 将非常有用。本文将解释如何实现这一点。

过程详见以下两节

从 EF Power Tool 生成的 POCO 类库生成 DataContracts

为了从 EF Power Tool 创建的 POCO 类库生成 DataContracts,需要进行以下 3 个步骤:

  1. 修改 EF Power Tool 的 T4 模板并添加附加属性
  2. 使用 xsd.exe 工具从 POCO 类库创建 XSD 文件
  3. 使用 SVCUtil.exe 将 xsd 文件转换为 datacontracts

下图提供了这些步骤的摘要

第一个 zip 文件包含一个示例 VS 2013 解决方案,我们将用它来逐步完成此过程。

第二个 zip 文件包含相同的 VS 2013 解决方案,其中已应用了建议的更改以生成 datacontracts。它仅供参考。

让我们在 Visual Studio 中打开第一个 zip 文件中的解决方案。该解决方案包含三个项目,即:

  • DataContracts - 将在此处放置自动生成的 datacontracts 文件
  • WCF 服务库 - 使用 DataContracts 项目,并且仅是本文使用的脚手架。
  • Models - 使用 EF Power Tool 对现有数据库进行反向工程创建的现有模型。

Test Solution

上图显示了 Models 项目、DataContracts 项目以及一个引用了 DataContracts 项目的 WCF 服务库项目。在此示例中,Service Library 实际上并未引用 Models 项目。在更实际的应用中,它会设置如下:

在这里,Models 项目被 Repositories 项目引用,用于执行 WCF 服务库请求的数据库操作。在本文中,我们将保持简单。重点是 Models 项目,以及如何将其转换为 DataContracts 项目,而不是深入研究 Repositories 和使用 Models 项目中的 POCO 库进行 CRUD 操作。

1. 修改 EF Power Tool 的 T4 模板并添加附加属性

EF Power Tool 使用 T4 模板自动生成 POCO 类、DBContext 和映射。Models 类库中的默认自动生成代码在生成 xsd 文件时会产生序列化错误,因为属性类型为 ICollectionDBSetICollectionList 通常在 servicecontract 中声明。DBSet 类型不需要提供给客户端。因此,这两种数据类型都将被排除在生成的 XSD 文件之外。为了做到这一点,我们将直接修改 T4 模板,如下所示,并重新生成在使用 EF Power Tool 生成 EF POCO 类中创建的 POCO 类库。

1.1 右键单击 Models 项目,然后选择 -> Entity Framework -> Customize Reverse Engineer Templates。

如果菜单选项未出现,则表示 Visual Studio 中未安装 EF Power Tool。可以从以下位置安装:

此时,Models 项目将显示如下:

已创建一个名为 CodeTemplates 的新文件夹。这暴露了 EF Power tool 用于创建 DBContext、POCO 类和映射的三个 T4 模板。

1.2 修改 Entity.tt 文件,并在第 61 行添加以下内容。

[System.Xml.Serialization.XmlIgnore]

The text will now appear as:
foreach (var navProperty in efHost.EntityType.NavigationProperties.Where
(np => np.DeclaringType == efHost.EntityType))
    {
        if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
        {
#>
        [System.Xml.Serialization.XmlIgnore]
        public virtual ICollection<<#= code.Escape(navProperty.ToEndMember.GetEntityType()) 
        #>> <#= code.Escape(navProperty) #> { get; set; }
<#
        }
        else
        {
#>
        public virtual <#= code.Escape(navProperty.ToEndMember.GetEntityType()) 
        #> <#= code.Escape(navProperty) #> { get; set; }
<#
        }
    }
#>
    }

1.3 修改 Context.tt 文件,并在第 30 行添加以下内容

[System.Xml.Serialization.XmlIgnore]

文本现在将显示如下:

<#
    foreach (var set in efHost.EntityContainer.BaseEntitySets.OfType<EntitySet>())
    {
#>
        [System.Xml.Serialization.XmlIgnore]
        public DbSet<<#= set.ElementType.Name #>> <#= set.Name #> { get; set; }
<#
    }
#>

1.4 为了将 T4 模板中的更改应用到 POCO 类,有必要重新生成它们。右键单击 Models 项目,然后选择 Reverse Engineer Code First,如下所示:

现在输入 SQL Server 的登录详细信息。

单击 Advanced 按钮。

将突出显示的属性设置为 True,然后单击 OK 按钮关闭 Advanced Properties 窗口。在 Connection properties 窗口中单击 OK 按钮。这将使用对 T4 模板所做的更改重新生成 Models 文件夹中的文件。

过程完成后,User.cs 文件将发生以下更改:

[System.Xml.Serialization.XmlIgnore]
public virtual ICollection<UserGroup> UserGroups { get; set; }

UsersContext.cs 文件也为所有 DbSet 属性应用了 XmlIgnore 属性。

        [System.Xml.Serialization.XmlIgnore]
        public DbSet<Group> Groups { get; set; }
        [System.Xml.Serialization.XmlIgnore]
        public DbSet<GroupPermission> GroupPermissions { get; set; }
        [System.Xml.Serialization.XmlIgnore]
        public DbSet<Permission> Permissions { get; set; }
        [System.Xml.Serialization.XmlIgnore]
        public DbSet<sysdiagram> sysdiagrams { get; set; }
        [System.Xml.Serialization.XmlIgnore]
        public DbSet<User> Users { get; set; }
        [System.Xml.Serialization.XmlIgnore]
        public DbSet<UserGroup> UserGroups { get; set; }

[System.Xml.Serialization.XmlIgnore] 可防止属性被序列化。对 T4 模板的更改已针对特定类型的属性(如 ICollectionDbset)进行,这些属性不应被序列化。xsd 工具的 DataContractSerializer 无法序列化接口,因此需要将 ICollection 更改为 List 以避免错误。在本例中,我选择了阻止 ICollection 被序列化。DBSet 也将由于继承自 IEnumerable 接口而无法序列化。

通过直接修改 T4 模板以添加 [System.Xml.Serialization.XmlIgnore] 属性,只需应用一次模板更改。当再次生成类库时,[System.Xml.Serialization.XmlIgnore] 属性将自动应用于不需要序列化的属性。

让我们生成 Models 类库项目。在生成过程中,您可能会看到以下错误:

The type or namespace name 'EfTextTemplateHost' could not be found 
(are you missing a using directive or an assembly reference?)  

可以忽略此错误,因为项目在生成时不会使用 T4 模板。仅当运行 EF power tool 生成 POCO 类时,才会使用这些模板。

models.dll 文件在 bin\debug 文件夹中创建,并将在下一步用于序列化。

2. 使用 xsd.exe 工具从 POCO 类库创建 XSD 文件

将使用 VS 命令提示符中的 xsd.exe 工具创建 POCO 类的 xsd 文件。然后将 .xsd 文件添加到 DataContracts 项目,并使用 SVCUtil.exe 工具生成包含 datacontracts 的 .cs 文件。

2.1 通过从 Visual Studio 2013 工具文件夹加载以下窗口,转到 VS 命令提示符

选择上面的突出显示的选项。注意:具体步骤可能因您安装的 VS 版本而异。

将目录更改为包含 models.dll 文件的目录。该文件位于 bin\debug 文件夹中,如上所示。

输入以下命令:

xsd models.dll

名为 Schema0.xsd 的文件在 bin\debug 文件夹中生成。

Schema0.xsd 文件复制并添加到 DataContracts 项目中,如下面的突出显示部分所示:

3. 使用 SVCUtil.exe 将 xsd 文件转换为 datacontracts。

打开 Visual Studio 命令提示符,并将目录更改为 DataContracts 项目的应用程序文件夹。

输入以下命令:

svcutil.exe /target:code   /n:*,DataModelLayer /dataContractOnly 
/serializer:DataContractSerializer /importXmlTypes schema0.xsd /out:DataContracts.cs

该命令将生成一个名为 DataContracts.cs 的文件。该文件可以包含在 Visual Studio 的 DataContractLayer 项目中,并由 servicecontract 使用以向客户端提供数据。

schema0.xsd 也可以提供给使用该服务的客户端,允许客户端开发人员使用 SVCUtil.exe 生成 datacontracts,或者可以将 DataContracts.cs 文件发送给编写客户端应用程序的开发人员。

DataContracts.cs 文件包含 Models 项目中所有类文件的 datacontracts。打开该文件时,显示的第一个 datacontractGroup 模型的,如下所示。请注意,ICollection 属性已从 datacontract 中排除,因为它在模型中被标记为 [System.Xml.Serialization.XmlIgnore] 属性。

<!--StartFragment -->[System.Runtime.Serialization.DataContractAttribute(Name="Group", Namespace="")]
    public partial class Group : object, System.Runtime.Serialization.IExtensibleDataObject
    {
        private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
       
        private int IDField;
       
        private string NameField;
       
        private System.Nullable<bool> ActiveField;
       
        private System.Nullable<System.DateTime> DateCreatedField;
       
        public System.Runtime.Serialization.ExtensionDataObject ExtensionData
        {
            get
            {
                return this.extensionDataField;
            }
            set
            {
                this.extensionDataField = value;
            }
        }
       
        [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true)]
        public int ID
        {
            get
            {
                return this.IDField;
            }
            set
            {
                this.IDField = value;
            }
        }
       
        [System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false)]
        public string Name
        {
            get
            {
                return this.NameField;
            }
            set
            {
                this.NameField = value;
            }
        }
       
        [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true, Order=2)]
        public System.Nullable<bool> Active
        {
            get
            {
                return this.ActiveField;
            }
            set
            {
                this.ActiveField = value;
            }
        }
       
        [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true, Order=3)]
        public System.Nullable<System.DateTime> DateCreated
        {
            get
            {
                return this.DateCreatedField;
            }
            set
            {
                this.DateCreatedField = value;
            }
        }
    }

另外,如果您滚动浏览该文件并找到 UsersContext datacontract,它将显示一个空类,因为我们只对生成简单 POCO 类的 datacontract 感兴趣。如前所述,DBContext 不会提供给 Client

[System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
    [System.Runtime.Serialization.DataContractAttribute(Name="UsersContext", Namespace="")]
    public partial class UsersContext : DataModelLayer.DbContext
    {
    }

上面 datacontract 的实际模型如下所示,其中 DBSet 返回类型属性已应用 [System.Xml.Serialization.XmlIgnore] 属性。 当它被 xsd.exe 序列化并通过 svcutil.exe 处理时,具有 XmlIgnore 属性的属性将从生成的 datacontract 中排除。

using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using Models.Models.Mapping;

namespace Models.Models
{
    public partial class UsersContext : DbContext
    {
        static UsersContext()
        {
            Database.SetInitializer<UsersContext>(null);
        }

        public UsersContext()
            : base("Name=UsersContext")
        {
        }

        [System.Xml.Serialization.XmlIgnore]
        public DbSet<Group> Groups { get; set; }
        [System.Xml.Serialization.XmlIgnore]
        public DbSet<GroupPermission> GroupPermissions { get; set; }
        [System.Xml.Serialization.XmlIgnore]
        public DbSet<Permission> Permissions { get; set; }
        [System.Xml.Serialization.XmlIgnore]
        public DbSet<sysdiagram> sysdiagrams { get; set; }
        [System.Xml.Serialization.XmlIgnore]
        public DbSet<User> Users { get; set; }
        [System.Xml.Serialization.XmlIgnore]
        public DbSet<UserGroup> UserGroups { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new GroupMap());
            modelBuilder.Configurations.Add(new GroupPermissionMap());
            modelBuilder.Configurations.Add(new PermissionMap());
            modelBuilder.Configurations.Add(new sysdiagramMap());
            modelBuilder.Configurations.Add(new UserMap());
            modelBuilder.Configurations.Add(new UserGroupMap());
        }
    }
}

至此,关于如何将 EF Power Tool 生成的 POCO 类库项目转换为 datacontracts 的部分结束。上述方法也可以用于生成 POCO 的 datacontracts,这些 POCO 是从 EF designer 创建的,通过修改 T4 模板(如上所述)并创建 .xsd 文件,该文件可以通过 SVCUtil.exe 工具处理以创建 datacontracts 文件。

转换不使用 EF Power Tool 创建的 POCO 类库项目

更改 T4 模板以生成 POCO 并使其适合转换为 datacontracts 的第一步可以跳过,因为未使用 EF Power tool。

以下是生成类库 datacontracts 所需的修订步骤摘要:

  • 直接修改 POCO 类,并将 [System.Xml.Serialization.XmlIgnore] 属性应用于返回无法序列化的数据类型的属性,例如 ICollectionDBSet。或者,使用不同的返回类型,即,将 ICollection 更改为 List
  • 生成类库以刷新 Bin\Debug 文件夹中的项目 DLL。
  • 通过 xsd.exe 工具处理项目 DLL,如上所示,可以选择序列化整个程序集或仅特定类。
  • 通过 SVCUtil.exe 处理生成的 .xsd 文件,以生成包含所有 datacontracts 的文件。

关注点

  1. 运行 XSD 工具时,可能会出现类似下图的错误。
    Error: There was an error processing 'models.dll'.
      - There was an error reflecting type 'Models.Models.Group'.
      - Cannot serialize member 'Models.Models.Group.GroupPermissions' of type 'Syst
    em.Collections.Generic.ICollection`1[[Models.Models.GroupPermission, Models, Ver
    sion=1.0.0.0, Culture=neutral, PublicKeyToken=null]]', see inner exception for m
    ore details.
      - Cannot serialize member Models.Models.Group.GroupPermissions of type System.
    Collections.Generic.ICollection`1[[Models.Models.GroupPermission, Models, Version=1.0.0.0, 
    Culture=neutral, PublicKeyToken=null]] because it is an interface.

    出现此错误是因为 xsd.exe 无法序列化接口。通过修改 T4 模板并在导致错误的属性上添加 [System.Xml.Serialization.XmlIgnore] 来解决了此问题。也可以通过将 ICollection 更改为 List 来解决此错误。

  2. datacontracts.cs 文件包含空类或不应作为 datacontracts 提供给客户端的类。

    可以生成一个包含要转换为 datacontracts 的特定类的 xsd 文件,而排除那些不打算向消费者公开的类。

    Models 项目的 Bin\Debug 文件夹中,输入以下命令:

    xsd models.dll /t:User /t:UserGroup /t:Group /t:Permission /t:GroupPermission

    在上面的示例中,只有 UserUserGroupGroupPermissionGroupPermission 类将被序列化到 Schema0.xsd 文件。models.dll 中的其他类(如 UsersContext.cs)将被排除。

结论

本文演示了如何通过将类库项目的 .dll 文件转换为 .xsd 文件,然后使用 SVCUtil.exe 工具创建 DataContracts 文件,从而将 POCO 类库转换为 DataContracts。本文的重点是展示如何通过直接操作生成类的 T4 模板来实现这一点,以使类更适合序列化。本文还简要介绍了如何为未使用 EF Power Tool 创建的 POCO 类库创建 datacontracts

历史

  • v 1.0 - 12:25 GMT 2016-02-26
© . All rights reserved.