3

我需要针对 xsd 验证 sql server 中的 xml。xsd 太复杂,无法与 XML SCHEMA COLLECTION 一起使用,所以我正在编写一个 SQL CLR 函数来完成它。我对如何“不得不”编写代码有两个问题。代码是针对 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

我确实遵循了一条线索,尝试使用初始阅读器作为第二个阅读器的基础,该阅读器在大致等效的控制台应用程序中工作。控制台应用程序的不同之处在于,第一个准备就绪是来自文件 uri 而不是来自 SqlXml。可悲的是,这在 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

我仍然希望对第二个问题有想法,但我可以回答问题的第一部分。也许我应该把它分成一个单独的问题。

[编辑:也想出了第二个问题。见下文。]

我以某种方式掩盖了一个晦涩的错误:System.InvalidOperationException:无法将一致性检查更改为 Document。确保 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

至于第二个问题,我将实际验证移至第二个朋友班。我仍然保留模式缓存,但可以使用该功能进行基于集合的操作。

我不会将更改发布到主要课程,因为它们非常微不足道,但是新课程:

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 回答