(ASP.NET) ASP.NET (2008 год)

XML-коллектор параметров ASP2-форм & XSLT-генератор динамических SQL.

На этой страничке я расскажу об одной из своих любимых технологий, которые не очень распространены, но которые я с успехом применяю на протяжении ряда лет. Фактически, я опишу здесь некий макропроцессор над SQL-запросами, встроенный в ASP2 и построенный на стандартных XSLT-шаблонах.


Когда-то я писал многокритериальные поисковые запросы ВОТ ТАК: многокритериальные сортировки - развешиваением огромных гирлянд в ORDER BY и с огромными гирляндами WHERE - пример на той же страничке пониже, строка 443. У меня на хомячке буквально горы моего кода, который я приводил для демонстрации разнообразных концепций.

В некоторых проектах у меня были такие огромные и длинные SQL-запросы - что ввести их вручную было просто невозможно и я писал некие препроцессоры (обработчки текста) - результатом работы которых был собственно итоговой оператор SQL немерянной длины. Например в самом конце этой странички я выкладывал фрагмент такого формирователя оператора SQL.

Этот подход хорош, конечно, полным использованием возможностей предкомпилированных планов запросов в SQL - но как быть, когда количество параметров в WHERE становится просто астрономическим и кроме того, заказчики меняют его без конца?

Тогда такой подход уже не годится - нужна более гибкая схема. Позволяющаяя менять эти параметры пачками. Кроме того, бывают ситуации, когда несколько подобных форм (обычно это поисковые формы) формируют весьма похожие запросы в SQL. Вот, например, пять таких похожих поисковых форм одного проекта (по анкетам , по объявлениям , по фотоальбомам , по рассказам , по видеороликам ). Причем по мере развития проекта, этих поисковых форм может стать и двадцать пять. Кроме того, параметры поисков (как впрочем и параметры регистрации) - заказчики тоже меняют чуть ли не каждый день. В такой ситуации полагаться на фиксированные предкомпилированные SQL-планы уже нереально, и составлять огромные SQL-процедуры тоже бессмысленно - придется надеяться только на SQL-кеширование.

Итак, на этой страничке я опишу одну свою технику, с помощью которой я справляюсь с подобными задачами.


Ясно, что требуемую гибкость не достичь с помощью каких-то реляционных моделей или типизированных коллекций. Хотя у меня на сайте лежит один пример такого менеджера классификаторов на базе реляционной модели, где поисковые параметры вообще формируются оператором (как собственно и сами параметры-классифкаторы товарных позиций). Но во втором подобном менеджере классифкаторов, котороый я делал позже - я уже отказался от реляционных моделей. Требуемую гибкость в сочетании с простотой дают только XML-модели. XML я с давних пор активно юзаю, и у меня на сайте есть и множество конкретных примеров использования XML и несколько описаний общих обзоров.

И эта задача для весьма гибкого формирования поисковых запросов со множество подобных и меняющихся форм - идеально подходит для применения XML. Однако в этом деле есть несколько ньюансов, на которых мы остановимся.

Итак, в первую очередь нам надо собрать все параметры с формы. С учетом конкретных выбранных юзером значений и умолчаний для каждой позиции выбора:





Как вы видите, этот сформированный XML даже несколько напоминает коллекцию Request.Forms, одако содержит и GET-параметры запроса странички и главное - параметры умолчания для каждого выбора пользователя, которые нам позволяют понять, что это выбор ПРОПУЩЕН и не участвует в формировании SQL-запроса. Это важный элемент гибкости - иначе нам бы пришлось формировать в WHERE десятки погашающих условия флагов - так как это было описано выше.

Мы могли бы даже проверить построенную нами XML на корректность данной схеме (как например я делал в этом проекте) - если бы не были уверены, что построили ее сами и корректно вот этим кодом:

00001: Imports Microsoft.VisualBasic
00002: 
00003: Public Class ParseFindParm
00004: 
00005: #Region "Анализ формы поиска и построение XML с параметрами поиска"
00006: 
00007:     Dim XMLNameSpace As String
00008:     Dim FindParm As New System.Xml.XmlDocument
00009:     ''' <summary>
00010:     ''' При создании класса соберем с формы все значения текстбоксов, чекбоксов и комбобоксов в XML 
00011:     ''' и параметров запроса формы (для пейждера)
00012:     ''' </summary>
00013:     Public Sub New(ByVal FromForm As System.Web.UI.Page)
00014:         XMLNameSpace = FromForm.GetType.FullName
00015:         Dim Root As System.Xml.XmlElement = FindParm.CreateElement("FindParm", XMLNameSpace)
00016:         For Each OneKey As String In FromForm.Request.QueryString.AllKeys
00017:             Dim OneAttribute As System.Xml.XmlAttribute = FindParm.CreateAttribute(OneKey)
00018:             OneAttribute.Value = FromForm.Request.QueryString(OneKey)
00019:             Root.Attributes.Append(OneAttribute)
00020:         Next
00021:         FindParm.AppendChild(Root)
00022:         For Each M1_Master As Control In FromForm.Controls    'Me.Controls=M1.Master - это корень, в котором Head и проч.
00023:             For Each M1_Master_Conntrol As Control In M1_Master.Controls(3).Controls 'это внутренность PlaceHolder'а
00024:                 If M1_Master_Conntrol.GetType.FullName = "System.Web.UI.WebControls.ContentPlaceHolder" Then
00025:                     For Each X As Control In M1_Master_Conntrol.Controls
00026:                         If X.GetType.FullName = "System.Web.UI.WebControls.DropDownList" Then
00027:                             'формируем <cbOrder SelectedValue="0" Text="0" ToolTip="Порядок" xmlns="System.Web.UI.WebControls.DropDownList" />
00028:                             Dim Drop1 As System.Web.UI.WebControls.DropDownList = CType(X, System.Web.UI.WebControls.DropDownList)
00029:                             'пространство имен xmlns="System.Web.UI.WebControls.DropDownList" все равно все равно всегде присутсвует в элементе - поэтому выносить его в атрибут нет смысла
00030:                             Dim OneDropNode As System.Xml.XmlNode = FindParm.CreateNode(System.Xml.XmlNodeType.Element, Drop1.ID, XMLNameSpace)
00031:                             Dim ValueAttribute As System.Xml.XmlAttribute = FindParm.CreateAttribute("Value")
00032:                             Dim ToolTipAttribute As System.Xml.XmlAttribute = FindParm.CreateAttribute("ToolTip")
00033:                             Dim DefaultValueAttribute As System.Xml.XmlAttribute = FindParm.CreateAttribute("DefaultValue")
00034:                             Dim WebControlAttribute As System.Xml.XmlAttribute = FindParm.CreateAttribute("WebControl")
00035:                             If Drop1.SelectedItem IsNot Nothing Then
00036:                                 ValueAttribute.Value = Drop1.SelectedValue
00037:                             Else
00038:                                 ValueAttribute.Value = Drop1.Text
00039:                             End If
00040:                             ToolTipAttribute.Value = Drop1.ToolTip
00041:                             DefaultValueAttribute.Value = Drop1.Attributes("DefaultValue")
00042:                             WebControlAttribute.Value = X.GetType.FullName
00043:                             OneDropNode.Attributes.Append(ValueAttribute)
00044:                             OneDropNode.Attributes.Append(ToolTipAttribute)
00045:                             OneDropNode.Attributes.Append(DefaultValueAttribute)
00046:                             OneDropNode.Attributes.Append(WebControlAttribute)
00047:                             Root.AppendChild(OneDropNode)
00048:                         End If
00049:                         If X.GetType.FullName = "System.Web.UI.WebControls.TextBox" Then
00050:                             'формируем <TextBox3 Text="" ToolTip="Ник содержит" xmlns="System.Web.UI.WebControls.TextBox" />
00051:                             Dim Text1 As System.Web.UI.WebControls.TextBox = CType(X, System.Web.UI.WebControls.TextBox)
00052:                             Dim OneTextNode As System.Xml.XmlNode = FindParm.CreateNode(System.Xml.XmlNodeType.Element, Text1.ID, XMLNameSpace)
00053:                             Dim TextAttribute As System.Xml.XmlAttribute = FindParm.CreateAttribute("Text")
00054:                             Dim ToolTipAttribute As System.Xml.XmlAttribute = FindParm.CreateAttribute("ToolTip")
00055:                             Dim DefaultValueAttribute As System.Xml.XmlAttribute = FindParm.CreateAttribute("DefaultValue")
00056:                             Dim WebControlAttribute As System.Xml.XmlAttribute = FindParm.CreateAttribute("WebControl")
00057:                             TextAttribute.Value = Text1.Text
00058:                             ToolTipAttribute.Value = Text1.ToolTip
00059:                             DefaultValueAttribute.Value = Text1.Attributes("DefaultValue")
00060:                             WebControlAttribute.Value = X.GetType.FullName
00061:                             OneTextNode.Attributes.Append(TextAttribute)
00062:                             OneTextNode.Attributes.Append(ToolTipAttribute)
00063:                             OneTextNode.Attributes.Append(DefaultValueAttribute)
00064:                             OneTextNode.Attributes.Append(WebControlAttribute)
00065:                             Root.AppendChild(OneTextNode)
00066:                         End If
00067:                         If X.GetType.FullName = "System.Web.UI.WebControls.CheckBox" Then
00068:                             'формируем <OnlyFoto ControlType="" Checked="True" Text="Только с фото" ToolTip="" xmlns="System.Web.UI.WebControls.CheckBox" />
00069:                             Dim Check1 As System.Web.UI.WebControls.CheckBox = CType(X, System.Web.UI.WebControls.CheckBox)
00070:                             Dim OneCheckNode As System.Xml.XmlNode = FindParm.CreateNode(System.Xml.XmlNodeType.Element, Check1.ID, XMLNameSpace)
00071:                             Dim CheckedAttribute As System.Xml.XmlAttribute = FindParm.CreateAttribute("Checked")
00072:                             Dim TextAttribute As System.Xml.XmlAttribute = FindParm.CreateAttribute("Text")
00073:                             Dim ToolTipAttribute As System.Xml.XmlAttribute = FindParm.CreateAttribute("ToolTip")
00074:                             Dim DefaultValueAttribute As System.Xml.XmlAttribute = FindParm.CreateAttribute("DefaultValue")
00075:                             Dim WebControlAttribute As System.Xml.XmlAttribute = FindParm.CreateAttribute("WebControl")
00076:                             CheckedAttribute.Value = Check1.Checked
00077:                             TextAttribute.Value = Check1.Text
00078:                             ToolTipAttribute.Value = Check1.ToolTip
00079:                             DefaultValueAttribute.Value = Check1.Attributes("DefaultValue")
00080:                             WebControlAttribute.Value = X.GetType.FullName
00081:                             OneCheckNode.Attributes.Append(CheckedAttribute)
00082:                             OneCheckNode.Attributes.Append(TextAttribute)
00083:                             OneCheckNode.Attributes.Append(ToolTipAttribute)
00084:                             OneCheckNode.Attributes.Append(DefaultValueAttribute)
00085:                             OneCheckNode.Attributes.Append(WebControlAttribute)
00086:                             Root.AppendChild(OneCheckNode)
00087:                         End If
00088:                     Next
00089:                 End If
00090:             Next
00091:         Next
00092:     End Sub
00093: 
00094:     ''' <summary>
00095:     ''' Сформированный XML будет виден тут
00096:     ''' </summary>
00097:     Public Overrides Function ToString() As String
00098:         Return FindParm.OuterXml
00099:     End Function
00100: #End Region

Итак, мы имеем корректный XML и хотим с его помощью построить SQL-запрос. Какую же технологию нам применить? Задумываться тут нечего - у меня на сайте десятки примеров XSLT-преобразований.


Но сначала нам надо выявить какие из параметров поисковой формы НЕ ЗАДЕЙСТВОВАНЫ юзером для запроса. Те сохранили свои изначальные умолчания. Для этого мы вводим на формах дополнительные атрибуты к стандартным атрибутам контролов. Собственно же семантику для построения запросов мы могли ы отгребать из имен контролов и из еще одного дополнительного атрибута, но я предпочитаю это делать по ToolTip'ам. В итоге входные данные для нашего парсера выглядят это в исходном тексте примерно так:

......
00201:                 <asp:DropDownList ID="cbMonthTo" runat="server" Font-Size="Small" Width="80px" ToolTip="Месяц (до)"  DefaultValue="12">
00202:                     <asp:ListItem Value="1">январь</asp:ListItem>
00203:                     <asp:ListItem Value="2">февраль</asp:ListItem>
00204:                     <asp:ListItem Value="3">март</asp:ListItem>
00205:                     <asp:ListItem Value="4">апрель</asp:ListItem>
00206:                     <asp:ListItem Value="5">май</asp:ListItem>
00207:                     <asp:ListItem Value="6">июнь</asp:ListItem>
00208:                     <asp:ListItem Value="7">июль</asp:ListItem>
00209:                     <asp:ListItem Value="8">август</asp:ListItem>
00210:                     <asp:ListItem Value="9">сентябрь</asp:ListItem>
00211:                     <asp:ListItem Value="10">октябрь</asp:ListItem>
00212:                     <asp:ListItem Value="11">ноябрь</asp:ListItem>
00213:                     <asp:ListItem Value="12" Selected="True">декабрь</asp:ListItem>
00214:                 </asp:DropDownList>
00215:                 <asp:DropDownList ID="cbYearTo" runat="server" Width="60px" ToolTip="Год (до)" DefaultValue="2010">
00216:                     <asp:ListItem>2008</asp:ListItem>
00217:                     <asp:ListItem>2009</asp:ListItem>
00218:                     <asp:ListItem Selected="True">2010</asp:ListItem>
00219:                 </asp:DropDownList>
.....

Итого, давайте подведем промежуточные итоги - чего мы добились с помощью такой технологии. Мы вынесли всю гибкость и модифицируемость нескольких подобных форм из алгоритмов в данные - в единый XSLT-файлик с описанием процесса получения SQL-запроса. С другой стороны наш движок, производящий XML, работает тоже по исходным текстовым файлам страничек, куда нам тоже несложно просто редактированием текстов вносить любые изменения - при этом для любых модификациях поисковых параметров форм НЕ ИЗМЕНЯЕТСЯ НИ ЕДИНАЯ СТРОЧКА ПРОГРАММНОГО КОДА!

Собственно говоря - вызов нашего класса парсинга формы и построения SQL на форме выглядит вот так:



Ни этот код на форме, ни код класса НИКОГДА уже не будет меняться - ни при изменениях постановки задачи по полям отборов, ни при изменениях структуры базы. Вся изменчивость у нас вынесена фактически в несколько XSLT-файликов, которым скармливается на вход разметка формы - а на выходе мы получаем готовый SQL-запрос.

Фишка тут также в том, что поскольку поддерживается пейджинг, то запросов должно быть ДВА - первых просто дает количество записей в отборе по базе и его результат участвует просто в формировании пейджера, а второй запрос - это собственно пейждинговый запрос, который формирует рекордсет, выводимый на форму

Первый формирователь SQL (для запроса на количество) - выглядит примерно вот так:



Этот файл формирования SQL-запроса достаточно длинный (и на момент написания этой заметки - я даже не изготовил его полностью для данного проекта) - но смысл должен быть понятен:

Ну а собственно пейжинговый запрос за данными выглядит вот так: (начало)

00001: <?xml version="1.0" encoding="utf-8"?>
00002: 
00003: <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
00004:                 xmlns:anketa="ASP.find_anketa_aspx"
00005:                 xmlns:foto="ASP.find_foto_aspx"
00006:                 xmlns:video="ASP.find_video_aspx"
00007:                 xmlns:story="ASP.find_story_aspx"
00008:                 xmlns:poputi="ASP.find_poputi_aspx">
00009:     <xsl:output method="text"/>
00010: 
00011:     <xsl:template match="/poputi:*"  >
00012: 
00013:         <xsl:param name="PageSize">
00014:             <xsl:value-of select="10"/>
00015:         </xsl:param>
00016:         <xsl:param name="PageNum">
00017:             <xsl:value-of select="0"/>
00018:         </xsl:param>
00019:         <!-- это для автономной отладки запроса минуя exec sp_executesql -->
00020:         <!--declare @PageSize int
00021:         declare @PageNum int
00022:         select @PageSize=<xsl:value-of select="$PageSize"/>
00023:         select @PageNum=<xsl:value-of select="$PageNum"/>
00024:         ;-->
00025:         WITH All_Users as
00026:         (
00027:         SELECT ROW_NUMBER() OVER (order by UserName) as [ROW_NUMBER], Poputi.* from Poputi
00028:         WHERE (1=1)
00029:         <xsl:apply-templates />                                             <!-- так отгребаются все узлы -->
00030:         <!--<xsl:apply-templates  select="//*[@DefaultValue!=@Value]" />--> <!-- а так только те, где юзер установил параметры на форме -->
00031:         )
00032:         SELECT * from All_Users
00033:         WHERE [ROW_NUMBER]>ISNULL(@PageSize,1)*ISNULL(@PageNum,0) and [ROW_NUMBER]&lt;=ISNULL(@PageSize,1)*ISNULL(@PageNum+1,1000)
00034:         ;
00035:     </xsl:template>
00036:     <xsl:template  match="/poputi:FindParm/poputi:cbCity"  >
00037:         <xsl:if test="@Value!=''" >
00038:             <xsl:if test="@DefaultValue!=@Value" >
00039:                 and City_Name='<xsl:value-of select="@Value" />' -- <xsl:value-of select="@ToolTip" />
00040:             </xsl:if>
00041:         </xsl:if>
00042:     </xsl:template>
00043:     <xsl:template match="/poputi:FindParm/poputi:cbFrom" >
00044:         <xsl:if test="@Value!=''" >
00045:             <xsl:if test="@DefaultValue!=@Value" >
00046:                 and Country_Name='<xsl:value-of select="@Value" />' -- <xsl:value-of select="@ToolTip" />
00047:             </xsl:if>
00048:         </xsl:if>
00049:     </xsl:template>

Ну и теперь остаток моего класса-формирователя SQL (который мы вызывали с формы выше):

00102: #Region "Построение SQL-запросов на количество записей и собственно на отбор юзеров по заданному XML с параметрами запроса"
00103: 
00104:     ''' <summary>
00105:     ''' XSLT-преобразованием сформируем SQL-запрос
00106:     ''' На всякий случай XSLT-преобразование догружает для формирования SQL-запроса текущие значения параметров PageSize и PageNum
00107:     ''' </summary>
00108:     Private Sub BuildSelect(ByVal XSLT_FileName As String)
00109:         Dim XsltFile As String = HttpContext.Current.Server.MapPath(XSLT_FileName)
00110:         Dim XSLT As New System.Xml.Xsl.XslCompiledTransform
00111:         Try
00112:             XSLT.Load(XsltFile)
00113:         Catch ex As System.IO.FileNotFoundException
00114:             Throw New Exception("Нету файла: " & XsltFile)
00115:         Catch ex As System.Xml.Xsl.XsltCompileException
00116:             Throw New Exception("Ошибка XSLT-компиляции в: " & XsltFile)
00117:         Catch ex As System.Xml.Xsl.XsltException
00118:             Throw New Exception("Ошибка XSLT-преобразования в: " & XsltFile)
00119:         End Try
00120:         XSLT_Result = New System.Text.StringBuilder
00121:         Dim Writer As New System.IO.StringWriter(XSLT_Result)
00122:         Dim XSLT_Parm As New System.Xml.Xsl.XsltArgumentList
00123:         XSLT_Parm.AddParam("PageSize", XMLNameSpace, _PageSize)
00124:         XSLT_Parm.AddParam("PageNum", XMLNameSpace, _PageNum)
00125:         XSLT.Transform(FindParm, XSLT_Parm, Writer)
00126:     End Sub
00127: 
00128:     Dim XSLT_Result As System.Text.StringBuilder
00129:     ''' <summary>
00130:     ''' Это свойство вернет сформированный XSLT-преобразованием оператор SQL
00131:     ''' </summary>
00132:     Public ReadOnly Property XSLT_Generated_SQL_Statement()
00133:         Get
00134:             Return XSLT_Result.ToString
00135:         End Get
00136:     End Property
00137: 
00138: #End Region
00139: 
00140: #Region "Предварительный подсчет количества записей"
00141: 
00142:     Dim _Total_Rows As Integer
00143:     ''' <summary>
00144:     ''' Суммарное количество записей в отборе для пейджера, которые вернул предварительный запрос
00145:     ''' </summary>
00146:     Public ReadOnly Property Total_rows() As Integer
00147:         Get
00148:             Return _Total_Rows
00149:         End Get
00150:     End Property
00151: 
00152:     ''' <summary>
00153:     ''' Сформированная команда подсчета количества записей
00154:     ''' </summary>
00155:     Public ReadOnly Property CountCMD() As String
00156:         Get
00157:             Return _CountCMD.SqlCMD.CommandText
00158:         End Get
00159:     End Property
00160: 
00161:     Dim _CountCMD As ExecSQL_RDR
00162:     ''' <summary>
00163:     ''' Получить счетчик отобранных запросом записей для пейджера
00164:     ''' Эта функция выполнит установленный заданный в XSLT-файле оператор SQL
00165:     ''' И вычитает из ного ОДНО значение из нулевой строки и поля ROW_COUNT, которое будет считатся общем количеством записей
00166:     ''' </summary>
00167:     Public Function GetCount(ByVal XSLT_FileName As String) As Integer
00168:         BuildSelect(XSLT_FileName)
00169:         _CountCMD = New ExecSQL_RDR()
00170:         Dim RDR As Data.SqlClient.SqlDataReader = _CountCMD.ExecSQL(XSLT_Result.ToString, Data.CommandType.Text, Data.CommandBehavior.SingleRow)
00171:         If RDR.Read Then
00172:             _Total_Rows = RDR("ROW_COUNT")
00173:         End If
00174:         _CountCMD.Close()
00175:         Return _Total_Rows
00176:     End Function
00177: 
00178: #End Region
00179: 
00180: #Region "Отбор заказанных юзеров в DataTable c указанием конкретной страницы отбора"
00181: 
00182:     Dim _PageSize As Integer = 10
00183:     ''' <summary>
00184:     ''' Размер страницы пейджинга
00185:     ''' </summary>
00186:     Public Property PageSize() As Integer
00187:         Get
00188:             Return _PageSize
00189:         End Get
00190:         Set(ByVal value As Integer)
00191:             _PageSize = value
00192:         End Set
00193:     End Property
00194: 
00195:     Dim _PageNum As Integer = 0
00196:     ''' <summary>
00197:     ''' Текущая запрашиваемая страница
00198:     ''' </summary>
00199:     Public Property PageNum() As Integer
00200:         Get
00201:             Return _PageNum
00202:         End Get
00203:         Set(ByVal value As Integer)
00204:             _PageNum = value
00205:         End Set
00206:     End Property
00207: 
00208:     Dim CMD As ExecSQL_RDR
00209:     ''' <summary>
00210:     ''' Итоговая команда, которую скармливаем SQL-серверу после всего
00211:     ''' </summary>
00212:     Public ReadOnly Property SelectCMD() As String
00213:         Get
00214:             Return CMD.SqlCMD.CommandText
00215:         End Get
00216:     End Property
00217: 
00218: 
00219:     ''' <summary>
00220:     ''' Итог: получить заданную страницу отбора юзеров
00221:     ''' Эта функция выполнит указанный в XSLT-файле SQL-запрос, 
00222:     ''' Передав ему параметрами текущие значения свойств класса PageSize и PageNum
00223:     ''' И вернет DataTable с именем OnePageOfSelectedUsers с отбором юзеров из базы
00224:     ''' </summary>
00225:     Public Function GetUsers(ByVal XSLT_FileName As String) As Data.DataTable
00226:         BuildSelect(XSLT_FileName)
00227:         Dim DT As New Data.DataTable("OnePageOfSelectedUsers")
00228:         CMD = New ExecSQL_RDR("@PageSize", _PageSize, "@PageNum", _PageNum)
00229:         Dim RDR As Data.SqlClient.SqlDataReader = CMD.ExecSQL(XSLT_Result.ToString, Data.CommandType.Text, Data.CommandBehavior.CloseConnection)
00230:         DT.Load(RDR)
00231:         CMD.Close()
00232:         Return DT
00233:     End Function
00234: #End Region
00235: 
00236: End Class

Тут используется пара моих собственных библиотечных функций для обращения к ADO.NET - хотя по большому счету он не нужет. Фактически эта описанная мною шаблонная надстройка над SQL является альтернативой ADO.NET-параметризации и параметризации запросов хранимыми процедурами SQL и уж тем более LINQ-расширениям NET-языков.. Мы просто опираемся на внутреннее кеширование SQL и строим SQL-запросы динамически, сами - причем cуществующими стандартными технологиями - XSLT-шаблонами. И вы видите, насколько это более легкое и гибкое решение, чем LINQ-идиотизм!

В шаблоне вы там видите закоментированную заглушку, которая позволяет обходится без параметров в sp_executeSQL, а уложить параметры непосредственно в SQL-запрос. Собственно XSLT-шаблон для этого и принимает параметры. Просто я не воспользовался ими в шаблоне - в данном проекте запросы просты и примитивны - а взял параметры SQL-запроса уже в sp_executeSQL.

Но такая ситуация бывает редко - например нам нужно при различных выборах на форме джойнить или две таблицы или десять - и описанная мною технология с успехом справляется с этой задачей, даже когда заказчики на ходу меняют структуру базы, контролы на форме для запросов. И админ сайта тоже может все менять на ходу - лишь меняя разметку страницы и XSLT-шаблон.


При желании, можете считать это очередным моим ответом тупоголовости MS, ее идиотизму как со строгой типизацией, так и с LINQ, а также технологиям классических BLL. Впрочем, MS так швыряет из стороны сторону, что даже КПСС отдыхает. То у них была суперфинишной идеология с переработками ЧИСТО исходных текстов - простая ASP. Потом вдруг, текстовые технологии, куда нас заводили много лет, вдруг у MS оказали ацтойными, и оказалось вдруг, что хорошо, наоборот, когда HTML-разметка откомпилирована в DLL, а не лежит в исходниках. И все аргументы насчет удобства XHTML, XML и прочих чисто текстовых технологий оказались вдруг забытыми. И даже то, что каждая девочка в простом ASP безо всякой CMS могла что угодно подкорректировать. Это вдруг тоже оказалось забытым. Вдруг стали актуальными не только html, упакованные в DLL, но и сверх-строгие типизации и дженерики, которыми нам парили мозги несколько лет. Умалчивая, правда, что самый распространенный на планете язык - JavaScript, который работает в каждом браузере - никаких типизаций не поддерживает. Не говоря уж про LISP и множество прочих языков. Но вот очередной пьяный качок MS уже в противоположную сторону - теперь строжайшая типизация опять ацтой - да здравствуют переменные VAR из LINQ-расширений! Круг MS-идиотизма обязан замкнутся - в следующем релизе ASP.NET html-разметка опять по идее должна быть строго исходными текстиками. Так и будет крутить нас MS из одной стороны в другую - набивая нам головы дерьмом, а себе карманы - деньгами!


PS.

В ходе обсуждения этой моей концепции на различных форумах - многие товариши напирали на отсутствие проверки корректности ТЕЛА формируемых XSLT-выражений на этапе компиляции. Господа Креш, Нахлобуч, ЗЫ и другие - я ведь предупреждал вас, что чтение на ночь микрософтовских учебников (и особенно Фаулера) - приводит к обширным поражениям мозга. Попробуйте асилить такую мысль - СТРАНИЧКА САЙТА ВЫСТУПАЕТ КОМПЛЯТОРОМ этих выражений. Те админ сайта (или же девелопер на завершающих этапах девелопмента) вводит SQL-выражение, ИСПОЛНЯЕТ ЕГО САЙТОМ - если плохо - корректирует это выражение, нажимает назад в браузере - и новое откорректированное XSLT-выражение исполняется ЕЩЕ РАЗ. Вот я намеренно упустил в выражении закрывающую кавычку - понимаете, в чем отличия этих технологий?


Comments ( )
Link to this page: //www.vb-net.com/asp2/33/index.htm
< THANKS ME>