Dynamic XSD + Linq

Я заметил, что у меня на сайте опубликованы десятки моих различных SQL-процедур, сделанных на XSLT, различных SQL-CLR-сборок, сделаных на XSLT, всякие хитрые движки на XSLT, многочисленные шлюзы, разбирающие XML, но совсем мало примеров на LINQ. Поэтому я выложу тут еще одну версию той же формы, что я показал выше. Эту форму я переделывал больше десятка раз. Первую версию - на простом классе, когда предполагались четко зафиксированные набор свойств, была фиксированная схема XML и по существу могли меняться только данные (значения XML-атрибутов) - я показал выше. Потом я переделал эту же форму на LINQ - в условиях когда атрибуты, теги и пространства имен были изначально известны и четко определены. Предполагалось, что XML-схема меняться не будет. Это позволило загрузить XSD-схему в каталог проекта Visual Studio, указать директиву Import и работать на LINQ с прекрасной подсказкой (небольшой фрагмент кода из этого варианта я показал выше).

Однако позже выяснилось, что схема может меняться администратором системы. От статической подсказки Visual Studio пришлось отказаться. Пришлось отказаться и от сервиса компилятора VB, который автоматически умеет добавлять пространство имен, указанное в Import. Потом выяснилось, что некие атрибуты у некоторых тегов должны все же быть всегда и админам системы нельзя позволять менять схему произвольно. Потом выяснилось, что и имя схемы может иногда меняться, потом выяснилось что эта же форма должна работать с несколькими разными XML-полями в SQL и так далее и так далее - уже изготовлено более 10 вариантов этой формы.

Поэтому я решил показать тут один промежуточный вариант этой формы на LINQ, в котором начинающие программисты могут увидеть для себя много поучительного. Этот вариант состоит из трех форм и работает с динамически формируемой админом системы XSD-схемой с фиксированным пространством имен, но работающий с несколькими различными XML-полями в базе. При этом пользователи системы могут вносить данные по схеме, определенной админом системы, имя схемы определено статически, но никаких изначальных ограничений на построение админом схемы данных этот вариант кода не предусматривает. Я выбрал для показа на сайте именно этот вариант (из более чем десяти вариантов), потому что именно этот вариант кода наиболее легко гнется в любую сторону.

Итак, у меня в базе создано семь XML-полей с профилями. Админ создает схемы данных, хранящихся в этих профилях, потом юзера вносят данные по этим схемам. Здесь будет рассмотрена лишь часть админа, который имеет возможность создать XML-схемы хранения данных в семи XML-полях MS SQL-сервера, и заполнить данными созданные схемы.

Это можно было бы решить множеством способов, но я предпочитаю всегда наиболее толстый SQL-слой из вохможных, поэтому у меня есть в SQL несколько процедур, одна из которых все читает из SQL, другие сохраняют XML в поля базы (надеюсь вы узнаете дизайнер LINQ)




В проекте существует форма, которая к делу не относится, но позволяет манипулироваться с помощью Querystring("Type") типом SQL-полей. Нужная нам форма SetUserPaymentProfile, которая позволяет произвольно добавлять и убавлять теги в Sequence-последовательностях. В данном случае пока есть только тег A, а теперь c помощью формы SetPaymentProfileTags добавлен тег B и теперь добавлен еще тег C.

Далее c помощью формы SetPaymentProfileSchema мы можем добавить атрибуты к тегам, в которых можно будет укладывать данные. Добавим атрибут C1 (и сразу пропишем в него значение 111) и атрибут С2 и сразу пропишем в него значение 222. В итоге получаем данные чрезвычайно гибкой структуры (практически произвольной), которые укладываются в базу.




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


Тело формы SetUserPaymentProfile, которая отображает итоговой XML и позволяет добавлять/удалять дочерние ноды выглядит вот так (фрагмент):


  15:          <blockquote>
  16:              <table width="100%" style="text-align: left; vertical-align: top;">
  17:                  <tr>
  18:                      <td>
  19:                          <h4>
  20:                              <asp:Label ID="lTitle" runat="server"></asp:Label>
  21:                          </h4>
  22:                      </td>
  23:                  </tr>
  24:                  <tr>
  25:                      <td>
  26:                          <asp:Panel ID="Panel1" runat="server" ScrollBars="Vertical" Height="350">
  27:                              <asp:DataList ID="DataList1" runat="server">
  28:                                  <HeaderTemplate>
  29:                                      <td>
  30:                                      </td>
  31:                                      <td>
  32:                                      </td>
  33:                                      <td>
  34:                                      </td>
  35:                                  </HeaderTemplate>
  36:                                  <ItemTemplate>
  37:                                      <td>
  38:                                          <asp:ImageButton ID="DelImageButton1" runat="server" ImageUrl="~/Images/remove.gif"
  39:                                              OnClick="DelImageButton1_Click" CommandArgument='<%# Databinder.Eval(Container,"ItemIndex") %>' />
  40:                                          <asp:Literal ID="AddAttrLiteral" runat="server"></asp:Literal>
  41:                                      </td>
  42:                                      <td>
  43:                                          <asp:Label ID="tx_Tag_Name" runat="server"></asp:Label>
  44:                                      </td>
  45:                                      <td>
  46:                                          <asp:TextBox ID="tx_Xml" runat="server" Width="550" ReadOnly="true"></asp:TextBox>
  47:                                      </td>
  48:                                  </ItemTemplate>
  49:                              </asp:DataList>
  50:                              <asp:ImageButton ID="RefreshImageButton1" runat="server" ImageUrl="~/Images/ico0016.gif"
  51:                                  OnClick="RefreshImageButton1_Click" />
  52:                              <asp:Literal ID="AddNodesLiteral" runat="server"></asp:Literal>
  53:                          </asp:Panel>
  54:                      </td>
  55:                  </tr>
  56:                  <tr>
  57:                      <td>
  58:                          <asp:Button ID="Button1" runat="server" Text="OK" BackColor="#EF6771" Width="130px" />
  59:                      </td>
  60:                  </tr>
  61:              </table>
  62:              <asp:Label ID="Lerr1" runat="server" ForeColor="Red"></asp:Label>
  63:          </blockquote>
...
   4:  Partial Class SetUserPaymentProfile
   5:      Inherits ProfileBasePage2
   6:   
   7:      Protected Sub SetUserPaymentProfile_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   8:          'Navigator1.NavigateList( "MyRequest", "MyRequestLog.aspx", "Exit", "Logout.ashx")
   9:          '
  10:          Select Case Request.QueryString("Type")
  11:              Case "BankingCards"
  12:                  lTitle.Text = "Банковские карты. Профиль "
  13:              Case "InetMoney"
  14:                  lTitle.Text = "Виртуальные деньги. Профиль "
  15:              Case "HardMoney"
  16:                  lTitle.Text = "Наличные. Профиль "
  17:              Case "Invoice"
  18:                  lTitle.Text = "Инвойс. Профиль "
  19:              Case "BankTransfer"
  20:                  lTitle.Text = "Банковский перевод. Профиль "
  21:              Case "Bonus"
  22:                  lTitle.Text = "Бонусные баллы. Профиль "
  23:              Case "Reserved"
  24:                  lTitle.Text = "Отложенная оплата. Профиль "
  25:              Case "Common"
  26:                  lTitle.Text = "Общий профиль "
  27:          End Select
  28:          '
  29:          AddNodesLiteral.Text = "<a onclick=""window.open('','new','resizable=no,menubar=no,scrollbars=no,width=800,height=500');"" target='new' title='new' href='SetPaymentProfileTags.aspx?type=" & Request.QueryString("Type") & "&id=" & Me.TargetUser.id & "'><img src='Images/ico0015.gif' style='border-width:0'/></a>"
  30:          If Not IsPostBack Then
  31:              Try
  32:                  lTitle.Text &= " " & Me.TargetUserPaymentProfile(0).ProfileName
  33:                  Refresh()
  34:                  '
  35:              Catch ex As Exception
  36:                  DebugLog.TraceTXT(Me.AppRelativeVirtualPath, ex.Message)
  37:                  Lerr1.Text = ex.Message
  38:              End Try
  39:          End If
  40:      End Sub
  41:   
  42:      Protected Sub RefreshImageButton1_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs) Handles RefreshImageButton1.Click
  43:          Refresh()
  44:      End Sub
  45:   
  46:      Sub Refresh()
  47:          Dim Nodes As Collections.Generic.IEnumerable(Of XNode) = WorkingXML.Nodes()
  48:          DataList1.DataSource = Nodes
  49:          DataList1.DataBind()
  50:      End Sub
  51:   
  52:   
  53:      Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
  54:          If Me.CurrentUser Is Nothing Then Exit Sub 'прокисла сессия
  55:          If Me.TargetUser Is Nothing Then Exit Sub 'прокисла сессия
  56:          '
  57:          If Me.CurrentUser.IsAdmin Then
  58:              Try
  59:                  Me.SaveProfile()
  60:                  '
  61:                  Response.Write("<script type='text/javascript' language='javascript'>" & vbCrLf & _
  62:                  "    window.close();" & vbCrLf & _
  63:                  "</script>")
  64:                  Response.End()
  65:                  '
  66:              Catch ex As Exception
  67:                  DebugLog.TraceTXT(Me.AppRelativeVirtualPath, ex.Message)
  68:                  Lerr1.Text = ex.Message
  69:              End Try
  70:          End If
  71:          '
  72:      End Sub
  73:   
  74:      Protected Sub DataList1_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DataListItemEventArgs) Handles DataList1.ItemDataBound
  75:          If e.Item.DataItem IsNot Nothing Then
  76:              Dim OneXmlTag As XElement = CType(e.Item.DataItem, XElement)
  77:              '<b:VISA xmlns:b="http://Airts.vb-net.com/" b:Module="Assist" b:Code="11" b:Comission="22" b:DayLimit="33" b:MounthLimit="44" />
  78:              '
  79:              Dim tx_Tag_Name As Label = CType(e.Item.FindControl("tx_Tag_Name"), Label)
  80:              tx_Tag_Name.Text = OneXmlTag.Name.LocalName
  81:              '
  82:              Dim tx_xml As TextBox = CType(e.Item.FindControl("tx_xml"), TextBox)
  83:              tx_xml.Text = OneXmlTag.ToString
  84:              '
  85:              Dim AddAttrLiteral As Literal = CType(e.Item.FindControl("AddAttrLiteral"), Literal)
  86:              AddAttrLiteral.Text = "<a onclick=""window.open('','edit','resizable=no,menubar=no,scrollbars=no,width=800,height=500');"" target='edit' title='edit' href='SetPaymentProfileSchema.aspx?type=" & Request.QueryString("Type") & "&id=" & Me.TargetUser.id & "&Node=" & OneXmlTag.Name.LocalName & "'><img src='Images/ico0008.gif' style='border-width:0'/></a>"
  87:          End If
  88:      End Sub
  89:   
  90:      Protected Sub DelImageButton1_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs)
  91:          Dim DelImageButton1 As ImageButton = CType(sender, ImageButton)
  92:          Dim BankingCards_xml As Collections.Generic.IEnumerable(Of XElement) = From AllColumn In Me.TargetUserPaymentProfile Select AllColumn.BankingCardsProfile
  93:          If BankingCards_xml(0) IsNot Nothing Then
  94:              Dim Nodes As Collections.Generic.IEnumerable(Of XNode) = BankingCards_xml(0).Nodes()
  95:              Nodes(DelImageButton1.CommandArgument).Remove()
  96:          End If
  97:          Me.TargetUserPaymentProfile(0).BankingCardsProfile = BankingCards_xml(0)
  98:          Refresh()
  99:      End Sub
 100:   
 101:  End Class

Как видите, эта форма не делает почти ничего. Отрисовывает табличку, формирует код для кнопок обновить, добавить и удалить ноды. И обрабатывает эти события. Все остальное вынесено в базовую страничку проекта, которую я опубликовал в разделе Базовые странички. А в этой самой базовой страничке и выполняются все операции LINQ.

Увы, XElement несеализуем, поэтому запихнуть его во вьюстейт не получится по-простому. Поэтому я нашел оптимальное решение - чтобы хранить его в Session, но с разными именами. Тогда множество отдельных форм, открытых админом на экране для разных юзеров - будут работать с разными вариантами рабочего профиля юзера.

Форма SetPaymentProfileTags для манипулирования тегами устроена так же просто. В Диве работает размещена табличка, отображающая последовательность тегов. Теги можно добавлять, убирать и редактировать атрибуты. Это как раз и есть работа с типизированными коллекциями LINQ.


   1:  <%@ Page Language="VB" AutoEventWireup="false" CodeFile="SetPaymentProfileTags.aspx.vb"
   2:      Inherits="SetPaymentProfileTags" EnableEventValidation="false" ValidateRequest="false" %>
   3:   
   4:  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   5:  <html xmlns="http://www.w3.org/1999/xhtml">
   6:  <body style="background-color: #E5E9EC;">
   7:      <form id="form1" runat="server">
   8:      <blockquote>
   9:          <asp:Panel ID="Panel1" runat="server" ScrollBars="Vertical" Height="400">
  10:              <asp:Label ID="lTitle" runat="server" Font-Bold="true"></asp:Label>
  11:              <br />
  12:              <br />
  13:              <asp:DataList ID="DataList1" runat="server" Width="100%" Style="text-align: left;
  14:                  vertical-align: middle;">
  15:                  <ItemTemplate>
  16:                      <tr>
  17:                          <td>
  18:                              <asp:ImageButton ID="DelImageButton1" runat="server" ImageUrl="~/Images/remove.gif"
  19:                                  OnClick="DelImageButton1_Click" CommandArgument='<%# Databinder.Eval(Container,"ItemIndex") %>' />
  20:                          </td>
  21:                          <td>
  22:                              <asp:TextBox ID="tx_Name" runat="server" Text='<%# Eval("Name") %>' Width="300"></asp:TextBox>
  23:                          </td>
  24:                      </tr>
  25:                  </ItemTemplate>
  26:              </asp:DataList>
  27:              <br />
  28:              <table width="100%" style="text-align: left; vertical-align: middle;">
  29:                  <tr>
  30:                      <td>
  31:                          <asp:ImageButton ID="AddImageButton1" runat="server" ImageUrl="~/Images/ico0015.gif" />
  32:                      </td>
  33:                      <td>
  34:                          <asp:TextBox ID="tx_Name" runat="server" Width="300"></asp:TextBox>
  35:                      </td>
  36:                  </tr>
  37:              </table>
  38:          </asp:Panel>
  39:          <br />
  40:          <asp:Button ID="Button1" runat="server" Text="OK" Width="130px" BackColor="#EF6771" /><br />
  41:          <asp:Label ID="Lerr1" runat="server" ForeColor="Red"></asp:Label>
  42:      </blockquote>
  43:      </form>
  44:  </body>
  45:  </html>
...
   2:   
   3:  Partial Class SetPaymentProfileTags
   4:      Inherits ProfileBasePage2
   5:   
   6:      Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   7:          Try
   8:              lTitle.Text = Request.QueryString("Type") & "=> Tags" & " (Профиль " & Me.TargetUserPaymentProfile(0).ProfileName & ")"
   9:              If Not IsPostBack Then
  10:                  refresh()
  11:              End If
  12:          Catch ex As Exception
  13:              DebugLog.TraceTXT(Me.AppRelativeVirtualPath, ex.Message)
  14:              Lerr1.Text = ex.Message.Replace("<", "&lt;").Replace(">", "&gt;")
  15:          End Try
  16:   
  17:      End Sub
  18:   
  19:      Sub refresh()
  20:          If Me.WorkingXML IsNot Nothing Then
  21:              DataList1.DataSource = Me.WorkingXML.Nodes
  22:              DataList1.DataBind()
  23:          End If
  24:      End Sub
  25:   
  26:      Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
  27:          '
  28:          Me.SaveProfile()
  29:          '
  30:          Response.Write("<script type=""text/javascript"" language=""javascript""> " & vbCrLf & _
  31:                         "window.close();" & vbCrLf & _
  32:                         "</script>")
  33:      End Sub
  34:   
  35:      Protected Sub AddImageButton1_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs) Handles AddImageButton1.Click
  36:          Try
  37:              Dim NewNodes As New XElement("{http://Airts.vb-net.com/}" & tx_Name.Text)
  38:              Me.WorkingXML.Add(NewNodes)
  39:              tx_Name.Text = ""
  40:              refresh()
  41:          Catch ex As Exception
  42:              Lerr1.Text = ex.Message
  43:          End Try
  44:   
  45:      End Sub
  46:   
  47:      Protected Sub DelImageButton1_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs)
  48:          Dim DelImageButton1 As ImageButton = CType(sender, ImageButton)
  49:          Try
  50:              If Me.WorkingXML.Nodes IsNot Nothing Then
  51:                  Dim NewNodes As XElement = Me.WorkingXML.Nodes(DelImageButton1.CommandArgument)
  52:                  NewNodes.Remove()
  53:                  tx_Name.Text = ""
  54:              End If
  55:              refresh()
  56:          Catch ex As Exception
  57:              Lerr1.Text = ex.Message
  58:          End Try
  59:      End Sub
  60:  End Class

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


   1:  <%@ Page Language="VB" AutoEventWireup="false" CodeFile="SetPaymentProfileSchema.aspx.vb"
   2:      Inherits="SetPaymentProfileSchema" EnableEventValidation="false" ValidateRequest="false" %>
   3:   
   4:  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   5:  <html xmlns="http://www.w3.org/1999/xhtml">
   6:  <body style="background-color: #E5E9EC;">
   7:      <form id="form1" runat="server">
   8:      <blockquote>
   9:          <asp:Panel ID="Panel1" runat="server" ScrollBars="Vertical" Height="400">
  10:              <asp:Label ID="lTitle" runat="server" Font-Bold="true"></asp:Label>
  11:              <br />
  12:              <br />
  13:              <asp:DataList ID="DataList1" runat="server" Width="100%" Style="text-align: left;
  14:                  vertical-align: middle;">
  15:                  <ItemTemplate>
  16:                      <tr>
  17:                          <td>
  18:                              <asp:ImageButton ID="DelImageButton1" runat="server" ImageUrl="~/Images/remove.gif"
  19:                                  OnClick="DelImageButton1_Click" CommandArgument='<%# Eval("Name") %>' />
  20:                          </td>
  21:                          <td>
  22:                              <asp:TextBox ID="tx_Name" runat="server" Text='<%# Eval("Name") %>' Width="300"></asp:TextBox>
  23:                          </td>
  24:                          <td>
  25:                              <asp:TextBox ID="tx_Value" runat="server" Text='<%# Eval("Value") %>' Width="300"></asp:TextBox>
  26:                          </td>
  27:                      </tr>
  28:                  </ItemTemplate>
  29:              </asp:DataList>
  30:              <br />
  31:              <table width="100%" style="text-align: left; vertical-align: middle;">
  32:                  <tr>
  33:                      <td>
  34:                          <asp:ImageButton ID="AddImageButton1" runat="server" ImageUrl="~/Images/ico0015.gif" />
  35:                      </td>
  36:                      <td>
  37:                          <asp:TextBox ID="tx_Name" runat="server" Width="300"></asp:TextBox>
  38:                      </td>
  39:                      <td>
  40:                          <asp:TextBox ID="tx_Value" runat="server" Width="300"></asp:TextBox>
  41:                      </td>
  42:                  </tr>
  43:              </table>
  44:          </asp:Panel>
  45:          <br />
  46:          <asp:Button ID="Button1" runat="server" Text="OK" Width="130px" BackColor="#EF6771" /><br />
  47:          <asp:Label ID="Lerr1" runat="server" ForeColor="Red"></asp:Label>
  48:      </blockquote>
  49:      </form>
  50:  </body>
  51:  </html>
...
   3:  Partial Class SetPaymentProfileSchema
   4:      Inherits ProfileBasePage2
   5:   
   6:      Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   7:          Try
   8:              lTitle.Text = Request.QueryString("Type") & "=> " & Request.QueryString("Node") & "=> " & "Attributes" & " (Профиль " & Me.TargetUserPaymentProfile(0).ProfileName & ")"
   9:              If Not IsPostBack Then
  10:                  refresh()
  11:              End If
  12:          Catch ex As Exception
  13:              DebugLog.TraceTXT(Me.AppRelativeVirtualPath, ex.Message)
  14:              Lerr1.Text = ex.Message.Replace("<", "&lt;").Replace(">", "&gt;")
  15:          End Try
  16:   
  17:      End Sub
  18:   
  19:      Sub refresh()
  20:          If Me.WorkingXML IsNot Nothing Then
  21:              Dim WorkAttibutes As System.Collections.Generic.IEnumerable(Of XAttribute) = GetWorkingNode(Me.WorkingXML).Attributes
  22:              DataList1.DataSource = WorkAttibutes
  23:              DataList1.DataBind()
  24:          End If
  25:      End Sub
  26:   
  27:      Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
  28:          '
  29:          Me.SaveProfile()
  30:          '
  31:          Response.Write("<script type=""text/javascript"" language=""javascript""> " & vbCrLf & _
  32:                         "window.close();" & vbCrLf & _
  33:                         "</script>")
  34:      End Sub
  35:   
  36:      Protected Sub AddImageButton1_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs) Handles AddImageButton1.Click
  37:          Try
  38:              If Me.WorkingXML IsNot Nothing Then
  39:                  Dim NewAttr As New XAttribute("{http://Airts.vb-net.com/}" & tx_Name.Text.Trim, tx_Value.Text.Trim)
  40:                  GetWorkingNode(Me.WorkingXML).Add(NewAttr)
  41:                  tx_Name.Text = ""
  42:                  tx_Value.Text = ""
  43:              End If
  44:              refresh()
  45:          Catch ex As Exception
  46:              Lerr1.Text = ex.Message
  47:          End Try
  48:   
  49:      End Sub
  50:   
  51:      Protected Sub DelImageButton1_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs)
  52:          Dim DelImageButton1 As ImageButton = CType(sender, ImageButton)
  53:          Try
  54:              If Me.WorkingXML IsNot Nothing Then
  55:                  Dim NewAttr As XAttribute = GetWorkingNode(Me.WorkingXML).Attribute(DelImageButton1.CommandArgument)
  56:                  NewAttr.Remove()
  57:                  tx_Name.Text = ""
  58:                  tx_Value.Text = ""
  59:              End If
  60:              refresh()
  61:          Catch ex As Exception
  62:              Lerr1.Text = ex.Message
  63:          End Try
  64:      End Sub
  65:   
  66:      Function GetWorkingNode(ByVal Xml As XElement) As XElement
  67:          If Xml IsNot Nothing Then
  68:              Dim WorkingNode As XElement
  69:              For i As Integer = 0 To Xml.Nodes().Count - 1
  70:                  If CType(Xml.Nodes(i), XElement).Name.LocalName = Request.QueryString("Node") Then
  71:                      WorkingNode = CType(Xml.Nodes(i), XElement)
  72:                  End If
  73:              Next
  74:              Return WorkingNode
  75:          End If
  76:      End Function
  77:   
  78:  End Class

Как видите, буквально несколько десятков строк кода - а какая получилась красота! Это гораздо круче например движка 1С, который тоже делает нечто подобное - только он не умеет организовать ДЛЯ КАЖДОГО документа собственные реквизиты (свойства) - а только для всей последовательности. В моей концепции - КАЖДЫЙ документ имеет самостоятельные реквизиты. И достигнуто это буквально несколькими строчками кода на бейсике.

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



Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17>  <18>  <19>  <20>  <21>  <22>  <23
Link to this page: //www.vb-net.com/Linq/nolinq.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <CHAT ME>  <ABOUT ME>  < THANKS ME>