Сборка для работы с данными стандартных ASP.NET-профилей на уровне SQL
Микрасофтавцав иногда пробивает на светлые идеи но, как правило - это остается недоделанными полуфабрикатами. Не исключение - и их идея ASP NET профилей. С одной стороны это достаточно удобно - прописав в конфиг подобное определение получить автоматически создаваемый класс-обертку ProfileCommon и вот такую IntelliSense-подсказку.
Но вся эта идея - класический пример очередного сырья со стороны MS. Все как всегда брошено на полпути. Ведь в базе-то эта информация хранится в весьма крученном виде. А ведь данные профилей ВСЕГДА нужны именно в процедурах.
Но как же выковырнуть на уровне SQL эти данные профиля? Вот например типичная проца - где я на уровне SQL хотел бы увидеть - активирован ли логин. Я вообще не желаю создавать НИКАКИЕ обьекты в базе - пока не увижу, что логин активирован. Хотя бы в силу защиты от DOS-атак. Следовательно - самое естественное место для хранения этого свойства - профиль. А как я при отборе могу увидеть, что профиль активировался?
А никак, пока к недоделанной микрософтовской идее не будет докручен основной компонент - вычитывание данных профилей на уровне SQL. Именно такую важную сборку мы и рассмотрим на этой страничке.
Итак, теперь я опишу этот свой парсер. Я сделал его двухкомпонентным - чтобы работать и с параметрами профилей и с бинарниками на уровне SQL. В принципе он не сложен - но тут требуется корректная и очень кропотливая работа с индексами, памятью, предикатами, буферами и культурами - которая под силу не каждому программисту.
00001: Imports System 00002: Imports System.Data 00003: Imports System.Data.SqlClient 00004: Imports System.Data.SqlTypes 00005: Imports Microsoft.SqlServer.Server 00006: Imports System.Globalization 00007: 00008: Partial Public Class UserDefinedFunctions 00009: 00010: <Microsoft.SqlServer.Server.SqlFunction(DataAccess:=DataAccessKind.Read)> _ 00011: Public Shared Function GetAspBinaryProfileProperty(ByVal UserID As String, ByVal ProfilePropertyName As String) As SqlBytes 00012: Dim Names As String() = Nothing 00013: Dim Buffer As Byte() = Nothing 00014: Using CN As New SqlConnection("context connection=true") 00015: CN.Open() 00016: Dim CMD As New SqlCommand("SELECT PropertyNames, PropertyValuesBinary FROM dbo.aspnet_Profile Where UserID='" & UserID & "'", CN) 00017: Dim RDR As SqlDataReader = CMD.ExecuteReader 00018: If RDR.Read Then 00019: Names = RDR.GetString(0).Split(CChar(":")) 00020: Dim Buflen As Integer = CInt(RDR.GetBytes(1, 0, Nothing, 0, 0)) 00021: Buffer = New Byte(Buflen - 1) {} 00022: RDR.GetBytes(1, 0, Buffer, 0, Buflen) 00023: Dim i As Integer 00024: For i = 0 To CInt((Names.Length / 4) - 1) 00025: Dim OnePropertyName As String = Names((i * 4)) 00026: Dim PropertyStartIndex As Integer = Integer.Parse(Names(((i * 4) + 2)), CultureInfo.InvariantCulture) 00027: Dim PropertyLength As Integer = Integer.Parse(Names(((i * 4) + 3)), CultureInfo.InvariantCulture) 00028: If (OnePropertyName = ProfilePropertyName) Then 00029: If (((Names(((i * 4) + 1)) = "B") AndAlso (PropertyStartIndex >= 0)) AndAlso ((PropertyLength > 0) AndAlso (Buffer.Length >= (PropertyStartIndex + PropertyLength)))) Then 00030: Dim dst() As Byte = New Byte(PropertyLength - 1) {} 00031: System.Buffer.BlockCopy(Buffer, PropertyStartIndex, dst, 0, PropertyLength) 00032: Return New SqlBytes(dst) 00033: End If 00034: End If 00035: Next 00036: End If 00037: End Using 00038: End Function 00039: End Class 00001: Imports System 00002: Imports System.Data 00003: Imports System.Data.SqlClient 00004: Imports System.Data.SqlTypes 00005: Imports Microsoft.SqlServer.Server 00006: Imports System.Globalization 00007: 00008: Partial Public Class UserDefinedFunctions 00009: 00010: <Microsoft.SqlServer.Server.SqlFunction(DataAccess:=DataAccessKind.Read)> _ 00011: Public Shared Function GetAspStringProfileProperty(ByVal UserID As String, ByVal ProfilePropertyName As String) As SqlString 00012: Dim Names As String() = Nothing 00013: Dim Values As String = Nothing 00014: Using CN As New SqlConnection("context connection=true") 00015: CN.Open() 00016: Dim CMD As New SqlCommand("SELECT PropertyNames, PropertyValuesString FROM dbo.aspnet_Profile Where UserID='" & UserID & "'", CN) 00017: Dim RDR As SqlDataReader = CMD.ExecuteReader 00018: If RDR.Read Then 00019: Names = RDR.GetString(0).Split(CChar(":")) 00020: Values = RDR.GetString(1) 00021: Dim i As Integer 00022: For i = 0 To CInt((Names.Length / 4) - 1) 00023: Dim OnePropertyName As String = Names((i * 4)) 00024: Dim PropertyStartIndex As Integer = Integer.Parse(Names(((i * 4) + 2)), CultureInfo.InvariantCulture) 00025: Dim PropertyLength As Integer = Integer.Parse(Names(((i * 4) + 3)), CultureInfo.InvariantCulture) 00026: If (OnePropertyName = ProfilePropertyName) Then 00027: If (((Names(((i * 4) + 1)) = "S") AndAlso (PropertyStartIndex >= 0)) AndAlso ((PropertyLength > 0) AndAlso (Values.Length >= (PropertyStartIndex + PropertyLength)))) Then 00028: Return Values.Substring(PropertyStartIndex, PropertyLength) 00029: End If 00030: End If 00031: Next 00032: End If 00033: End Using 00034: End Function 00035: 00036: End Class
Публикуем сборку в SQL, делаем враппер вокруг сборки, небольшой тестик и прогоняем его. Как видите - теперь MS-идея профилей приобрела требуемую функциональную полноту и мы можем ухватить данные из профилей в SQL-отборы.
В принципе, для самых ленивых - я выкладываю свою сборку здесь в откомпилированном виде.
А может микрософтовцам хватит мозгов ее скопировать у меня с сайта и включить ее в состав SQL-framework? Для них же еще одна подсказка - реже (например для бана юзеров), но все же иногда требуется, представьте себе, и поставить значение какого-то параметра прямо в SQL - не городя вавилонских пирамид по вытягиванию всего этого профильного хозяйства на WEB-сервер джобами. А если этого всего нельзя - то для чего вы делали эти профили?
|