3

xsdに対してSQLサーバーでxmlを検証する必要があります。xsd は複雑すぎて XML SCHEMA COLLECTION で使用できないため、それを行う SQL CLR 関数を作成しています。コードを書く必要があった方法には 2 つの問題があります。コードは 2.0 をターゲットとする VB.NET ですが、C# でも同じ問題が発生すると思います。満足していない場合は、切り替えてください。

[1] SqlXml.CreateReader によって作成されたリーダーに設定を添付できないように見えるため、XmlDocument に読み込んで検証を実行する必要があります。また、SqlXml を直接 XmlDocument にロードすることもできません。追加の型変換を行う必要があります。

私は何かを見逃していますか、それともそのままですか?

[2]共有メンバーを使用してイベント ハンドラーから検証結果を渡し、呼び出し元に戻すのが嫌いです。これは、個々の呼び出しのシーケンスがあることがわかっている最初の特定の使用法では問題ありません。しかし、これを複数の呼び出し元またはセット操作で使用しようとしたことがある場合、結果が疑わしいのではないかと心配しています。

これを回避する方法はありますか?

Imports System.Data.SqlTypes
Imports Microsoft.SqlServer.Server
Imports System.Xml
Imports System.Xml.Schema

Partial Public Class UserDefinedFunctions
  'this is from an ms sample
  Const TARGET_NAMESPACE As String = "http://www.contoso.com/books" 
  Const SCHEMA_URI As String = "D:\temp\temp.xsd"

  Shared schemaSet As XmlSchemaSet
  Shared schemaValidationEventHandler As ValidationEventHandler
  Shared isValid As Boolean
  Shared doc As XmlDocument

  Shared Sub New()
    schemaSet = New XmlSchemaSet
    schemaSet.Add(TARGET_NAMESPACE, SCHEMA_URI)
    schemaSet.Compile()

    doc = New XmlDocument
    doc.Schemas = schemaSet

    schemaValidationEventHandler = New ValidationEventHandler(
                                   AddressOf ValidationCallBack)
  End Sub

  <SqlFunction()> _
  Public Shared Function ValidateWithContosoXsd(ByVal xml As SqlXml) _
                                                         As SqlBoolean
    isValid = True

    Dim reader As XmlReader = xml.CreateReader
    reader.Settings.ValidationType = ValidationType.Schema

    doc.Load(reader)
    doc.Validate(schemaValidationEventHandler)

    ValidateWithContosoXsd = isValid
  End Function

  Private Shared Sub ValidationCallBack(ByVal sender As Object,
                                        ByVal args As ValidationEventArgs)
    isValid = False
  End Sub
End Class

手がかりに従って、最初のリーダーを 2 番目のリーダーの基礎として使用してみました。これは、ほぼ同等のコンソール アプリで動作します。コンソール アプリの違いは、最初の準備が SqlXml ではなくファイル uri から行われることです。悲しいことに、clr で実行すると、これは常に有効と表示されます。

Partial Public Class UserDefinedFunctions

    Const TARGET_NAMESPACE As String = "http://www.contoso.com/books"
    Const SCHEMA_URI As String = "D:\temp\temp.xsd"

    Shared settings As XmlReaderSettings
    Shared schemaSet As XmlSchemaSet
    Shared schemaValidationEventHandler As ValidationEventHandler
    Shared isValid As Boolean

    Shared Sub New()

        schemaSet = New XmlSchemaSet
        schemaSet.Add(TARGET_NAMESPACE, SCHEMA_URI)
        schemaSet.Compile()

        schemaValidationEventHandler = New ValidationEventHandler(AddressOf ValidationCallBack)

        settings = New XmlReaderSettings
        settings.Schemas = schemaSet
        settings.ValidationType = ValidationType.Schema
        AddHandler settings.ValidationEventHandler, schemaValidationEventHandler

    End Sub

    <SqlFunction()> _
    Public Shared Function ValidateWithContosoXsd(ByVal xml As SqlXml) As SqlBoolean

        isValid = True

        Dim baseReader As XmlReader = xml.CreateReader
        Dim reader As XmlReader = XmlReader.Create(baseReader, settings)

        While reader.Read()
        End While

        ValidateWithContosoXsd = isValid

    End Function

    Private Shared Sub ValidationCallBack(ByVal sender As Object, ByVal args As ValidationEventArgs)
        isValid = False
    End Sub
End Class
4

1 に答える 1

2

私はまだ2番目の質問についてのアイデアを望んでいますが、質問の最初の部分に答えることができます. おそらく、それを別の質問に分割する必要があります。

[編集: 2 番目の質問も考え出しました。下記参照。]

System.InvalidOperationException: 適合チェックをドキュメントに変更できません。ラッピング シナリオでは、XmlReaderSettings の ConformanceLevel が Auto に設定されていることを確認します。

それを解決すると、以下の作業コードにつながります。他にもいくつかの変更がありますが、キーは settings.ConformanceLevel = ConformanceLevel.Auto です。

<SqlFunction()> _
Public Shared Function ResetSchema(ByVal targetNameSpace As SqlString, ByVal schemaUri As SqlString) As Boolean
    Dim result As Boolean

    Try
        schemaSet = New XmlSchemaSet
        schemaSet.Add(targetNameSpace, schemaUri)
        schemaSet.Compile()

        settings = New XmlReaderSettings
        settings.Schemas = schemaSet
        settings.ValidationType = ValidationType.Schema
        settings.ConformanceLevel = ConformanceLevel.Auto
        settings.ValidationFlags = settings.ValidationFlags Or XmlSchemaValidationFlags.ReportValidationWarnings
        AddHandler settings.ValidationEventHandler, AddressOf ValidationCallBack

        result = True
    Catch
        'result = False
    End Try

    ResetSchema = result
End Function

Public Shared Function ValidateWithXsd(ByVal xml As SqlXml) As SqlBoolean
    Try

        isValid = True

        Dim reader As XmlReader = xml.CreateReader
        Dim validatingReader As XmlReader = XmlReader.Create(reader, settings)

        While validatingReader.Read
        End While

    Catch ex As Exception
        Throw ex
    Finally
        ValidateWithXsd = isValid
    End Try

End Function

Private Shared Sub ValidationCallBack(ByVal sender As Object, ByVal args As ValidationEventArgs)
    isValid = False
End Sub

2 番目の質問については、実際の検証を 2 番目のフレンド クラスに移動しました。私はまだスキーマをキャッシュしたままにしていますが、関数を使用してセットベースの操作を行うことができます。

プライマリ クラスへの変更は非常に些細なことなので投稿しませんが、新しいクラスは次のようになります。

Friend Class SchemaValidator

Private isValid As Boolean

Friend Function Validate(ByVal baseReader As XmlReader, ByVal settings As XmlReaderSettings) As Boolean
    Try

        isValid = True
        AddHandler settings.ValidationEventHandler, AddressOf ValidationEventHandler
        Dim validatingReader As XmlReader = XmlReader.Create(baseReader, settings)

        While validatingReader.Read
        End While

    Catch ex As Exception
        Throw ex
    Finally
        RemoveHandler settings.ValidationEventHandler, AddressOf ValidationEventHandler
        Validate = isValid
    End Try

End Function

Private Sub ValidationEventHandler(ByVal sender As Object, ByVal args As ValidationEventArgs)
    isValid = False
End Sub
End Class
于 2013-09-13T14:01:10.463 に答える