Autogenerate DataContracts for WCF from POCO classes

Datetime:2016-08-23 04:35:36          Topic: WCF  DataBase           Share

Introduction

In a previous article titled Use EF Power Tool to generate EF POCO classes a class libary 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 POCO's 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

Convert a class library project containing POCO classes not created using the EF Power tool

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 attitional atrributes
  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.

To summarize the above steps:  we will create an XSD file from a class library dll, then process the XSD file using a utility to produce a .cs file containing the datacontracts.

Thefirst zip file contains a sample VS 2013 Solution which will be used to walkthough this process.  

Thesecond 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 consist 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. 

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:

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.

1. Modify the T4 Template for the EF Power tool and add attitional atrributes

EF Power Tool uses T4 Templates to autogenerate the POCO classes, DBContext, and mappings.  The default autogenerated code in the Models class libary cannot be converted into an xsd file without producing  serialisation 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.

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: Download EF Power Tool .

The Models project will now appear as below:

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

[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

[System.Xml.Serialization.XmlIgnore]

The text will now appear as:

<#
    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 neccessary to generate them again. Right Click on the Models project and select Reverse Engineer Code First as shown below.

Now key in the login details for SQL Server.

Click the Advanced button.

Set the highlighted property to True  and Click the OK button to closed 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:

[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.

[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 serlialzed.  The changes to the T4 templates has targetted 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 ran 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:

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

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.

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

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.  

Key in the following command:

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.

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.  

<!--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.  

[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.

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 a datacontracts.   The above approach can also be used to generate datacontracts for POCO's which were created from the EF designer, by modifying the T4 templates as suggested above and creating an .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 the changing the T4 template to generate POCO's 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, Versio
n=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

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 convertinng the .dll for the class library project to a .xsd file, then using the SVCUtil.exe tool to create a DataConracts file.  The focus of the article has been to demonstrate how this can be achived 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 serialisation.  The article also briefly explained how to create datacontracts for a class library of POCO's which were not produced using the EF Power tool.

History

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





About List