Fixes to L2S Template to support WCF

Aug 11, 2010 at 6:20 PM

Greetings,

I have just today installed T4Toolbox, and I think it's great.

In order to make this work with WCF Serialization of my L2S objects, I had to make some tweaks, and I'm posting to share those tweaks and maybe they can make it into another future version of T4Toolbox.

What I did, was compare the output of the TT code generation with the "stock" one generated by MSLinqToSQLGenerator.

I found a few differences, and the first obvious one was that there is no [OnDeserializing] in the TT.  I noticed the stock one had a handler and it was calling a function Initialize().  The only other place Initialize() was referenced was in the stock constructor.

So I changed to add this:

[OnDeserializing()]
[EditorBrowsableAttribute(EditorBrowsableState.Never)]
public void OnDeserializing(StreamingContext context)
{
	this.Initialize();
}

Just above where the TT already outputs OnSerializing().

Next I changed the Constructor generator to also call Initialize(), and move all the constructor code to Initialize().

    /// <summary>
    /// Renders constructor of the entity class
    /// </summary>
    private void RenderConstructor()
    {
#>

public <#= this.Type.Name #>()
{
	this.Initialize();
}

private void Initialize()
{
<#+
        foreach (Association association in this.associations)
        {
            if (IsEntityRef(association))
            {
#>
    this.<#= FieldName(association) #> = default(EntityRef<<#= association.Type #>>);
<#+
            }
            else
            {
#>
    this.<#= FieldName(association) #> = new EntitySet<<#= association.Type #>>(this.Attach<#= PropertyName(association) #>, this.Detach<#= PropertyName(association) #>);
<#+
            }
        }

        if (this.tableHasPrimaryKey)
        {
#>
    this.OnCreated();
<#+
        }
#>
}
<#+
    }

Finally, I was getting the dreaded error with Circular Reference during serialization.  So, I changed the RenderDataContractAttribute() function to include the IsReference=true as follows:

    /// <summary>
    /// Renders [DataContract] attribute for the entity class if serialization
    /// option was specified for the Data Context in the LINQ to SQL model.
    /// </summary>
    private void RenderDataContractAttribute()
    {
        if (this.IsDataContract(this.Type))
        {
            this.WriteLine("[DataContract(IsReference=true)]");
            //this.WriteLine("[DataContract]");
        }
    }

Then when I tested, I noticed that my M2M associations were serializing, but my O2O associations weren't.  I fixed this by changing the IsDataMember private function to consider it a DataMember even if it has a foreign key, thusly:

    /// <summary>
    /// Returns true if the specified <paramref name="association"/> should be serialized.
    /// </summary>
    private bool IsDataMember(Association association)
    {
        return this.IsDataContract(this.Type) && //association.IsForeignKey == false &&
            association.AccessModifier == GeneratedTextTransformation.AccessModifier.Public;
    }

 

Now I can serialize my L2S entities and subobjects with WCF.

Hope this helps somebody else out! :)  If somebody notices any problems with these modifications, please post back to this discussion to let me know.

Hugh

 

Coordinator
Aug 18, 2010 at 11:02 PM

Hugh,

Thanks for posting this. I will update the code when I get a chance.

Oleg

Coordinator
Aug 19, 2010 at 12:34 AM

Hugh,

Could you try this build? http://t4toolbox.codeplex.com/releases/view/44623

Thanks,
Oleg

Aug 19, 2010 at 3:15 PM

Hi Oleg, I have switched to Entity Framework 1.0, and I'm now using the TT template found here:

http://blogs.msdn.com/b/dsimmons/archive/2008/10/27/using-t4-templates-to-generate-ef-classes.aspx

I have also made a big change in the way I Serialize Entities.  Now, I only allow 1 object to be serialized at a time, I never serialize sub-objects.

So that is quite different than the solution I posted above, which could potentially serialize your entire object graph.

Best of luck,

Hugh

Jan 12, 2011 at 5:42 PM

I recently tried switching over my project from the standard code generator for L2S entities to the generator supplied in T4 Toolkit, and it appears this change has broken our use of the DataContractJsonSerializer to serialize our object as JSON.

Here's the exception:

System.Runtime.Serialization.SerializationException

The type '[class name]' cannot be serialized to JSON because its IsReference setting is 'True'. The JSON format does not support references because there is no standardized format for representing references. To enable serialization, disable the IsReference setting on the type or an appropriate parent class of the type.

The classes generated by the standard L2S generator have a DataContract attribute, but IsReference is not set to true.

Is there any way to avoid that property being set on the attribute? Thanks!

Jan 12, 2011 at 5:51 PM

Hi Adam,

I have seen that before.

I think I solved it by using a combination of techniques.  1) I used a ViewModel that has no sub objects.  And 2) I used a different Serializer like this:

 

System.Web.Script.Serialization.JavaScriptSerializer ser = new System.Web.Script.Serialization.JavaScriptSerializer();
return ser.Serialize(myObject);

 

Hope that helps!

Hugh

Jan 12, 2011 at 6:04 PM

Writing ViewModels for all of our entity classes that leave out child objects, while it would have been the correct approach to take from the beginning, would be a non-trivial amount of code to write right now.

The JavaScriptSerializer is a deprecated class, and I would rather not use it for that reason, and I don't want to introduce any differences in the generated JSON by changing serializers, as I cannot easily change the consumer.

I may have to switch back to using MSLinqToSqlGenerator, which does not add the IsReference=true on the DataContract attribute, and find a workaround for the issue I switched to T4 Toolbox to fix: MSLinqToSqlGenerator assuming unknown types (like enums) are reference types instead of value types.