Обновляем приложение с MVC 2 до MVC 3

    • ASP.NET
    • MVC
    • Razor
    • ASP.NET MVC 3
    • ASP.NET MVC 2
    • ASPX
  • modified:
  • reading: 6 minutes

Уже год мой сайт (блог) живет на самописном движке, который я сделал при помощи ASP.NET MVC 2. Конечно же я мог пользоваться бесплатными площадками для блогов, но мне пока интересно развиваться по многим направлениям. И если моя основная специальность сейчас Silverlight/WPF, то я все равно стараюсь не забыть про старый добрый веб, без него никуда в нынешнее время. Ну и нужно быть на гребне волны, потому я и решил что пора бы проапгрейдить свой сайт с MVC2 до MVC3. Зачем? А просто, чтобы было. Чтобы в будущем, когда захочется что-то допилить или доделать, а у меня уже была последняя версия, и я мог использовать последние фичи технологии.

Обновляем проект с MVC2 до MVC3

Первое задание, которое нужно выполнить для обновления на новую версию – это обновить сам проект и библиотеки. Если еще сама платформа не скачена, тогда идем на официальный сайт ASP.NET MVC и качаем третью версию отсюда http://www.asp.net/mvc/mvc3. Дальше нам нужно просто поменять references с библиотек версии 2.0 на версию 3.0. Делается это очень просто, качаем ASP.NET MVC 3 Application Upgrader (все бы производители фреймворков так заботились о разработчиках), запускаем, указываем путь до sln файла проекта и ждем результата. Эта тулза так же положит последние версии файлов jQuery фреймворка вам на сайт, но так как я использую Google jQuery CDN (чего и вам советую), то я просто вынес эти файлы из своего проекта и просто поменял ссылку на версию jQuery файла с 1.4.2 на 1.4.4. Еще эта библиотека заменила references во всех моих проектах этого солюшена с MVC 2.0 на MVC 3.0 (и добавила ссылки на две дополнительные библиотеки), как и должна была, а так же обновила web.config файл, а именно добавила в appSettings пару параметров, предназначения которых я пока не знаю:

<add key="ClientValidationEnabled" value="false" />
<add key="UnobtrusiveJavaScriptEnabled" value="false" />

Потом разберемся, что это значит.

В web.config прописала ссылки на библиотеки (желтым - новое):

<compilation debug="true" targetFramework="4.0">
    <assemblies>
        <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
        <add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    </assemblies>
</compilation>

Добавила пару namespace (чтобы на страницах можно было использовать без подключения этих namespace):

<pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID">
   <namespaces>
       <add namespace="System.Web.Mvc" />
       <add namespace="System.Web.Mvc.Ajax" />
       <add namespace="System.Web.Mvc.Html" />
       <add namespace="System.Web.Routing" />
       <add namespace="System.Web.Helpers" />
       <add namespace="System.Web.WebPages" />
   </namespaces>
</pages>

Ну и конечно, прописала asssemblyBinding для того, чтобы не было конфликтов версий (в случае, если используются какие-то библиотеки, которые были скомпилированы с версией MVC 2.0):

<runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
        <dependentAssembly>
           <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
            <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="3.0.0.0" />
        </dependentAssembly>
    </assemblyBinding>
</runtime>

Так же был подправлен файл web.config, который находится в папке Views (добавился обработчик Razor, ну и поменяли версию библиотек со второй на третью).

<?xml version="1.0"?>
<configuration>
  <configSections>
    <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
      <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
    </sectionGroup>
  </configSections>
   <system.web>
    <httpHandlers>
      <add path="*" verb="*" type="System.Web.HttpNotFoundHandler" />
    </httpHandlers>
     <pages validateRequest="false" 
        pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" 
        pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" 
        userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
      <controls>
        <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" namespace="System.Web.Mvc" tagPrefix="mvc" />
      </controls>
    </pages>
  </system.web>
   <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <handlers>
      <remove name="BlockViewHandler" />
      <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
    </handlers>
  </system.webServer>
   <system.web.webPages.razor>
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
      </namespaces>
    </pages>
  </system.web.webPages.razor>
 
  <appSettings>
    <add key="webpages:Enabled" value="false" />
  </appSettings>
</configuration>

Можем скомпилировать наше приложение и запустить. У меня так все заработало с первого раза. Если не компилируется или не запускается, то читаем документацию по MVC 3 об изменениях.

Выкладываем на хостинг

Очевидно, что сейчас мало хостеров поддерживают ASP.NET MVC 3, потому нужно вместе со своим веб-приложением так же поместить в bin директорию библиотеки MVC 3. Как оказалось выложить просто 3 новых библиотеки System.Web.Mvc, System.Web.Helpers и System.Web.WebPages в bin папку к хостеру не достаточно. Эти библиотеки тянут за собой еще список других. Я не стал разбираться с тем, чтобы понять какой именно список библиотек мне нужен, а просто скопировал в bin к хостеру все из папки:

c:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v1.0\Assemblies\

Предполагаю, что список необходимых библиотек такой:

  • Microsoft.Web.Infrastructure.dll
  • System.Web.Helpers.dll
  • System.Web.Razor.dll
  • System.Web.WebPages.dll
  • System.Web.WebPages.Deployment.dll
  • System.Web.WebPages.Razor.dll

Проблема с MembershipProvider

При запуске приложения у хостера получил ошибку (у меня используется свой MembershipProvider, какой именно - расскажу потом)

This method cannot be called during the application's pre-start initialization stage.

Лечится очень просто, идем на статью ASP.NET MVC 3 Release Notes, находим Known Issues, добавляем:

<appSettings>
  <add key="enableSimpleMembership" value="false" />
  <add key="autoFormsAuthentication" value="false" />
</appSettings>

Проблема решена.

Обновление представлений с ASPX на Razor

Ну раз переходим на новый фреймворк, то нужно бы использовать новый View Engine под названием Razor, так как с виду он действительно немного удобнее чем ASPX. Когда я обновлял свое приложение, я пытался найти какую-нибудь утилиту для конвертации, нашел я только проект http://aspx2razor.codeplex.com/, у которого до сих пор нет готовой сборки. Я попытался собрать это творение, но там какие-то непонятные reference на библиотеку Antlr3.Runtime.dll, я ее ни разу вроде не использовал, за минуту я не нашел, где мне ее скачать можно, тогда и написал в твиттере:

Выкладывайте в SVN все библиотеки, которые вы скачивали, даже из toolkit! Не все их ставят, и версии бывают разные!

Ну действительно, если уж публикуете код, то делайте его так, чтобы любой 5 летний ребенок мог просто запустить VS и скомпилировать проект, а не вот так вот:

reference

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

Переводил я проект с версии 2.0 на 3.0 уже как пару недель назад, и вот недавно свершилось чудо, Telerik опубликовал код своей библиотеки, которая умеет переводить ASPX страницы в Razor. Для этого идем скачивать исходный код библиотеки с GitHub, компилируем и запускаем проект aspx2razor (консольное приложение). Я его, правда, немного подпилил, чтобы была возможность сразу запустить эту тулзу по всем папкам, и сложить их так же по отдельным папкам, как они и были в исходнике, для этого я поправил строки с 64 по 77 файла Program.cs:

foreach (var file in Directory.GetFiles(inputDirectory, inputFilter, SearchOption.AllDirectories))
{    Console.Write("Converting {0}... ", Path.GetFileName(file));
    
    var webFormsPageSource = File.ReadAllText(file, Encoding.UTF8);
    var webFormsDocument = Parser.Parse(webFormsPageSource);
    var razorDom = Converter.Convert(webFormsDocument);
    var razorPage = Renderer.Render(razorDom);
     string directoryName = Path.GetDirectoryName(file).Substring(inputDirectory.Length + 1);
     string outDirectory = Path.Combine(outputDirectory, directoryName);
    if (!Directory.Exists(outDirectory))
        Directory.CreateDirectory(outDirectory);
     var outputFile = Path.Combine(outDirectory, Path.GetFileNameWithoutExtension(file) + ".cshtml");
    File.WriteAllText(outputFile, razorPage, Encoding.UTF8);    Console.WriteLine("done");
}

Код не будет работать, если будут не абсолютные директории и еще может в паре случаях, но вот на таком примере работает отлично:

aspx2razor.exe c:\Projects\PersonalWeb\PersonalWeb\Views\*.aspx c:\Projects\PersonalWeb\Razor\Views\

Он нашел все файлы с расширением aspx и сделал cshtml файлы, причем все разложил так же по папкам, как и было первоначально. Конвертация Master страниц не поддерживается, потому пока не обновил свой блог на Razor (в итоге обновил), с этим отдельно буду разбираться, как там Master страницы при помощи его описываются.

Если существуют еще проблемы и решения

Если существуют еще проблемы или решения, с которыми вы не можете разобраться при обновлении, то можете попробовать спросить меня, постараюсь помочь. А если знаете еще какие-то стандартные проблемы или решения, которые стоит предпринять при обновлении, то поделитесь, пожалуйста, советом-комментарием.

See Also