Generating Interfaces for LINQ to SQL Entities by ThinqLinq

Generating Interfaces for LINQ to SQL Entities

At DevLink I had the pleasure of presenting a session on LINQ to SQL Tricks and Tips. The slides and demos for LINQ to SQL Tricks and Tips are available on my download page if you are interested. Following the session, an attendee voiced the desire for LINQ to SQL to create interfaces while it creates the class definitions in order to make it easier to mock the entities in unit testing scenarios.

As part of the presentation, I showed Damien Guard’s L2ST4 code generation template. The great thing about these templates is that they are fully customizable.  If you’ve been following this blog, you may remember my post showing adding property Get logging to LINQ to SQL with T4. In this post, I’m going to show you how to add the ability to generate and implement interfaces for the table’s columns. I’m only going to show implementing the table’s columns and not the associations. Additionally, you will need to modify this if you use inheritance in your LINQ to SQL models. I’m using the VB Template in this example, but the C# changes are very similar. Hopefully, you will see how easy it is to make these kinds of changes and can add these extensions yourself if necessary.

Ok, so let’s get started. First off, we will set up a flag in the options so that we can toggle creating the interfaces in our code generation. At the top of the file, change the declaration of the options anonymous type adding the CreateInterfaces = true as follows:

var options = new {
	DbmlFileName = Host.TemplateFile.Replace(".tt",".dbml"), // Which DBML file to operate on (same filename as template)
	SerializeDataContractSP1 = false, // Emit SP1 DataContract serializer attributes
	FilePerEntity = false, // Put each class into a separate file
	StoredProcedureConcurrency = false, // Table updates via an SP require @@rowcount to be returned to enable concurrency	
	EntityFilePath = Path.GetDirectoryName(Host.TemplateFile), // Where to put the files	
	CreateInterfaces = true // Add interfaces for each table type
};

Next, we define the interfaces. To make things easier, we’ll just declare the interface for each table just before we define the table itself. This will keep the interfaces in the same namespace and the same file as the tables (if you use the FilePerEntity option). The biggest trick is to figure out where to insert this code.  Search for the following text in the existing template: “if (data.Serialization && class1.IsSerializable) {“. Change the template between the Namespace declaration and the serialization settings as follows:

Namespace <#=data.EntityNamespace#>	

<#		}
#>		

<#
if (options.CreateInterfaces) { #>
	'Interface
	<#=code.Format(class1.TypeAttributes)#>Interface I<#=class1.Name#>
	<#			foreach(Column column in class1.Columns) {#>
	<# if (column.IsReadOnly) {#>ReadOnly <#}#>Property <#=column.Member#> As <#=code.Format(column.Type)#>
	<# } 
	#>
	End Interface
<# } 
#>
<#		if (data.Serialization && class1.IsSerializable) {

Here we create an interface named IClass where Class is the actual name of the class we are going to generate.  Once we have the interface created, we iterate over each of the columns defining the properties that correspond to the table’s columns.

Next, we need to alter the Class definition to have it implement our new interface. Scrolling down about 15 lines, find the line where we declare that the class implements the INotifyPropertyChanging and INotifyPropetyChanged interfaces. Change this line to read as follows:

	Implements INotifyPropertyChanging, INotifyPropertyChanged<#
if (options.CreateInterfaces) {#>, I<#=class1.Name#><#}#>

If we were using C#, our job would be done. However, VB requires that interfaces be implemented explicitly.  Since we are generating this code, making this last change is relatively easy as well. Scroll down to the definition of the property getter and setter (in my copy, this is line 334). Change the property definition to read as follows:

#>		<#=code.Format(column.MemberAttributes)#><# if (column.IsReadOnly) {#>ReadOnly <#}#>Property <#=column.Member#> As <#=code.Format(column.Type)#><# 
			if (options.CreateInterfaces) {#> Implements I<#=class1.Name#>.<#=column.Member#><#}#>

			Get

Ok. We’re done. All we need to do is save our changes to the TT file and it will regenerate the classes from our DBML assuming you have already set your project up to use the TT files rather than the default LINQ to SQL generator.

Posted on - Comment
Categories: LINQ - VB - VB Dev Center -
comments powered by Disqus