SchemaImporterExtension – Använd samma klasser i klienten som på WebService servern

Har du någon gång svurit över att du inte kan använda samma klasser i din klient applikation som på servern när du använder WebServices. Det har jag!

När man använder WebServices för att kommunicera mellan en klient och en server som båda är del av samma projekt så känns det väldigt märkligt att man inte kan ha samma klasser på båda sidorna. När man skapar en WebReference så skapas nya klasser som ligger i en separat namnrymd och innehåller endast properties för den serialiserade datan. Anledningen att det är på detta sättet är för att skapa lösa kokpplingar mellan systemen. Men i många situationer så har man ändå en stark koppling mellan klient och server så hur kan man enklast återanvända klasserna mellan klient och server.

Lösningen är att använda en ny klass i .NET Framework 2.0 som heter SchemaImporterExtension. Genom att skriva en egen SchemaImporterExtension och registrera denna för WSDL.exe så kan man påverka vilken kod som genereras av WSDL.exe verktyget och Add WebReference i Visual Studio. Koden nedan är ett exempel på en SchemaImporterExtension som kommer att återanvända alla typer från assembly:et Common när WebService proxyn skapas.

Imports System
Imports System.CodeDom
Imports System.CodeDom.Compiler
Imports System.Xml.Serialization
Imports System.Xml.Serialization.Advanced
Imports System.Xml.Schema
Imports System.Reflection

Public Class SwebusExpressSchemaImporterExtension
    Inherits SchemaImporterExtension

    Private nameSpaceImported As Boolean

    Public Overrides Function ImportSchemaType(ByVal name As String, ByVal ns As String, ByVal context As System.Xml.Schema.XmlSchemaObject, ByVal schemas As System.Xml.Serialization.XmlSchemas, ByVal importer As System.Xml.Serialization.XmlSchemaImporter, ByVal compileUnit As System.CodeDom.CodeCompileUnit, ByVal mainNamespace As System.CodeDom.CodeNamespace, ByVal options As System.Xml.Serialization.CodeGenerationOptions, ByVal codeProvider As System.CodeDom.Compiler.CodeDomProvider) As String
       

        Dim common As Assembly = Assembly.Load("Common")

        For Each t As Type In common.GetTypes()
            If name = t.Name And ns.StartsWith("http://www.mydomain.com/Namespace") Then
                If Not nameSpaceImported Then
                    mainNamespace.Imports.Add(New CodeNamespaceImport("Common"))
                    nameSpaceImported = True
                End If
                Return t.FullName
            End If
        Next

        Return MyBase.ImportSchemaType(name, ns, context, schemas, importer, compileUnit, mainNamespace, options, codeProvider)
    End Function
End Class

Hur funkar det?

  1. Först laddar man in de assembly som man vill återanvända typerna ifrån på klienten
  2. Sen undersöker man om det finns något matchande klassnamn och namespace
  3. Om ingen match anropa basklassen
  4. Om match
    1. Importera klassens namespace som skall återanvändas
    2. Retunera den återanvända klassens hela namn

Svårare än så är det inte! 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s