(MVC) MVC (2013)

<< назад    Проекты нового Вотпуска.



На этой страничке я расскажу еще об одном сложном проекте, программировать который я упирался весь 2013-й год и который совершенно неожиданно загнулся в процессе внедрения в начале 2014-го года. Это еще один из моих Epic Fail Project, которые произошли в последние годы.

Этот проект оказался неудачным чисто из-за увеличения рейтинга Солцеликого Хуепутина, ибо как известно, каждое увеличение рейтинга Хуйла и сопутствующее усиление патриотизма всего на 1% - автоматически приводит к увеличения бегства капиталлов на 10%, коллапсу экономики на 10%, сокражению доходов на 10%, увеличению безработицы на 10% и падению туристического трафика на 20%.

У каждого из моих эпических фейлов последних лет была своя причина, не все они так или иначе связаны с нарастанием рейтинга Верховного Чекистского Лысогнома и сопутствующему коллапсу экономики России:

Итак, ближе к проекту. У компании ВОТПУСК много проектов. Восемь-девять лет назад я сделал для этой компании несколько своих первых проектов: foto, poputi, story , user и search. Изначально я сделал им еще видеоконвертер, аналогичный https://www.youtube.com/ , но по невыясненной причине у него появлялись утечки памяти и он подвисал и у него требовалось примерно раз в три дня нажимать кнопку RESTART. Однако модератор был такой ленивый, что забывал это делать постоянно, в итоге компания не смогла победить лень модератора и через пару лет видеохостинг прекратил свое существование. А может быть дело не только в лени модератора, а и коммерческой неэффективности и неконткурентности видеохостинга. Позже я делал и многие другие проекты для этой компании, но несмотря на их шероховатости и баги, они отбирались, проекты доводились до совершенства и работают и поныне, например аренда.

Но вернемся к этим пяти проектам. По состоянию на 2006-й год выглядели вполне на уровне.





Особенно шикарным было техническое ядро этих проектов, например все рисунки хранились в базе, что позволяло не бодаться с гигабайтными каталогами, которые бы бекапились и копирвоались сутками, а сделать BAK-RESTORE базы за две-три секунды. Ну и все остальное было сделано круто - например в каталогах проекта ничего не хранилось, всякий кеширующий мусор хранился на отдельном (внешнем по отношению к проекту ресурсе), этот диск с кешами можно было в любой момент отмонтировать и примонтировать новый и он постепенно сам заполнялся нужной для проекта ерундой.

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





Естестественно, за 8-9 лет внешний ползовательский интерфейс устарел. Как визуально, так появились и новые фишки (например рейтингование всего и вся) в такого рода проектах. Изначальное техническое задание, по которому я программировал этот проект 9 лет назад, также содержало некоторые раздражающие ограничения, например фотки в рассказ не вставлялись в любое место, а была лишь титуальная фотка и привязанный к рассказу альбом, а народ уже привык к OpenSource-интерфейсу LJ, где можно пихать фотки в любое место рассказа.

Поэтому было изготовлено ТЗ нового проекта, и назначен руководитель проекта, некий Алексей Тараканов, которому я и отчитывался о ходе работы.





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


Кроме того, в силу того, что в проекте работал отдельный верстальщик (а старый проект, сделанный мною 8-9 лет назад, верстал я сам), то с внешним версталщиком, как вы понимаете, работать на ASP.NET практически невозможно - то было принято решение делать новый проект на ASP.NET.MVC.

В итоге, в новых проектах ВОТПУСКА получилось 323 тысячи строк и 1064 файла. Я понимаю, что часть этого кода автоматически сгенерированная, но все-таки обьем этого проекта оценить это позволяет. Предположим, по этой стаистике - кода верхнего уровня тут впятеро больше, чем например в моем проекте http://www.shel-auto.ru/ или втрое больше, чем в моем проекте FlySeason. Ну конечно все эти сравнения относительны - например во FlySeason уровень SQL был довольно скромный, зато было немерянно кода на FLEX-AIR. А в shel-auto.ru на уровене SQL вообще всего десяток процедур. Однако, общая оценка обьемов программирования проекта нового ВОТПУСКА и трудоемкость относительно других моих проектов - из этих цифр более ли менее понятна.





Из-за того, что MVC вместо простого ASP.NET, из-за нового функционала - мне пришлось практически весь этот проект (объемом в треть миллиона строк кода и тысячу файлов) делать практически заново, то ест старого кода удалось использовать всего лишь 1-5 проецентов.

А кроме того, было решено, что все значимые вещи в новом проекте (в целях ускорения) будут выполнены с кешированием, то есть без прямых запросов в базу. Сравните трудоемкость прямых запросов в базу и запросов с кешированием. Для того, чтобы сделать простой запрос - надо просто разметить кеш в DBML (первый скрин) и в одну LINQ-строку (строка 16 на вором скрине) получить данные. А на третьем-четвертом скрине вы видите трудоемкость этого процесса без прямых запросов в базу, а с запросами в кеш.





Теперь я покажу, как выглядит этот программный проект в студии:





Ну и теперь собственно формы этого проекта. Вы можете и сами по ним пощелкать, но поскольку проект заброшен и никому теперь не нужен (c учетом коллапса туриндустрии) - что-то могло уже отвалится и уже перестало работать.

Проект foto





Проект story





Проект poputi





Проект search





Проект user





В заключение я возьму какой-нибудь осмысленный кусочек из тысячи файлов, составляющих этот проект - опубликую его и пояснию как этот фрагмент работает. Возьмем для примера и посмотрим на кусочек кода, который управляет переключением вкладок ("С высоким рейтингом", "Обсуждаемые", "Новые") - на проекте Foto и формирует соответствующие URL и хлебные крошки.

Итак, для начала есть контрольчик, содержащий разные стили активной/неактивной вкладки.


   1:  <%@ Control Language="VB" Inherits="System.Web.Mvc.ViewUserControl" %>
   2:   
   3:  <div style="margin-bottom: 0;" id="tabBlockMy">
   4:      <ul class="tabNavigation">
   5:      <% If Model.Tab = "RatingTab" Then%>
   6:              <li>
   7:              <div id="bor_botStart" class="bor_bot">
   8:              </div>
   9:          </li>
  10:          <li>
  11:   
  12:          <%: Html.ActionLink("С высоким рейтингом", "RatingTab", "Top", Nothing, New With {.class = "selected"})%>
  13:          </li>   
  14:          <li>
  15:              <div class="bor_bot">
  16:              </div>
  17:          </li>
  18:          <li>
  19:          <%: Html.ActionLink("Обсуждаемые", "CommentTab", "Top")%>
  20:          </li>
  21:          <li>
  22:              <div class="bor_bot">
  23:              </div>
  24:          </li>
  25:          <li>
  26:          <%: Html.ActionLink("Новые", "NewsTab", "Top")%>
  27:          </li>
  28:      <%    ElseIf Model.Tab = "CommentTab" Then%>
  29:              <li>
  30:              <div id="bor_botStart" class="bor_bot">
  31:              </div>
  32:          </li>
  33:          <li>
  34:          <%: Html.ActionLink("С высоким рейтингом", "RatingTab", "Top")%>
  35:          </li>   
  36:          <li>
  37:              <div class="bor_bot">
  38:              </div>
  39:          </li>
  40:          <li>
  41:          <%: Html.ActionLink("Обсуждаемые", "CommentTab", "Top", Nothing, New With {.class = "selected"})%>
  42:          </li>
  43:          <li>
  44:              <div class="bor_bot">
  45:              </div>
  46:          </li>
  47:          <li>
  48:          <%: Html.ActionLink("Новые", "NewsTab", "Top")%>
  49:          </li>
  50:      <%    ElseIf Model.Tab = "NewsTab" Then%>
  51:              <li>
  52:              <div id="bor_botStart" class="bor_bot">
  53:              </div>
  54:          </li>
  55:          <li>
  56:          <%: Html.ActionLink("С высоким рейтингом", "RatingTab", "Top")%>
  57:          </li>   
  58:          <li>
  59:              <div class="bor_bot">
  60:              </div>
  61:          </li>
  62:          <li>
  63:          <%: Html.ActionLink("Обсуждаемые", "CommentTab", "Top")%>
  64:          </li>
  65:          <li>
  66:              <div class="bor_bot">
  67:              </div>
  68:          </li>
  69:          <li>
  70:          <%: Html.ActionLink("Новые", "NewsTab", "Top", Nothing, New With {.class = "selected"})%>
  71:          </li>
  72:      <%    End If%>
  73:   
  74:      </ul>
  75:      <div id="zakladka_p">
  76:          &nbsp;</div>
  77:  </div>
  78:   

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


   1:  Namespace Foto
   2:      Public Class TopController
   3:          Inherits System.Web.Mvc.Controller
   4:          Dim PAGESIZE As Integer = 30
   5:   
   6:          Protected Overloads Overrides Sub OnActionExecuting(ByVal ctx As ActionExecutingContext)
   7:              MyBase.OnActionExecuting(ctx)
   8:              'Для навигатора
   9:              Dim LinkNodes As String() = ctx.HttpContext.Request.FilePath.Split("/")
  10:              Dim TxtNodes As New ArrayList
  11:              For i As Integer = 0 To LinkNodes.Count - 1
  12:                  Select Case LinkNodes(i)
  13:                      Case ""
  14:                          TxtNodes.Add("")
  15:                      Case "Top"
  16:                          TxtNodes.Add("Лучшие фото")
  17:                      Case "RatingTab"
  18:                          TxtNodes.Add("С высоким рейтингом")
  19:                      Case "CommentTab"
  20:                          TxtNodes.Add("Обсуждаемые")
  21:                      Case "NewsTab"
  22:                          TxtNodes.Add("Новые")
  23:                      Case "GoCountry"
  24:                          Select Case Session("Tab")
  25:                              Case "RatingTab"
  26:                                  TxtNodes.Add("С высоким рейтингом")
  27:                              Case "CommentTab"
  28:                                  TxtNodes.Add("Обсуждаемые")
  29:                              Case "NewsTab"
  30:                                  TxtNodes.Add("Новые")
  31:                          End Select
  32:                      Case Else
  33:                          If HttpContext.Application("CountryList") IsNot Nothing Then
  34:                              For j As Integer = 0 To HttpContext.Application("CountryList").Count - 1
  35:                                  If LinkNodes(i).ToLower.Trim = HttpContext.Application("CountryList")(j).ID.ToLower.Trim Then
  36:                                      TxtNodes.Add(HttpContext.Application("CountryList")(j).Name)
  37:                                  End If
  38:                              Next
  39:                          End If
  40:                  End Select
  41:              Next
  42:              ViewData("Navigator") = New With {.Link = LinkNodes, .Txt = TxtNodes}
  43:          End Sub
  44:   
  45:          Function Index() As ActionResult
  46:              Return RedirectToAction("RatingTab")
  47:          End Function
  48:   
  49:          Sub GetTopFoto()
  50:              Dim db1 As New Votpusk_DBDataContext
  51:              Dim BestUser = (db1.GetTopBestUser1).ToList
  52:              ViewData("BestUser") = BestUser
  53:   
  54:              Dim GetTopFoto = (db1.GetTopDayFoto).ToList
  55:              ViewData("TopFoto") = GetTopFoto(0)
  56:   
  57:              Dim TopFotoID As Integer = GetTopFoto(0).UserData_i
  58:              Dim Group = (From X In db1.UserDatas Select X Where X.i = TopFotoID).ToList(0).ToGroup
  59:              Dim FotoGroupID As Integer
  60:              If Group Is Nothing Then
  61:                  FotoGroupID = 0
  62:              Else
  63:                  FotoGroupID = Group
  64:              End If
  65:              ViewData("FotoGroup") = (From X In db1.UserDatas Select X Where X.ToGroup = FotoGroupID Order By X.i).ToList
  66:   
  67:              'Dim BestFoto = (db1.GetTopBestFoto).ToList
  68:              'ViewData("BestFoto") = BestFoto
  69:   
  70:          End Sub
  71:   
  72:          Function RatingTab(id As String) As ActionResult
  73:              Session("Tab") = "RatingTab"
  74:              GetTopFoto()
  75:              Dim DB1 As New Votpusk_DBDataContext
  76:              Dim BestFotoList
  77:              If id IsNot Nothing Then
  78:                  BestFotoList = (From X In DB1.TmpFotoWithBestRatings Take 30 Select X Where X.GroupName_CountryID = id Order By X.ROW_NUMBER).ToList
  79:              Else
  80:                  BestFotoList = (From X In DB1.TmpFotoWithBestRatings Take 30 Select X Order By X.ROW_NUMBER).ToList
  81:              End If
  82:              Dim Model = New With {.Tab = "RatingTab", .FotoList = BestFotoList, .FotoListCount = PAGESIZE + 1}
  83:              If ViewData("Navigator").Link.Length > 3 Then
  84:                  ViewData("PageTitle") = "foto.votpusk.ru - фото с высоким рейтингом (" & ViewData("Navigator").Txt(3).ToString.Trim & ")"
  85:              Else
  86:                  ViewData("PageTitle") = "foto.votpusk.ru - фото с высоким рейтингом "
  87:              End If
  88:              Return View("Index", Model)
  89:          End Function
  90:   
  91:          Function CommentTab(id As String) As ActionResult
  92:              Session("Tab") = "CommentTab"
...
 108:          End Function
 109:   
 110:          Function NewsTab(id As String) As ActionResult
 111:              Session("Tab") = "NewsTab"
...
 123:          End Function

И наконец, тот самый контрольчик хлебных крошек, который читает сформированную коллекцию и формирует хлебные крошки.


   1:  <%@ Control Language="VB" Inherits="System.Web.Mvc.ViewUserControl" %>
   2:   
   3:  <div id="blockBreadCrumbs">
   4:      <a href="http://www.votpusk.ru">Главная</a>
   5:      <img src="http://www.votpusk.ru/image/Hlebnye-kroshki.gif" class="breadCrumbs">
   6:      <a href="http://<%:Application("FotoDomain")%>">Фото туристов</a>
   7:      <img src="http://www.votpusk.ru/image/Hlebnye-kroshki.gif" class="breadCrumbs">
   8:      <% For i As Integer = 1 To ViewData("Navigator").Txt.Count - 1%>
   9:          <% Dim Link As String = ""
  10:              For j As Integer = 1 To i
  11:                  Link &= ViewData("Navigator").Link(j).ToString() & "/"
  12:              Next
  13:              %>
  14:          <a href="http://<%:Application("FotoDomain")%>/<%: Link %>"><%: ViewData("Navigator").Txt(i).ToString()%></a>
  15:          <%If i < ViewData("Navigator").Txt.Count - 1 Then%>
  16:          <img src="http://www.votpusk.ru/image/Hlebnye-kroshki.gif" class="breadCrumbs">
  17:          <%End If%>
  18:      <% Next%>
  19:  </div>

Красиво, не правда ли?



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