Click here to Skip to main content
15,886,823 members
Articles / Programming Languages / C#

Autogenerate DataContracts for WCF from POCO Classes

Rate me:
Please Sign up or sign in to vote.
5.00/5 (6 votes)
7 Dec 2016CPOL10 min read 20.6K   226   12  
Convert a class library of POCO classes to datacontracts for use with WCF without having to write DataContract and DataMember attributes directly to the POCO classes.

Introduction

In a previous article titled Use EF Power Tool to generate EF POCO classes, a class library containing POCO classes was autogenerated from an existing SQL server database using the EF Power Tool. The purpose of this article is to demonstrate how this class library containing POCO classes can be converted into DataContracts for use in a WCF service application, without the need to modify the autogenerated POCO classes manually. For completeness, there is also a brief section on how to create DataContracts from a class library of POCOs not produced using the EF Power Tool.

Background

When using the EF Power Tool to generate POCO classes, mappings, and DBContext from an existing database to write an application using the Code First approach, there may a situation when some or all of the POCO classes need to be used in a WCF service. In order to do this, it would be necessary to make changes to the POCO classes manually, and add DataContract and DataMember attributes. However, the changes would be overwritten the next time the EF Power tool was executed to refresh any changes made to the database schema. Some way of producing DataContracts for a WCF Service, from existing POCO classes, would be useful without having to change the POCO classes manually. The article explains how this is possible.

The process is detailed in the following two sections:

Generate DataContracts from POCO Class Library Produced Using the EF Power Tool

In order to produce the DataContracts from a class library of POCO classes created by the EF power tool, the following 3 steps are involved:

  1. Modify the T4 Template for the EF Power tool and add additional attributes
  2. Create an XSD file from the POCO class library using xsd.exe tool
  3. Use SVCUtil.exe to convert the xsd file into datacontracts.

The diagram below provides a summary of these steps:

Image 1

The first zip file contains a sample VS 2013 solution which will be used to walk though this process.

The second zip file contains the same VS 2013 solution with the suggested changes already applied in order to produce the datacontracts. It is attached for the purpose of reference only.

Let's open the solution contained in the first zip file in Visual Studio. The solution consists of three projects, i.e.:

  • DataContracts - Autogenerated datacontracts file will be placed here
  • The WCF Service Library - uses the DataContracts project and is only a scaffold used for this article.
  • Models - the existing models created using the EF Power Tool to reverse engineer an existing database.

Test Solution

The above screenshot shows the Models project, the DataContracts project, and a WCF Service library project which references the DataContracts project. The Models project is actually not referenced by the Service Library in this example. In a more realistic application, it would be setup as follows:

Image 3

Here, the Models project is referenced by the Repositories project for performing the database operations requested by the WCF Service Library. In this article, we will keep things simple. The focus is the Models project, and how this can be converted into the DataContracts project, rather than delving into the Repositories and CRUD operations using the POCO library in the Models project.

Image 4

1. Modify the T4 Template for the EF Power Tool and Add Additional Attributes

EF Power Tool uses T4 Templates to autogenerate the POCO classes, DBContext, and mappings. The default autogenerated code in the Models class library cannot be converted into an xsd file without producing serialization errors for properties of type ICollection and DBSet. The ICollection or a List would normally be declared in a servicecontract. The DBSet type would not need to be served to a client. Therefore, both data types will be excluded from the resulting XSD file. In order to do this, we will directly modify the T4 templates as shown below and regenerate the POCO class library which was created in Use EF Power Tool to generate EF POCO classes.

1.1 Right-click on the Models project and select -> Entity Framework -> Customize Reverse Engineer Templates.

Image 5

If the menu option fails to appear, then EF Power Tool has not been installed in Visual Studio. This can be installed from the following location:

The Models project will now appear as below:

Image 6

A new folder named CodeTemplates has been created. This exposes the three T4 templates used by the EF Power tool to create the DBContext, POCO classes, and Mappings.

1.2 Modify the Entity.tt file and add the following at line 61.

C#
[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 Modify the Context.tt file and add the following at line 30:

C#
[System.Xml.Serialization.XmlIgnore]

The text will now appear as:

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

1.4 In order to apply the changes in the T4 templates to the POCO classes, it is necessary to generate them again. Right click on the Models project and select Reverse Engineer Code First as shown below.

Image 7

Now key in the login details for SQL Server.

Image 8

Click the Advanced button.

Image 9

Set the highlighted property to True and click the OK button to close the Advanced Properties window. Click the OK button in the Connection properties window. This will regenerate the files in the Models folder with the changes applied to the T4 Templates.

Once the process is completed, the User.cs file will have the following change:

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

The UsersContext.cs also has the XmlIgnore attribute applied to all the DbSet properties.

C#
[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; }

The [System.Xml.Serialization.XmlIgnore] prevents a property from being serialized. The changes to the T4 templates has targeted specific type of properties from being serialized such as ICollection and Dbset. The DataContractSerializer of the xsd tool is unable to serialize an interface, therefore the ICollection would need to be changed to a List in order to avoid an error. In this example, I have chosen to prevent the ICollection from being serialized. DBSet would also fail to serialize as it inherits from the IEnumerable interface.

By modifying the T4 template directly to add the [System.Xml.Serialization.XmlIgnore] attribute, the change to the templates would only need to be applied once. When the class library is generated again, the [System.Xml.Serialization.XmlIgnore] attribute will be applied automatically to the properties which are not to be serialized.

Let's build the Models class library project. During the build, you may see the following error:

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

This error can be ignored because when the project builds, it does not use the T4 templates. The templates are only used when the EF power is run to generate the POCO classes.

The models.dll file is created in the bin\debug folder, and will be used in the next step for serialization.

2. Create an XSD file from the POCO class library using xsd.exe tool

An xsd file of the POCO classes will be created using the xsd.exe tool from VS command prompt. The .xsd file will then be added to the DataContracts project, and the SVCUtil.exe tool will be used to produce the .cs file containing the datacontracts.

2.1 Go to the VS command prompt by loading the following window from the Visual Studio 2013 tools folder:

Image 10

Select the highlighted option above. Note: The procedure may be different depending on the version of VS you have installed.

Image 11

Change directory to the directory containing the models.dll file. This file is contained in the bin\debug folder as shown above.

Enter the following command:

xsd models.dll

The file named Schema0.xsd is generated in the bin\debug folder.

Image 12

Copy and add the Schema0.xsd file into the DataContracts project as highlighted below.

Image 13

3. Use SVCUtil.exe to convert the xsd file into datacontracts.

Open the Visual Studio Command prompt and change directory to the application folder of the DataContracts project.

Image 14

Key in the following command:

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

The command will generate a file named DataContracts.cs. This file can be included in the DataContractLayer project from Visual Studio and used by a servicecontract to serve data to the client.

The schema0.xsd can also be given to a client which consumes the service and allow the client developer to generate the datacontracts using the SVCUtil.exe, or the DataContracts.cs file can be sent to the developer who is writing the client application.

Image 15

The DataContracts.cs file contains the datacontracts for all the class files in the Models projects. When the file is opened, the first datacontract shown is for the Group model as seen below. Notice that the ICollection property has been excluded from the datacontract as it was marked with the [System.Xml.Serialization.XmlIgnore] attribute in the model.

C#
<!--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;
            }
        }
    }

Also, if you were to scroll down in the file and locate the UsersContext datacontract, this will show an empty class because we are only interested in producing a datacontract for simple POCO classes. As mentioned earlier, the DBContext will not be served to a Client.

C#
[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
    {
    }

The actual model for the above datacontract is shown below with the [System.Xml.Serialization.XmlIgnore] attribute applied to the DBSet return type properties. When this is serialized by xsd.exe and passed through svcutil.exe, the properties with the XmlIgnore attribute are excluded from the resulting datacontract.

C#
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());
        }
    }
}

This concludes the section on how to convert a class library project of POCO's generated from the EF Power tool to datacontracts. The above approach can also be used to generate datacontracts for POCOs which were created from the EF designer, by modifying the T4 templates as suggested above and creating a .xsd file which can be passed through the SVCUtil.exe tool in order to create the datacontracts file.

Convert a Class Library Project Containing POCO Classes Not Created using the EF Power Tool

The first step of changing the T4 template to generate POCOs to make them suitable for conversion to datacontracts can be skipped as the EF Power tool was not used.

Here is a summary of the revised steps involved in producing datacontracts for a class library:

  • Modify the POCO classes directly and apply the [System.Xml.Serialization.XmlIgnore] attribute to those properties returning data types which cannot be serialized such as ICollection and DBSet. Alternatively, use a different return type, i.e., change ICollection to List.
  • Build the class library to refresh the project DLL in the Bin\Debug folder.
  • Pass the project DLL through the xsd.exe tool as shown above either serializing the whole assembly or specific classes only.
  • Pass the resulting .xsd file through SVCUtil.exe in order to produce a file containing all the datacontracts.

Points of Interest

  1. When running the XSD tool, an error similar to the one shown below may appear.
    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.

    It appears because xsd.exe is unable to serialize interfaces. This was resolved by modifying the T4 templates and adding the [System.Xml.Serialization.XmlIgnore] over the property causing the error. It is also possible to resolve this error by changing the ICollection to a List.

  2. The datacontracts.cs file contains empty classes or classes which should not be served as datacontracts.

    It is possible to generate an xsd file containing specific classes to be converted to datacontracts, and leaving out the ones which are not to be exposed to a consumer.

    In the Bin\Debug folder of the Models project, enter the following command:

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

    Image 16

    In the above example, only the User, UserGroup, Group, Permission, GroupPermission classes are to be serialized to the Schema0.xsd file. Other classes in the models.dll such as UsersContext.cs are excluded.

Conclusion

The article demonstrated how it is possible to convert a class library of POCO classes to DataContracts by converting the .dll for the class library project to a .xsd file, then using the SVCUtil.exe tool to create a DataContracts file. The focus of the article has been to demonstrate how this can be achieved for POCO classes produced from using the EF Power tool by directly manipulating the T4 template which produces the classes to make the classes more suitable for serialization. The article also briefly explained how to create datacontracts for a class library of POCOs which were not produced using the EF Power tool.

History

  • v 1.0 - 12:25GMT 2016-02-26

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
Kuv Patel is a Senior Software Developer and Solutions Architect, specializing in .NET and Javascript frameworks development using MVC, ASP.NET, C#, WCF, Web API, Entity Framework, React, Angular.

Comments and Discussions

 
-- There are no messages in this forum --