<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3325710389670957908</id><updated>2011-11-27T16:26:46.578-08:00</updated><category term='NUnit'/><category term='log4net'/><category term='SNV'/><category term='подготовка'/><category term='Entity Framework'/><category term='CMS'/><title type='text'>Чистое программирование</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>23</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-2181554314840071448</id><published>2010-03-30T16:43:00.000-07:00</published><updated>2010-03-30T17:01:30.948-07:00</updated><title type='text'>Пишем CMS. Шаг № 8. Unit-тестирование</title><content type='html'>Это архиполезная вещь, особенно в проектах, которые имеют функционал работы с данными, с БД, например. Я расскажу, как я организую юнит-тестирование в своих проектах.&lt;br /&gt;&lt;br /&gt;1. Перво-наперво качаем последнюю версию NUnit (откуда, найдете сами).&lt;br /&gt;&lt;br /&gt;2. Создаем в солюшене отдельный проект типа ClassLibrary и добавляем референс на библиотеку nunit.framework.dll, которая лежит в папке framework каталога, куда распаковался наш nunit.&lt;br /&gt;&lt;br /&gt;3. Создаем новый класс, добавляем в него ссылку на пространство имен NUnit.Framework, затем перед классом добавляем атрибут [TestFixture], а перед методом-тестом (который должен быть обязательно public и возвращать void - атрибут [Test].&lt;br /&gt;&lt;br /&gt;4. Теперь надо организовать удобный запуск тестов. Я обычно делаю так. В окне свойств проекта с тестами щелкаю вкладку Debug, а там - Start external program, где указываю путь к nunit.exe. А сам проект устанавливаю как SetUp as statrup project. Это значит, что теперь, когда я нажимаю F5, запускается именно этот проект, вернее, запускается оболочка nunit.exe, которая подтягивает наши проекты.&lt;br /&gt;&lt;br /&gt;5. Теперь надо создать проект среды тестирования. Для этого нажмем F5 и увидим диалог NUnit, там мы выберем меню New Project, введем название и сохраним проект где-нибудь (я предпочитаю корневую папку проекта с тестами). Теперь можно добавить туда нашу dll с тестами (она обычно лежит в bin/debug) - через меню Project - Add assembly (некоторые версии NUnit имеют такой баг - после того, как вы создаете проект и загружаете туда dll, он не подхватывает библиотеку, выдает какую-то странную ошибку. Если это произошло, просто сохраните проект и закройте оболочку, затем запустите снова - должно пройти.)&lt;br /&gt;&lt;br /&gt;6. Ну вот и все! Теперь можете писать свои тесты и запускать их. Да! Одна маленькая, но очень важная деталь - если вы не обратите на это внимание, вы можете часами биться об стену, пытаясь понять, в чем дело. А дело в том, что если вы работаете например, с ADO.Net Entity Framework и вам надо потестировать, хорошо ли из базы забираются данные, то вы обязательно должны скопировать файл App.Config в папку проекта с тестами - именно отсюда фреймворк будет брать информацию по соединению с БД. Но и это еще не все. В свойствах NUnit проекта в поле Configuration File Name нужно обязательно указать имя этого конфигурационного файла.&lt;br /&gt;&lt;br /&gt;Теперь, кажется, все... удачного тестирования!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-2181554314840071448?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/2181554314840071448/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=2181554314840071448' title='Комментарии: 7'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/2181554314840071448'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/2181554314840071448'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2010/03/cms-8-unit.html' title='Пишем CMS. Шаг № 8. Unit-тестирование'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-8337413553130528362</id><published>2010-03-17T14:04:00.000-07:00</published><updated>2010-03-17T18:46:06.506-07:00</updated><title type='text'>Пишем CMS. Шаг № 7. Шаг вперед, два шага назад. Рефакторинг</title><content type='html'>Что-то мне не нравится контрол, который отвечает за показ результата на странице. Во-первых, надо добавить информацию, через сколько времени и куда будет перемещен пользователь, и что ему делать, если браузер не поддерживает перенаправления. Во-вторых, надо разбить метод Activate на два - один будет просто показывать сообщение об ошибке, а второй будет показывать подробную инфу, с ссылками, со всем прочим.&lt;br /&gt;&lt;br /&gt;Добавим в ресурсный файл Messages переменную по имени TransferPageMessage, что-то вроде "Вы будете перемещены на страницу {0} через {1} сек. Или, если ваш браузер не поддерживает автоматическое перенаправление, перейдите по ссылке ниже:" &lt;br /&gt;&lt;br /&gt;Изменим содержимое контрола:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;font size="2" face="Courier New" color="black"&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;&amp;#60;&lt;/font&gt;&lt;font color="#800000"&gt;asp:Panel&lt;/font&gt; &lt;font color="#ff0000"&gt;ID&lt;/font&gt;&lt;font color="#0000ff"&gt;="panelInfo"&lt;/font&gt; &lt;font color="#ff0000"&gt;runat&lt;/font&gt;&lt;font color="#0000ff"&gt;="server"&lt;/font&gt; &lt;font color="#ff0000"&gt;Visible&lt;/font&gt;&lt;font color="#0000ff"&gt;="true"&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;&amp;#60;&lt;/font&gt;&lt;font color="#800000"&gt;asp:Label&lt;/font&gt; &lt;font color="#ff0000"&gt;ID&lt;/font&gt;&lt;font color="#0000ff"&gt;="labInfo"&lt;/font&gt; &lt;font color="#ff0000"&gt;runat&lt;/font&gt;&lt;font color="#0000ff"&gt;="server"&lt;/font&gt; &lt;font color="#ff0000"&gt;Text&lt;/font&gt;&lt;font color="#0000ff"&gt;=""&lt;/font&gt; &lt;font color="#ff0000"&gt;CssClass&lt;/font&gt;&lt;font color="#0000ff"&gt;="LabelInfo"&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&amp;#60;/&lt;/font&gt;&lt;font color="#800000"&gt;asp:Label&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;&amp;#60;&lt;/font&gt;&lt;font color="#800000"&gt;asp:Label&lt;/font&gt; &lt;font color="#ff0000"&gt;ID&lt;/font&gt;&lt;font color="#0000ff"&gt;="labLink"&lt;/font&gt; &lt;font color="#ff0000"&gt;runat&lt;/font&gt;&lt;font color="#0000ff"&gt;="server"&lt;/font&gt;&amp;nbsp;&lt;font color="#ff0000"&gt;CssClass&lt;/font&gt;&lt;font color="#0000ff"&gt;="LabelInfo"&lt;/font&gt; &lt;font color="#ff0000"&gt;Visible&lt;/font&gt;&lt;font color="#0000ff"&gt;="false"&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&amp;#60;/&lt;/font&gt;&lt;font color="#800000"&gt;asp:Label&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&lt;/font&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;&amp;#60;&lt;/font&gt;&lt;font color="#800000"&gt;asp:HyperLink&lt;/font&gt; &lt;font color="#ff0000"&gt;ID&lt;/font&gt;&lt;font color="#0000ff"&gt;="linkInfo"&lt;/font&gt;&amp;nbsp;&lt;font color="#ff0000"&gt;runat&lt;/font&gt;&lt;font color="#0000ff"&gt;="server"&lt;/font&gt; &lt;font color="#ff0000"&gt;CssClass&lt;/font&gt;&lt;font color="#0000ff"&gt;="LinkInfo"&lt;/font&gt; &lt;font color="#ff0000"&gt;Visible&lt;/font&gt;&lt;font color="#0000ff"&gt;="false"&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;&amp;#60;/&lt;/font&gt;&lt;font color="#800000"&gt;asp:HyperLink&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;&amp;#60;/&lt;/font&gt;&lt;font color="#800000"&gt;asp:Panel&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&lt;/font&gt;&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;&lt;font size="1" color="gray"&gt;* This source code was highlighted with &lt;a href="http://virtser.net/blog/post/source-code-highlighter.aspx"&gt;&lt;font size="1" color="gray"&gt;Source Code Highlighter&lt;/font&gt;&lt;/a&gt;.&lt;/font&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;А теперь обновим код:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;font size="2" face="Courier New" color="black"&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt; &lt;font color="#0000ff"&gt;void&lt;/font&gt; ShowFail(&lt;font color="#0000ff"&gt;string&lt;/font&gt; Message)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;this&lt;/font&gt;.Visible = &lt;font color="#0000ff"&gt;true&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;panelInfo.CssClass = &lt;font color="#A31515"&gt;"PanelInfoFail"&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (Message != &lt;font color="#0000ff"&gt;string&lt;/font&gt;.Empty)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;labInfo.Text = Message;&lt;br /&gt;}&lt;br /&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt; &lt;font color="#0000ff"&gt;void&lt;/font&gt; ShowOk(&lt;font color="#0000ff"&gt;string&lt;/font&gt; Message, &lt;font color="#0000ff"&gt;string&lt;/font&gt; PageToName, &lt;font color="#0000ff"&gt;string&lt;/font&gt; LinkLabelCode, &lt;font color="#0000ff"&gt;string&lt;/font&gt; Link, &lt;font color="#0000ff"&gt;int&lt;/font&gt; SecondsToTransfer)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;this&lt;/font&gt;.Visible = &lt;font color="#0000ff"&gt;true&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;panelInfo.CssClass = &lt;font color="#A31515"&gt;"PanelInfoOK"&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (Message != &lt;font color="#0000ff"&gt;string&lt;/font&gt;.Empty)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;labInfo.Text = Message + &lt;font color="#A31515"&gt;"&amp;#60;br&amp;#62;&amp;#60;br&amp;#62;"&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (PageToName != &lt;font color="#0000ff"&gt;string&lt;/font&gt;.Empty)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;labLink.Visible = &lt;font color="#0000ff"&gt;true&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;labLink.Text = &lt;font color="#0000ff"&gt;string&lt;/font&gt;.Format(Messages.TransferPageMessage, PageToName, SecondsToTransfer) + &lt;font color="#A31515"&gt;"&amp;#60;br&amp;#62;&amp;#60;br&amp;#62;"&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (LinkLabelCode != &lt;font color="#0000ff"&gt;string&lt;/font&gt;.Empty &amp;#38;&amp;#38; Link != &lt;font color="#0000ff"&gt;string&lt;/font&gt;.Empty)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;linkInfo.Visible = &lt;font color="#0000ff"&gt;true&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;linkInfo.Text = LinkLabelCode;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;linkInfo.NavigateUrl = Link;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/font&gt;&lt;br /&gt;&lt;font size="1" color="gray"&gt;* This source code was highlighted with &lt;a href="http://virtser.net/blog/post/source-code-highlighter.aspx"&gt;&lt;font size="1" color="gray"&gt;Source Code Highlighter&lt;/font&gt;&lt;/a&gt;.&lt;/font&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;По-моему, стало лучше. И не забудем обновить вызовы в коде нашей страницы:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;font size="2" face="Courier New" color="black"&gt;&lt;font color="#0000ff"&gt;try&lt;/font&gt;&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (context == &lt;font color="#0000ff"&gt;null&lt;/font&gt;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;context = &lt;font color="#0000ff"&gt;new&lt;/font&gt; PBPWebDatabaseConnection();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (languageObject != &lt;font color="#0000ff"&gt;null&lt;/font&gt;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;languageObject.LanguageTitle = Server.HtmlEncode(txtLangName.Text);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;languageObject.LanguageCode = Server.HtmlEncode(txtLangCode.Text);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (filePathToUpload != &lt;font color="#A31515"&gt;""&lt;/font&gt;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;languageObject.IconName = Path.GetFileName(filePathToUpload);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (languageObject.LanguageId == 0)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;context.AddObject(&lt;font color="#A31515"&gt;"Language"&lt;/font&gt;, languageObject);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;int&lt;/font&gt; saved = context.SaveChanges();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (saved == 1)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;panelInfo.ShowOk(Messages.LanguageSaveOK, Messages.LanguagePageToTransfer,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Messages.LabelLinkToLanguages, Pages.Languages, 4);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Master.AddRedirect(Pages.Languages, 4);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;panelMain.Visible = &lt;font color="#0000ff"&gt;false&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result = &lt;font color="#0000ff"&gt;true&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;font color="#0000ff"&gt;catch&lt;/font&gt; (Exception ex)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#008000"&gt;//log&lt;/font&gt;&lt;br /&gt;}&lt;br /&gt;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (!result)&lt;br /&gt;&amp;nbsp;&amp;nbsp;panelInfo.ShowFail(Messages.LanguageSaveFail);&lt;br /&gt;&lt;/font&gt;&lt;br /&gt;&lt;font size="1" color="gray"&gt;* This source code was highlighted with &lt;a href="http://virtser.net/blog/post/source-code-highlighter.aspx"&gt;&lt;font size="1" color="gray"&gt;Source Code Highlighter&lt;/font&gt;&lt;/a&gt;.&lt;/font&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;До идеала, конечно, далеко, но мы постепенно будем улучшать код. Вот такие сообщения у меня получились в итоге:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_STb7hsxmscM/S6FGDr8sgjI/AAAAAAAAAS0/p2GkSXTWTzM/s1600-h/MessageFail.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 65px;" src="http://2.bp.blogspot.com/_STb7hsxmscM/S6FGDr8sgjI/AAAAAAAAAS0/p2GkSXTWTzM/s320/MessageFail.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5449714053245862450" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_STb7hsxmscM/S6FGDK0nHHI/AAAAAAAAASs/drMZotI9DMU/s1600-h/MessageOK.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 130px;" src="http://3.bp.blogspot.com/_STb7hsxmscM/S6FGDK0nHHI/AAAAAAAAASs/drMZotI9DMU/s320/MessageOK.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5449714044353584242" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-8337413553130528362?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/8337413553130528362/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=8337413553130528362' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/8337413553130528362'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/8337413553130528362'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2010/03/7.html' title='Пишем CMS. Шаг № 7. Шаг вперед, два шага назад. Рефакторинг'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_STb7hsxmscM/S6FGDr8sgjI/AAAAAAAAAS0/p2GkSXTWTzM/s72-c/MessageFail.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-5384691844020419749</id><published>2010-03-11T17:20:00.000-08:00</published><updated>2010-03-17T14:04:07.232-07:00</updated><title type='text'>Пишем CMS. Шаг № 6. Добавление нового языка</title><content type='html'>Продолжим.&lt;br /&gt;Сегодня мы добавим функциональность на страницу LanguageEdit.aspx, и сможем добавлять новые языки или редактировать уже существующие.&lt;br /&gt;&lt;br /&gt;Для начала определим две переменные: одна будет представлять собой объект Language, который мы создали еще раньше с помощью ADO.NET Framework Entity, а вторая - собственно инструмент работы с БД - контекст:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;font size="2" face="Courier New" color="black"&gt;&lt;font color="#0000ff"&gt;private&lt;/font&gt; Language languageObject;&lt;br /&gt;&lt;font color="#0000ff"&gt;private&lt;/font&gt; DatabaseConnection context;&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;&lt;font size="1" color="gray"&gt;* This source code was highlighted with &lt;a href="http://virtser.net/blog/post/source-code-highlighter.aspx"&gt;&lt;font size="1" color="gray"&gt;Source Code Highlighter&lt;/font&gt;&lt;/a&gt;.&lt;/font&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Далее в обработчик загрузки страницы Page_Load добавим проверку на наличие id:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;font size="2" face="Courier New" color="black"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (Request[&lt;font color="#A31515"&gt;"id"&lt;/font&gt;] != &lt;font color="#0000ff"&gt;null&lt;/font&gt;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;int&lt;/font&gt; id = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;int&lt;/font&gt;.TryParse(Request[&lt;font color="#A31515"&gt;"id"&lt;/font&gt;], &lt;font color="#0000ff"&gt;out&lt;/font&gt; id);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (id != 0)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;try&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;context = &lt;font color="#0000ff"&gt;new&lt;/font&gt; DatabaseConnection();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;var&lt;/font&gt; lan = &lt;font color="#0000ff"&gt;from&lt;/font&gt; l &lt;font color="#0000ff"&gt;in&lt;/font&gt; context.Language&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;where&lt;/font&gt; l.LanguageId == id&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;select&lt;/font&gt; l;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;languageObject = (Language)lan.First();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;catch&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#008000"&gt;//log&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (languageObject == &lt;font color="#0000ff"&gt;null&lt;/font&gt;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;languageObject = &lt;font color="#0000ff"&gt;new&lt;/font&gt; Language();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (!IsPostBack)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#2B91AF"&gt;Page&lt;/font&gt;.DataBind();&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;&lt;font size="1" color="gray"&gt;* This source code was highlighted with &lt;a href="http://virtser.net/blog/post/source-code-highlighter.aspx"&gt;&lt;font size="1" color="gray"&gt;Source Code Highlighter&lt;/font&gt;&lt;/a&gt;.&lt;/font&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Логика работы этого блока проста: если мы получаем в качестве параметра id, то извлекаем из базы объект по этому id. Если же параметра нет, значит, мы хотим создать новый язык.&lt;br /&gt;&lt;br /&gt;В обработчик события Unload можно добавить "уничтожение" объекта context.Dispose(), хотя, полагаю, ресурсы и так будут автоматически очищены по завершении обработки страницы.&lt;br /&gt;&lt;br /&gt;Теперь разместим на странице наши контролы, для начала это буду два поля ввода - в одном заголовок языка, во втором его кодовое представление (например, "en-EN"), с его помощью удобно сразу выставлять региональные настройки. Позднее мы добавим контрол загрузки файла, с помощью которого будем грузить на сервер иконки с изображением флага, а пока надо разобраться с основными вещами.&lt;br /&gt;&lt;br /&gt;Мы подошли к ключевому вопросу - привязка данных к форме. ASP.NET предлагает несколько контролов для привязки данных, но после некоторой возни с ним мне пришлось отказаться от их использования. Фактически, только два контрола - DetailsView и FormView - позволяют отображать данные по одному объекту, но мне так и не удалось заставить их привязываться к EntityObject. По какой-то, неведомой мне причине, эти контролы в качестве источника данных требуют обязательно объект, который наследут  IListSource, IEnumerable, или IDataSource. Наш объект EntityObject не реализует ни один из этих интерфейсов. В качестве источника данных можно было бы использовать ObjectDataSource, но для этого нужно соблюсти определенные требования к классам доступа DAL - поскольку мы используем классы, сгенерированные EntityFramework, использование их не получается. Можно, конечно, было бы написать обертку к этим классам, но плодить лишний код не хочется совершенно. &lt;br /&gt;&lt;br /&gt;После некоторых раздумий, а также просмотра обновленного кода проекта TheBeerHouse пришлось остановится на простой односторонней привязке данных к объекту. Что это значит? Очень просто - при открытии страницы мы создаем объект, а наши поля, привязанные к нему, заполняются автоматически. Когда мы хотим сохранить измененный объект, мы извлекаем содержимое полей вручную. Полагаю, ручное извлечение неизбежно - ведь объект еще не сохранен, а значит, поля невозможно привязать. Если кто подскажет более удачное решение, будет здорово.&lt;br /&gt;&lt;br /&gt;Итак, создаем на странице простое свойство, которое имеет только метод get и возвращает наш объект:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;font size="2" face="Courier New" color="black"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;protected&lt;/font&gt; Language LanguageObject&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;get&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;return&lt;/font&gt; languageObject;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;&lt;font size="1" color="gray"&gt;* This source code was highlighted with &lt;a href="http://virtser.net/blog/post/source-code-highlighter.aspx"&gt;&lt;font size="1" color="gray"&gt;Source Code Highlighter&lt;/font&gt;&lt;/a&gt;.&lt;/font&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;На страницу добавляем текстбоксы и осуществляем привязку, например:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;font size="2" face="Courier New" color="black"&gt;&lt;font color="#0000ff"&gt;&amp;#60;&lt;/font&gt;&lt;font color="#800000"&gt;asp:TextBox&lt;/font&gt; &lt;font color="#ff0000"&gt;ID&lt;/font&gt;&lt;font color="#0000ff"&gt;="txtLangCode"&lt;/font&gt; &lt;font color="#ff0000"&gt;runat&lt;/font&gt;&lt;font color="#0000ff"&gt;="server"&lt;/font&gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#ff0000"&gt;Text&lt;/font&gt;&lt;font color="#0000ff"&gt;="&amp;#60;%# LanguageObject.LanguageCode %&amp;#62;"&lt;/font&gt; &lt;font color="#ff0000"&gt;Width&lt;/font&gt;&lt;font color="#0000ff"&gt;="214px"&lt;/font&gt; &lt;font color="#0000ff"&gt;/&amp;#62;&lt;/font&gt;&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;&lt;font size="1" color="gray"&gt;* This source code was highlighted with &lt;a href="http://virtser.net/blog/post/source-code-highlighter.aspx"&gt;&lt;font size="1" color="gray"&gt;Source Code Highlighter&lt;/font&gt;&lt;/a&gt;.&lt;/font&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;А теперь вернемся к первому коду, обратите внимание:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;font size="2" face="Courier New" color="black"&gt;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (!IsPostBack)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#2B91AF"&gt;Page&lt;/font&gt;.DataBind();&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;&lt;font size="1" color="gray"&gt;* This source code was highlighted with &lt;a href="http://virtser.net/blog/post/source-code-highlighter.aspx"&gt;&lt;font size="1" color="gray"&gt;Source Code Highlighter&lt;/font&gt;&lt;/a&gt;.&lt;/font&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Этот код проверяет, был ли постбэк, и, если нет, привязывает все контролы формы. Если мы будем делать привязку и после постбэка, то введенные нами данные в форму будут затерты извлеченными из объекта, что нам совсем не нужно.&lt;br /&gt;&lt;br /&gt;Ну вот, осталось дело за малым: сохранить измененные данные. Кидаем на форму кнопку, добавляем событие Click и в обработчике пишем примерно такой код:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;font size="2" face="Courier New" color="black"&gt;&lt;font color="#0000ff"&gt;bool&lt;/font&gt; result = &lt;font color="#0000ff"&gt;false&lt;/font&gt;;&lt;br /&gt;&lt;font color="#0000ff"&gt;try&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (context == &lt;font color="#0000ff"&gt;null&lt;/font&gt;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;context = &lt;font color="#0000ff"&gt;new&lt;/font&gt; PBPWebDatabaseConnection();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (languageObject != &lt;font color="#0000ff"&gt;null&lt;/font&gt;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;languageObject.LanguageTitle = Server.HtmlEncode(txtLangName.Text);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;languageObject.LanguageCode = Server.HtmlEncode(txtLangCode.Text);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (filePathToUpload != &lt;font color="#A31515"&gt;""&lt;/font&gt;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;languageObject.IconName = Path.GetFileName(filePathToUpload);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (languageObject.LanguageId == 0)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;context.AddObject(&lt;font color="#A31515"&gt;"Language"&lt;/font&gt;, languageObject);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;int&lt;/font&gt; saved = context.SaveChanges();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (saved == 1)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;panelInfo.Activate(&lt;font color="#0000ff"&gt;true&lt;/font&gt;, Messages.LanguageSaveOK, Messages.LabelLinkToLanguages, Pages.Languages);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Master.AddRedirect(Pages.Languages, 4);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;panelMain.Visible = &lt;font color="#0000ff"&gt;false&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result = &lt;font color="#0000ff"&gt;true&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;catch&lt;/font&gt; (Exception ex)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#008000"&gt;//log&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (!result)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;panelInfo.Activate(&lt;font color="#0000ff"&gt;false&lt;/font&gt;, Messages.LanguageSaveFail, &lt;font color="#0000ff"&gt;string&lt;/font&gt;.Empty, &lt;font color="#0000ff"&gt;string&lt;/font&gt;.Empty);&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;&lt;font size="1" color="gray"&gt;* This source code was highlighted with &lt;a href="http://virtser.net/blog/post/source-code-highlighter.aspx"&gt;&lt;font size="1" color="gray"&gt;Source Code Highlighter&lt;/font&gt;&lt;/a&gt;.&lt;/font&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Сначала мы проверяем, не нуль ли контекст. Если вдруг он равен null, создаем новый. Объект languageObject уже должен быть создан к этому времени (пустой или взятый из базы). Перезаписываем его свойства данными из полей формы. Затем, если объект был заново созданный (id  у него равно 0), то добавляем в контекст. Если нет, ничего не делаем, потому что в этом случае объект уже привязан к контексту (это произошло, когда мы извлекали объект по id). Осталось просто сохранить объект и отреагировать на полученный результат.&lt;br /&gt;&lt;br /&gt;Вроде ничего сложного. Если что непонятно, спрашивайте. А загрузку файлов мы чуть попозже обсудим.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-5384691844020419749?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/5384691844020419749/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=5384691844020419749' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/5384691844020419749'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/5384691844020419749'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2010/03/cms-6.html' title='Пишем CMS. Шаг № 6. Добавление нового языка'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-3766512971990037879</id><published>2010-03-10T18:05:00.000-08:00</published><updated>2010-03-11T17:28:28.071-08:00</updated><title type='text'>Пишем CMS. Шаг № 5. Перенаправляем пользователя</title><content type='html'>Итак, мы показали пользователю сообщение, а том, что все в порядке, и теперь хотим через 4 секунды перенаправить его на другую страницу.&lt;br /&gt;&lt;br /&gt;Делается это очень просто. Чтобы браузер, открыв страница, через положенное время перенаправил пользователя в другое место, достаточно, чтобы эта страница имела следующий метатэг:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;font size="2" face="Courier New" color="black"&gt;&lt;font color="#0000ff"&gt;&amp;#60;&lt;/font&gt;&lt;font color="#800000"&gt;meta&lt;/font&gt; &lt;font color="#ff0000"&gt;http-equiv&lt;/font&gt;&lt;font color="#0000ff"&gt;="Refresh"&lt;/font&gt; &lt;font color="#ff0000"&gt;content&lt;/font&gt;&lt;font color="#0000ff"&gt;="4;url=http://site.com/anotherpage.aspx"&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&lt;/font&gt;&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;&lt;font size="1" color="gray"&gt;* This source code was highlighted with &lt;a href="http://virtser.net/blog/post/source-code-highlighter.aspx"&gt;&lt;font size="1" color="gray"&gt;Source Code Highlighter&lt;/font&gt;&lt;/a&gt;.&lt;/font&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Разумеется, мы хотим управлять перенаправлением, иметь возможность менять время, страницу и т.д., поэтому данный метатэг мы добавим программным образом. На мой взгляд, разумно добавить этот код в мастер-страницу, чтобы иметь возможность без лишнего дублирования кода всегда осуществлять перенаправление. &lt;br /&gt;&lt;br /&gt;Сначала добавим в .cs файл мастер-страницы наш код:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;font size="2" face="Courier New" color="black"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;public&lt;/font&gt; &lt;font color="#0000ff"&gt;void&lt;/font&gt; AddRedirect(&lt;font color="#0000ff"&gt;string&lt;/font&gt; PageToCode, &lt;font color="#0000ff"&gt;int&lt;/font&gt; SecondsToWait)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HtmlMeta metaRedirect = &lt;font color="#0000ff"&gt;new&lt;/font&gt; HtmlMeta();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;metaRedirect.HttpEquiv = &lt;font color="#A31515"&gt;"Refresh"&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;metaRedirect.Content = &lt;font color="#0000ff"&gt;string&lt;/font&gt;.Format(&lt;font color="#A31515"&gt;"{0};url={1}"&lt;/font&gt;, SecondsToWait, ResolveUrl(PageToCode));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;this&lt;/font&gt;.&lt;font color="#2B91AF"&gt;Page&lt;/font&gt;.Header.Controls.Add(metaRedirect);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;&lt;font size="1" color="gray"&gt;* This source code was highlighted with &lt;a href="http://virtser.net/blog/post/source-code-highlighter.aspx"&gt;&lt;font size="1" color="gray"&gt;Source Code Highlighter&lt;/font&gt;&lt;/a&gt;.&lt;/font&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Теперь откроем страницу LanguageEdit.aspx, с помощью которой мы редактируем и добавляем языки. Там мы добавим следующую директиву:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;font size="2" face="Courier New" color="black"&gt;&lt;font&gt;&amp;#60;%@ MasterType VirtualPath="~/Main.Master" %&amp;#62;&lt;/font&gt;&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;&lt;font size="1" color="gray"&gt;* This source code was highlighted with &lt;a href="http://virtser.net/blog/post/source-code-highlighter.aspx"&gt;&lt;font size="1" color="gray"&gt;Source Code Highlighter&lt;/font&gt;&lt;/a&gt;.&lt;/font&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Эта директива позволяет нам обращаться к мастер странице не просто как к объекту типа System.Web.UI.MasterPage, а к объекту нашего конкретного типа. Если вы запутались, поясняю: когда мы добавляем в проект мастер-страницу, всегда создается класс, который наследует вышеприведенный класс. Однако наша страница имеет объект Master, который всегда определен как MasterPage, и поэтому любые дополнительные члены мастера будут нам недоступны. Мы, конечно, можем осуществлять приведение типа ((Main)Master).OurMethod(), но согласитесь, это не очень красиво. Зато теперь наша страница знает, что мастер не просто MasterPage, а Main, и поэтому мы может осуществлять вызов метода напрямую:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;font size="2" face="Courier New" color="black"&gt;Master.AddRedirect(Pages.Languages, 4);&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;&lt;font size="1" color="gray"&gt;* This source code was highlighted with &lt;a href="http://virtser.net/blog/post/source-code-highlighter.aspx"&gt;&lt;font size="1" color="gray"&gt;Source Code Highlighter&lt;/font&gt;&lt;/a&gt;.&lt;/font&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Здесь мы опять же передаем переменную из ресурсного файла, которая имеет значение ~/Languages/Languages.aspx - т.е. страница, которая показывает список всех языков.&lt;br /&gt;&lt;br /&gt;Да, не забываем скрывать основную часть формы. Для этого ее (основную часть) можно разместить на панели и просто выставлять ей Visible = false. Не волнуйтесь, что она потащит за собой все данные. ASP.NET, когда видит что что-то невидимое, даже не генерирует html-код.&lt;br /&gt;&lt;br /&gt;Таким образом, когда пользователь добавляет новый язык, или редактирует уже существующий, он после этого перемещается на страницу со списком всех языков.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-3766512971990037879?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/3766512971990037879/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=3766512971990037879' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/3766512971990037879'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/3766512971990037879'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2010/03/cms-5.html' title='Пишем CMS. Шаг № 5. Перенаправляем пользователя'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-125960399741138051</id><published>2010-03-09T17:41:00.000-08:00</published><updated>2010-03-10T18:05:24.900-08:00</updated><title type='text'>Пишем CMS. Шаг № 4. Показываем пользователю результат действия</title><content type='html'>Итак, в прошлом номере нашей программы мы решили, что, как только пользователь что-нибудь такое сделает (добавит, удалит, отредактирует и т.д.) он сначала увидит сообщение (на той же странице по постбэку), а потом результат своего действия (через 3-4 секунды будет перенаправлен на нужную страницу).&lt;br /&gt;&lt;br /&gt;Итак, как же сказать пользователю "Все в порядке!"? Первое, что приходит в голову - разместить на странице Label, в методе обработке результата менять цвет и текст в зависимости от результата. Что мне не нравится в этом подходе? Ну допустим мы определяем внешний вид стилями, которые храним в файле CSS, одном на весь сайт. А что, если мы захотим рядом с текстом добавлять картинку или еще как-то украсить его? Будет весьма затруднительно поменять Label на Div на всех страницах, где мы используем сообщения. Поэтому мы так делать не будем. А мы поэтому создадим простой пользовательский контрол. Делается это очень просто:&lt;br /&gt;&lt;br /&gt;1. Добавляем новый item в наш проект (предлагаю сначала создать папку Controls и хранить все контролы там), выбираем тип Web User Control.&lt;br /&gt;&lt;br /&gt;2. Открываем файл ascx и добавляем такую панель:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;font size="2" face="Courier New" color="black"&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;&amp;#60;&lt;/font&gt;&lt;font color="#800000"&gt;asp:Panel&lt;/font&gt; &lt;font color="#ff0000"&gt;ID&lt;/font&gt;&lt;font color="#0000ff"&gt;="panelInfo"&lt;/font&gt; &lt;font color="#ff0000"&gt;runat&lt;/font&gt;&lt;font color="#0000ff"&gt;="server"&lt;/font&gt; &lt;font color="#ff0000"&gt;Visible&lt;/font&gt;&lt;font color="#0000ff"&gt;="true"&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;&amp;#60;&lt;/font&gt;&lt;font color="#800000"&gt;asp:Label&lt;/font&gt; &lt;font color="#ff0000"&gt;ID&lt;/font&gt;&lt;font color="#0000ff"&gt;="labInfo"&lt;/font&gt; &lt;font color="#ff0000"&gt;runat&lt;/font&gt;&lt;font color="#0000ff"&gt;="server"&lt;/font&gt; &lt;font color="#ff0000"&gt;Text&lt;/font&gt;&lt;font color="#0000ff"&gt;=""&lt;/font&gt; &lt;font color="#ff0000"&gt;CssClass&lt;/font&gt;&lt;font color="#0000ff"&gt;="LabelInfo"&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&amp;#60;/&lt;/font&gt;&lt;font color="#800000"&gt;asp:Label&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&lt;/font&gt;&amp;nbsp;&lt;font color="#0000ff"&gt;&amp;#60;&lt;/font&gt;&lt;font color="#800000"&gt;p&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&amp;#60;/&lt;/font&gt;&lt;font color="#800000"&gt;p&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&lt;/font&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;&amp;#60;&lt;/font&gt;&lt;font color="#800000"&gt;asp:HyperLink&lt;/font&gt; &lt;font color="#ff0000"&gt;ID&lt;/font&gt;&lt;font color="#0000ff"&gt;="linkInfo"&lt;/font&gt;&amp;nbsp;&lt;font color="#ff0000"&gt;runat&lt;/font&gt;&lt;font color="#0000ff"&gt;="server"&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;&amp;#60;&lt;/font&gt;&lt;font color="#800000"&gt;asp:Label&lt;/font&gt; &lt;font color="#ff0000"&gt;ID&lt;/font&gt;&lt;font color="#0000ff"&gt;="labLink"&lt;/font&gt; &lt;font color="#ff0000"&gt;runat&lt;/font&gt;&lt;font color="#0000ff"&gt;="server"&lt;/font&gt; &lt;font color="#ff0000"&gt;Text&lt;/font&gt;&lt;font color="#0000ff"&gt;=""&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&amp;#60;/&lt;/font&gt;&lt;font color="#800000"&gt;asp:Label&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;&amp;#60;/&lt;/font&gt;&lt;font color="#800000"&gt;asp:HyperLink&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;&amp;#60;/&lt;/font&gt;&lt;font color="#800000"&gt;asp:Panel&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;#62;&lt;/font&gt;&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;&lt;font size="1" color="gray"&gt;* This source code was highlighted with &lt;a href="http://virtser.net/blog/post/source-code-highlighter.aspx"&gt;&lt;font size="1" color="gray"&gt;Source Code Highlighter&lt;/font&gt;&lt;/a&gt;.&lt;/font&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Мы видим, что внутри панели размещается одна метка (текст сообщения), а также ссылка, внутри которой размещается еще одна метка. Это будет та ссылка, по которой мы предложим перейти пользователю, и по которой он обязательно перейдет, если его браузер не поддерживает автоматическое перенаправление.&lt;br /&gt;&lt;br /&gt;Теперь заглянем в код. У меня это выглядит так:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;font size="2" face="Courier New" color="black"&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt; &lt;font color="#0000ff"&gt;void&lt;/font&gt; Activate(&lt;font color="#0000ff"&gt;bool&lt;/font&gt;? IsResultOK, &lt;font color="#0000ff"&gt;string&lt;/font&gt; Message, &lt;font color="#0000ff"&gt;string&lt;/font&gt; LinkLabelCode, &lt;font color="#0000ff"&gt;string&lt;/font&gt; Link)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;this&lt;/font&gt;.Visible = &lt;font color="#0000ff"&gt;true&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (IsResultOK == &lt;font color="#0000ff"&gt;null&lt;/font&gt;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;panelInfo.CssClass = &lt;font color="#A31515"&gt;"PanelInfoFYI"&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (IsResultOK.Value)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;panelInfo.CssClass = &lt;font color="#A31515"&gt;"PanelInfoOK"&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;else&lt;/font&gt; &lt;font color="#0000ff"&gt;if&lt;/font&gt; (!IsResultOK.Value)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;panelInfo.CssClass = &lt;font color="#A31515"&gt;"PanelInfoFail"&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (Message != &lt;font color="#0000ff"&gt;string&lt;/font&gt;.Empty)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;labInfo.Text = Message;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (LinkLabelCode != &lt;font color="#0000ff"&gt;string&lt;/font&gt;.Empty &amp;#38;&amp;#38; Link != &lt;font color="#0000ff"&gt;string&lt;/font&gt;.Empty)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;linkInfo.Visible = &lt;font color="#0000ff"&gt;true&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;string&lt;/font&gt; linkText = LinkLabelCode;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;linkInfo.Text = linkText;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;labLink.Text = linkText;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;linkInfo.NavigateUrl = Link;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;else&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;linkInfo.Visible = &lt;font color="#0000ff"&gt;false&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;&lt;font size="1" color="gray"&gt;* This source code was highlighted with &lt;a href="http://virtser.net/blog/post/source-code-highlighter.aspx"&gt;&lt;font size="1" color="gray"&gt;Source Code Highlighter&lt;/font&gt;&lt;/a&gt;.&lt;/font&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Первый параметр говорит о том, каков результат - если все плохо (false), то подключаем стиль ошибки, если хорошо (true) - положительный результат. Иногда требуется просто проинформировать пользователя, в этом случае передадим null и отобразим сообщение голубеньким.&lt;br /&gt;&lt;br /&gt;Теперь посмотрим, как происходит вызов этого метода. После того, как мы положили на форму наш контрол (не забываем о регистрации - &lt;%@ Register src="../Controls/InfoPanel.ascx" tagname="InfoPanel" tagprefix="controls" %&gt; ), мы можем обращаться к нему напрямую:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;if (result)&lt;br /&gt;panelInfo.Activate(true, Messages.LanguageSaveOK, Messages.LabelLinkToLanguages, Pages.Languages);&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Вот тут немного интереснее. Что это за Messages и Pages? Это имена ресурсных файлов (вернее, классов, которые сгенерировались как только мы добавили файл ресурса). Зачем, спрашивается? Нам нужно, чтобы ссылка отображала текст. Мы, конечно, можем просто захардкодить этот текст, но, во-первых, мы с самого начала договорились, что будем писать красивый код, а во-вторых, и это, конечно, главное, подобный подход может привести к очень большому геморрою в будущем. Представим себе, что мы таким образом захардкодили сообщений эдак... двести по всему проекту... а потом пришла директива сверху: все сообщения начинать со слов "Дорогой пользователь!" И вот мы ползаем по сайту, и, чертыхаясь, исправляем все двести сообщений... &lt;br /&gt;&lt;br /&gt;В-общем, наши сообщения мы будем хранить в ресурсных файлах. Если что, его даже без проблем можно будет перевести на другие языки. Но сейчас для наших целей нам нужно два обычных ресурсных файла. Один - для сообщений, второй - для адресов страниц. Второй, возможно, нам в будущем не понадобится (пока не могу точно сказать), но пока что он избавляет от необходимости хардкодить ссылки, по которым надо переходить.&lt;br /&gt;&lt;br /&gt;Можно, конечно, хранить сообщения в отдельном классе, но по-моему, в ресурсах хранить удобнее. Теперь мы с легкостью можем ссылаться на любые переменные, добавленные в текстовый ресурс, кроме того, студия позволяет сортировать переменные по именам и значениям, что, согласитесь, очень удобно. Значения, которые мы забиваем в табличке ресурсов, можно посмотреть в файле &lt;НазваниеРесурса&gt;.designer.cs&lt;br /&gt;&lt;br /&gt;В следующем шаге мы перенаправим пользователя на указанную страницу.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-125960399741138051?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/125960399741138051/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=125960399741138051' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/125960399741138051'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/125960399741138051'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2010/03/cms-4.html' title='Пишем CMS. Шаг № 4. Показываем пользователю результат действия'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-3598831115707880979</id><published>2010-03-05T12:50:00.000-08:00</published><updated>2010-03-05T13:13:09.053-08:00</updated><title type='text'>Пишем CMS. Шаг № 3. Обработка действий пользователя</title><content type='html'>Не волнуйтесь, друзья, все в порядке. Никто ничего не забросил. Шла работа мысли. Оказывается, такие достаточно простые вопросы, как оповещение пользователя и разработка поведения системы в целом вовсе не являются простыми. То есть то, что получилось в результате, конечно, не является биномом Ньютона, однако, как показывает практика, огромное количество сайтостроителей правильный, хотя и простой подход игнорируют начисто. Я имею в виду либо отсутствие таких очевидных вещей, как редирект на нужную страницу, либо отсутствие вообще какой-то информабельности. Интернет-серферы меня поймут - как, бывает, бесит, когда регистрация заканчивается ничем! И непонятно, зарегистрировала тебя система или нет, а если да, то почему ничего не сказала?&lt;br /&gt;&lt;br /&gt;Рассмотрим несколько моделей поведения системы.&lt;br /&gt;1. Пользователь нажимает кнопку "Сохранить", система делает Postback и на странице появляется сообщение, что все в порядке. И что потом? Если пользователь нажмет кнопку "Сохранить" или "Отправить", что произойдет? Это зависит от того, что вообще происходит. Вполне может вылететь ошибка, если это, например, удаление, и производится дважды. И вообще, зачем, например, опять показывать пользователю форму с только что введенными данными, если все прошло нормально? Фтопку.&lt;br /&gt;&lt;br /&gt;2. Пользователь нажимает кнопку "Сохранить", система благополучно перенаправляет его на список чего-нибудь (где пользователь увидит добавленное). Немного лучше. По крайней мере F5 не будет вызывать ошибку. Минусы этого дела:&lt;br /&gt;- чтобы страница могла определить, какое сообщение показывать, очевидно, надо передавать его через строку запроса, которая будет иметь некрасивый вид;&lt;br /&gt;- гораздо более важно то, что если перенаправление в случае ошибки производится на эту же страницу, пользователь теряет все введенные данные.&lt;br /&gt;Не подходит тоже.&lt;br /&gt;&lt;br /&gt;3. Нечно среднее (как сделано в форуме PHPbb). Пользователь видит сообщение об успешной отправке данных 3-4 секунды, после чего система автоматически перенаправляет его на нужную страницу. В случае ошибки пользователь также видит сообщение, перенаправления не происходит, и все данные, введенные пользователем, по-прежнему видны. Если браузер не поддерживает автоматическое перенаправление, пользователь видит на странице, где сообщение, ссылку, и переходит по ней вручную. По-моему, отличный подход.&lt;br /&gt;&lt;br /&gt;Вот его мы и будем реализовывать. Рассмотрим сценарий с точки зрения asp.net. Пользователь нажимает кнопку, происходит Postback, мы возвращаемся на ту же страницу, обрабатываем введенные данные. Если все в порядке, показываем сообщение (а саму форму можно спрятать, чтобы не путать пользователя) со ссылкой, примерно 3-4 секунды, после чего перенаправляем пользователя на нужную страницу. Если ошибка, ничего особенного не делаем, просто показываем сообщение. Все данные, введенные пользователем, в результате будут сохранены. &lt;br /&gt;&lt;br /&gt;Теперь разобьем задачу на несколько подзадач:&lt;br /&gt;1. Как показывать сообщение и ссылку&lt;br /&gt;2. Как обрабатывать данные&lt;br /&gt;3. Как перенаправить пользователя&lt;br /&gt;&lt;br /&gt;Начнем с первой задачи, поскольку она независима, и будет намного лучше, если к моменту обработки данных мы будем знать, что делать с результатом. Как организовать сообщение о результате - в следующем шаге.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-3598831115707880979?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/3598831115707880979/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=3598831115707880979' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/3598831115707880979'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/3598831115707880979'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2010/03/cms-3.html' title='Пишем CMS. Шаг № 3. Обработка действий пользователя'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-4126693733175522197</id><published>2010-02-18T09:03:00.000-08:00</published><updated>2010-02-18T09:13:30.127-08:00</updated><title type='text'>Пишем CMS. Совет - удаленная отладка</title><content type='html'>Иногда бывает так, что кажется - невозможно понять, откуда у ошибки ноги растут. Или как решить данную проблему. В этом случае мне всегда помогает удаленная отладка. "Удаленная" - это не про код и не про машину, а про разработчика. Т.е. разработчик в прямом смысле слова удален от компьютера и кода. Можно пойти в душ, хорошо думается на обратном пути к дому. А главное - ничего не отвлекает. Когда вы сидите за компьютером и мельтешите по коду, замыливаются и глаз, и мозг. Когда код только в уме, это настоящая квинтэссенция мышления. Какое-то там сияние чистого разума. Рекомендую. Во время удаленной отладки надо представить себе вызовы, что почему вызывается и где что может ломаться. То же самое и с программированием - мысленно располагайте проблему перед собой и ищите пути решения. Это даже лучше работает, чем листок бумаги. Серьезно!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-4126693733175522197?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/4126693733175522197/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=4126693733175522197' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/4126693733175522197'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/4126693733175522197'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2010/02/cms_18.html' title='Пишем CMS. Совет - удаленная отладка'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-7970000547875022706</id><published>2010-02-17T18:43:00.000-08:00</published><updated>2010-02-18T15:28:03.223-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Entity Framework'/><category scheme='http://www.blogger.com/atom/ns#' term='CMS'/><title type='text'>Пишем CMS. Шаг № 2. Подключаем ADO.NET Entitry Framework</title><content type='html'>Сейчас нам надо разобраться, как работать с Entity Framework. Этот момент довольно подробно описан в моем старом посте &lt;a href="http://pure-development.blogspot.com/2008/04/5-adonet-entity-framework.html"&gt;здесь&lt;/a&gt;, поэтому я не буду повторяться, а лишь опишу шаги и действия, которые надо сделать, чтобы:&lt;br /&gt;1) Сгенерировать классы для работы с БД&lt;br /&gt;2) Просмотреть список доступных языков.&lt;br /&gt;Не так уж много, но для этого шага достаточно.&lt;br /&gt;&lt;br /&gt;Итак, создайте в солюшене новый проект, который назовите, например, DAL (data access layer). После создания проекта рекомендуется указать в его свойствах название dll, которая будет сгенерировать и пространство имен, создаваемое по умолчанию. Общепринятой практикой является давать названия в формате &lt;Название производителя&gt;.&lt;Название всего проекта&gt;.&lt;Название конкретного проекта&gt;.&lt;br /&gt;&lt;br /&gt;После этого добавьте в проект новый айтем - выберите ADO.NET Entity Data Model. На экране появится визард, в котором вы указываете сервер базы данных (выберите сперва Generate from database), затем укажите таблицы, которые будут использованы для генерирования классов. После этого в вашем проекте появится файлы .edmx и .designer.cs.&lt;br /&gt;&lt;br /&gt;Поскольку мы будем вызывать методы из классы из нашего веб-проекта (на данном этапе), нужно скопировать строку подключения из файла app.config в файл web.config нашего веб-проекта.&lt;br /&gt;&lt;br /&gt;Теперь в веб-проекте создайте каталог Language и добавьте в него две формы (на основе Master page - это будут Web Content Form). Про мастер-страницу мы поговорим немного позже, а пока, если вы еще не создали ее, то создайте, и добавьте две страницы Languages.aspx и LanguageEdit.aspx. Первая страница позволит нам просматривать список языков и удалять ненужные, вторая - создавать новые и редактировать уже имеющиеся языки.&lt;br /&gt;&lt;br /&gt;Откройте страницу Languages.aspx и разместите на ней контрол GridView (на самом деле вы можете разместить любой другой контрол, позволяющий просматривать данные в режиме таблицы, но пусть пока будет этот). Выставьте свойство AutoGenerateColumns = false (нам не нужны столбцы, генерируемые автоматически) и добавьте в таблицу столбцы типа BoundField, для каждого столбца укажите в поле DataField соответствующее поле таблицы. После этого добавьте в референсы проекта референс на наш DAL-проект, затем откройте файл кода Languages.aspx.cs и добавьте в список пространств имен наше пространство имен .DAL.&lt;br /&gt;&lt;br /&gt;Создайте метод, например LoadLanguages и добавьте в него простой код, который будет вытягивать все наши языки из базы (пейджинг пока не рассматриваем).&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;private void LoadLanguages()&lt;br /&gt;        {&lt;br /&gt;            using (WebDatabaseConnection context = new WebDatabaseConnection())&lt;br /&gt;            {&lt;br /&gt;                var query = from l in context.Language&lt;br /&gt;                            select l;&lt;br /&gt;                gridLanguages.DataSource = query;&lt;br /&gt;                gridLanguages.DataBind();&lt;br /&gt;                &lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Здесь WebDatabaseConnection context - это главный класс для работы с базой данных. Он был сгенерировать Entity Framework и найти его можно в соответствующем файле нашего DAL-проекта. Разумеется, этот код следует обрамить конструкцией try-catch, но мы этого пока делать не будем. Во-первых, потому, что нам надо четко решить, что делать в случае возникновения ошибки, а во-вторых, неплохо бы знать, какое исключение может быть выброшено, чтобы правильно интерпретировать результат. Дело в том, что мы не можем показывать пользователю исходное сообщение, которое будет содержать эксепшн, это противоречит принципам безопасности, да и не нужно особо - ведь уровень подготовки пользователя неизвестен. Поэтому обработкой ошибок мы займемся чуть позже. &lt;br /&gt;&lt;br /&gt;Заметьте, что в этом коде мы используем простейшую конструкцию Linq to Entity Framework, чтобы получить все данные из таблицы.&lt;br /&gt;&lt;br /&gt;Теперь в код метода PageLoad мы разместим вызов нашего метода, который будет грузить данные в таблицу:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;protected void Page_Load(object sender, EventArgs e)&lt;br /&gt;        {&lt;br /&gt;            if (!Page.IsPostBack)&lt;br /&gt;            {&lt;br /&gt;                LoadLanguages();&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Здесь мы сначала проверяем, является ли вызов обратным (postback) - это может случиться только в том случае, если мы производим какую-либо обработку данных, поэтому отображать данные нам в этот момент не нужно. Как же так? - спросите вы. А очень просто. Дело в том, что после каждой обработки данных мы будем делать редирект на эту же страницу. Объясню, зачем. Если мы ограничимся одним постбэком и откроем эту же страницу после постбэка без редиректа, а затем нажмем F5, чтобы обновить страницу, браузер покажет сообщение о том, что мы собираемся заново отправить данные. Допустим, мы перед этим удалили язык. У нас будет выбор - отправить данные заново или нажать Cancel. Если мы отменим действие, обновления страницы не произойдет, если нажмем Resend, наше приложение получит повторное сообщение об удалении языка, который уже был благополучно удален, в результате чего мы увидим ошибку. Конечно, можно эту ошибку обработать, но зачем? Куда как проще открыть страницу заново и, сколько мы не будем нажимать F5, повторного удаления (или иного действия) не будет.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-7970000547875022706?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/7970000547875022706/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=7970000547875022706' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/7970000547875022706'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/7970000547875022706'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2010/02/cms-2.html' title='Пишем CMS. Шаг № 2. Подключаем ADO.NET Entitry Framework'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-539019959411962968</id><published>2010-02-12T16:28:00.000-08:00</published><updated>2010-02-18T15:29:32.791-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SNV'/><category scheme='http://www.blogger.com/atom/ns#' term='CMS'/><category scheme='http://www.blogger.com/atom/ns#' term='NUnit'/><category scheme='http://www.blogger.com/atom/ns#' term='подготовка'/><category scheme='http://www.blogger.com/atom/ns#' term='log4net'/><title type='text'>Пишем CMS. Шаг № 1. Подготовительная работа</title><content type='html'>Прежде всего, определимся со структурой-архитектурой-технологией. Технология - используем связку ASP.NET 3.5 + SQL Server 2005, все это будем делать в бесплатной студии 2008 (то есть конечно, наверное, не всем она досталась бесплатно, но мне лично удалось получить ее на дне разработчика). В процессе разработки надо будет постараться использовать преимущества асп3.5.&lt;br /&gt;Структура сайта проста - есть основной проект, параллельно ему существует админский, а также библиотека для DAL (кто не знает, что это, отсылаю к википедии).&lt;br /&gt;&lt;br /&gt;Пока что мне не нравится идея создавать каждую страницу на лету, поэтому сейчас мы этого делать не будем, а будем создавать спокойно странички для каждого раздела в отдельности. Например, для управления языками у нас будет две страницы Languages.aspx и LanguageEdit.aspx, которые мы положим в каталог Languages. Вполне возможно, что в процессе работы эти страницы исчезнут, но не будем загадывать раньше времени. А пока надо сделать несколько важных вещей.&lt;br /&gt;&lt;br /&gt;1. Настроить систему контроля версий. Как это сделать, очень хорошо описано вот в этой книге: &lt;a href="http://www.free-ebooks-download.org/free-ebook/dotnet/ASP.NET/asp-net-3-5-social-networking.php"&gt;ASP.NET 3.5 Social Networking &lt;/a&gt;. Вообще классная книга, рекомендую (шепотом: на этом сайте ее можно скачать; disclaimer: а у меня бумажная, толстая). Можно читать вместо моего блога. Но меня чтение подобных книг неизменно уводит в сторону, я не знаю, почему. Вот еще хорошая книга &lt;a href="http://www.ozon.ru/context/detail/id/3308076/"&gt;Разработка Web-приложений в среде ASP.NET 2.0. Задача - проект - решение&lt;/a&gt;. Но код в этой книге ужасен. Поэтому рекомендую только для поиска конкретных решений, но ни в коем случае не как пример для подражания.&lt;br /&gt;&lt;br /&gt;2. Чтобы вас с самого начала не тошнило от собственного сайта, рекомендую найти ему подходящий шаблон. Его всегда можно будет изменить, а так хоть будем видеть, что где показывать. Огромное количество бесплатных классных шаблонов здесь &lt;a href="http://www.oswd.org/"&gt;http://www.oswd.org/&lt;/a&gt;. Рекомендую сразу же поменять картинки и цвета, а то, шарясь по интернету, вы с удивлением будете обнаруживать своей собственный сайт, только с другой начинкой. =))&lt;br /&gt;&lt;br /&gt;3. Создать БД, таблицы. Для организации локализации нам нужна таблица, в которой мы будем хранить название языка, его код (типа "ru-RU") и название файла иконки.&lt;br /&gt;&lt;br /&gt;4. Ну и не забудьте про TDD! Я обычно использую NUnit, очень удобная штука. Для тестов стоит создать отдельный проект. Мне кажется, все знают, как подсоединить NUnit к проекту для тестирования, но если кто-то забыл, спрашивайте.&lt;br /&gt;&lt;br /&gt;5. Также желательно подключить и настроить log4net для логгирования событий (если это вам, конечно, нужно). В той же книжке все это рассказывается тоже. С логгированием надо быть осторожным, легко можно переборщить. Сохранять в логе слеует только действительно необходимую информацию, а не все подряд. Иначе вы рискуете иметь миллионы никому не нужных записей.&lt;br /&gt;&lt;br /&gt;Следующим номером: очень краткий курс по работе с ADO.NET Entity Framework на примере работы с языками.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-539019959411962968?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/539019959411962968/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=539019959411962968' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/539019959411962968'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/539019959411962968'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2010/02/cms_12.html' title='Пишем CMS. Шаг № 1. Подготовительная работа'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-3298769890003312444</id><published>2010-02-12T15:32:00.000-08:00</published><updated>2010-02-12T16:18:04.524-08:00</updated><title type='text'>Пишем CMS или любителям изобретать велосипеды</title><content type='html'>Не знаю, как вам, а мне периодически хочется сделать какой-нибудь интересный сайт или сервис. Иногда довожу свою идею до конца, чаще бросаю на середине. Но каждый раз в самом начале встает вопрос - делать с нуля или воспользоваться готовой CMS? Сначала, конечно, я начинаю шарить по интернету в поисках приличной CMS. Ну, я люблю C# и ASP.NET, а не PHP и VB.NET, поэтому список подозреваемых невелик. Но и он очень быстро сужается - почти сразу же выясняется что данная CMS или глючная или очень медленная или какая-то слишком крутая, а мне надо попроще. И, конечно, остается в итоге один вариант - опять писать с нуля. Конечно, мой сайт будет также содержать глюки и тормозить, но это будут мои глюки, которые я постепенно исправлю, и тормоза, наверное, со временем пройдут как-нибудь... сами... Но это ладно, самое-то глупое во всей этой ситуации - писать с нуля один и тот же код. Потому что почти все сайты содержат примерно одинаковую информацию и функционируют по сути совершенно одинаково. Поэтому можно попытаться создать ну не CMS, конечно, это громко сказано, а так... шаблон... фреймворк, ну что-то такое, что можно использовать и потом.&lt;br /&gt;Ну что ж, сказано-сделано.&lt;br /&gt;Во-первых, что за сайт. Сейчас мне нужно сделать один довольно простой сайт, который будет содержать информацию о программном продукте. Возможно, в дальнейшем в него будет добавлено что-то вроде соцсети и магазин. А пока - новости, локализация, фидбэк, страница со скриншотами ну и разное по мелочи. Самое главное - возможность быстро в админке добавлять новости, информацию о продукте, скриншоты, менять иконки флажков для языков, возможность быстро и безболезненно добавить новый язык. Ничего сложного и навороченного, как видите. Можно было бы вообще не писать все это, если бы не одно но. В процессе работы меня периодически осеняет. Ну не то чтобы конечно, в прямом смысле, но все время приходят разные идеи, как можно отрефакторить, как можно сделать удобнее. Меня бесит, например, когда код разметки смешан c JavaScript-кодом, когда переадресация на нужную страницу делается в коде, и стоит потом эту страницу переместить, как переадресация валится, и все в таком же духе. Поэтому я буду свои идеи по ходу дела выкладывать сюда. Если конечно, мне не надоест. А называться это безобразие будет "Пишем CMS. 100 шагов". Я, конечно, не думаю, что приличную CMS можно написать даже за 300 шагов, но, во-первых, 100 звучит красиво и солидно, во-вторых, у меня нет уверенности, что меня хватит даже на 50, в третьих, накрайняк всегда можно поменять первую цифру. &lt;br /&gt;Поехали!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-3298769890003312444?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/3298769890003312444/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=3298769890003312444' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/3298769890003312444'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/3298769890003312444'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2010/02/cms.html' title='Пишем CMS или любителям изобретать велосипеды'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-2380200633687144523</id><published>2008-08-07T13:44:00.000-07:00</published><updated>2008-08-07T13:55:23.966-07:00</updated><title type='text'>Entity Framework - использование хранимых процедур</title><content type='html'>Предполагается, что большинство операций над данными в базе можно выполнить с использование LINQ. Но, конечно, это не всегда удобно и оптимально. Поэтому когда запрос более-менее сложен, я лично предпочитаю использовать хранимые процедуры. Остался лишь вопрос - как их дергать в коде?&lt;br /&gt;&lt;br /&gt;Оказалось, очень просто. Ну, во-первых, надо создать саму процедуру. Во-вторых, надо обновить модель - т.е. в &lt;strong&gt;ModelBrowser&lt;/strong&gt; щелкнуть правой кнопкой мыши на модели и выбрать &lt;strong&gt;Update Model From Database&lt;/strong&gt; и там уже выбрать процедуру. После этого она добавляется в модель, но как-то не до конца - в коде она не появится до тех пор, пока вы ей не скажете. А для этого недо щелкнуть на процедуре (в модели) и выбрать &lt;strong&gt;Create Function Import&lt;/strong&gt;. На экране появится окошко, в котором вас спросят, как назвать будущий метод, и что он будет возвращать. После этого можно обращаться к процедуре через контекст, причем обращение как к обычному методу, т.е.&lt;br /&gt;&lt;br /&gt;context.Proc(parameters)&lt;br /&gt;&lt;br /&gt;Один момент, который следует учесть. Если вы говорите, что процедура возвращает набор записей, которые могут быть приведены к какому-то классу, и если этот класс - полученный из других таблиц, но набор возвращаемых столбцов должен точно соответствовать набору столбцов (членов т.е.) для этого класса. Например, вы говорите, что будущий метод возвращает набор типа Client, а таблица Client в базе содержит три поля: Id, Name, Address. Так вот ваша процедура должна точно возвратить три этих столбца, а если она будет выбирать, например, только два из них, то будут выскакивать ошибки.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-2380200633687144523?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/2380200633687144523/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=2380200633687144523' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/2380200633687144523'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/2380200633687144523'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2008/08/entity-framework.html' title='Entity Framework - использование хранимых процедур'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-8666604398413448280</id><published>2008-06-20T12:14:00.002-07:00</published><updated>2008-06-21T07:07:15.390-07:00</updated><title type='text'>MVC - создание и использование повторно используемых компонентов</title><content type='html'>Итак, встала передо мной несложная, в-общем, задача - в зависимости от элемента, выбранного в выпадающем списке добавить, на страницу соответствующий блок с контролами. Данные берутся из базы, никаких перезагрузок страницы быть не  должно. В результате мне удалось найти три способа, решающих данную задачу.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Способ первый. Использование вложенных представлений&lt;/strong&gt;&lt;br /&gt;Оптимальное решение, на мой взгляд - использование контролов, или встроенных представлений. Однако и оно не без греха. Но рассмотрим все по порядку.&lt;br /&gt;&lt;br /&gt;В MS MVC существует класс под названием ComponentController. Это, по сути, обычный контроллер, использующий немного другие методы и сигнатуры. Зачем понадобилось делать дополнительный класс с почти такой же функциональностью, для меня осталась загадка. Итак, вот как используется этот класс:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt; &lt;font color="#0000ff"&gt;class&lt;/font&gt; CategoryController : ComponentController&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;public&lt;/font&gt; ActionResult Index()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#008000"&gt;// Add action logic here&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;throw&lt;/font&gt; &lt;font color="#0000ff"&gt;new&lt;/font&gt; NotImplementedException();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;public&lt;/font&gt; &lt;font color="#0000ff"&gt;void&lt;/font&gt; RenderControl(&lt;font color="#0000ff"&gt;string&lt;/font&gt; CategoryName)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RenderView(CategoryName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;Т.е. видим два существенных отличия: класс наследует ComponentController, а не просто Controller, и второе - по-другому выглядят методы действия. А выглядят они точно также, как выглядели методы действия обычных контроллеров раньше - т.е. возвращают void и рендерят представление с помощью RenderView.&lt;br /&gt;&lt;br /&gt;В данном случае имя представления передается как параметр, что позволяет вызывает вызывать представления абсолютно произвольно, используя при этом всего один метод контроллера. &lt;br /&gt;&lt;br /&gt;Размещается контроллер вместе с остальными в папке Controllers (думаю, на самом деле эти файлы можно размещать где угодно). А вот с представлением не так все просто. Оно должно лежать в специальной папке Components/[ComponentControllerName]/Views/[ViewName]. Т.е. в корне вы создаете папку Components, в ней произвольную папку, в которой создаете папку Views, а уже сюда кладете файлы представлений.&lt;br /&gt;&lt;br /&gt;Дальше все просто. Контроллер компонента создан, представление - тоже, остается только его вызвать. У меня это сделано так: главная страница загружает выпадающий список, в нем на onchange вызывается функция JavaScript, которая с помощью библиотеки Prototype загружает представление с соответствующими параметрами (подробности работы с Prototype смотри в другой записи блога &lt;a href="http://pure-development.blogspot.com/2008/05/mvc-ajax.html"&gt;тут&lt;/a&gt;). А вот это представление содержит вызов компонента. &lt;br /&gt;&lt;br /&gt;Вызов можно осуществлять двумя способами. Во-первых, используя RenderUserControl() - тогда сам вызов будет выглядеть так:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;&amp;#60;%= Html.RenderUserControl(&lt;font color="#A31515"&gt;"~/Views/SomeView.aspx"&lt;/font&gt;) %&amp;#62;&lt;br /&gt;&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Как видите, здесь указывается непосредственно имя файла. В ряде случае это может быть совершенно неудобно. Например, когда мы на стадии этого вызова еще не знаем имя файла, или хотим передать другой параметр, а метод чтобы сам решал, какое представление ему вызывать. Для этого используем второй способ:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;&amp;#60;% =Html.RenderComponent&amp;#60;CategoryController&amp;#62;(c =&amp;#62; c.RenderControl(&lt;font color="#A31515"&gt;"Public"&lt;/font&gt;)) %&amp;#62;&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Здесь мы просто указываем имя представления, а могли бы, например, передавать какой-нибудь идентификатор. Мы можем брать из модели, загруженной в объект ViewData, любой доступный член. Почему бы не передавать его в качестве параметра, например, вот так:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;&amp;#60;% =Html.RenderComponent&amp;#60;CategoryController&amp;#62;(c =&amp;#62; c.RenderControl(View.Model.PageId)) %&amp;#62;&lt;/font&gt;&lt;/blockquote&gt; &lt;br /&gt;&lt;br /&gt;К сожалению, подобный код не будет работать (из-за бага в MVC). Вы просто увидите ошибку следующего содержания:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Unable to cast object of type 'System.Linq.Expressions.MemberExpression' to type 'System.Linq.Expressions.ConstantExpression'.&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Выход нашел некто Chris Sainty (ссылка на его статью в конце заметки). Просто добавляем следующий метод в файл &lt;strong&gt;вызывающего&lt;/strong&gt; представления, и вместо стандартного Html.RenderComnonent вызываем его:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;&lt;font color="#0000ff"&gt;protected&lt;/font&gt; &lt;font color="#0000ff"&gt;string&lt;/font&gt; RenderComponent(Expression&amp;#60;Action&amp;#60;CategoryController&amp;#62;&amp;#62; action)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;var&lt;/font&gt; controller = &lt;font color="#0000ff"&gt;new&lt;/font&gt; CategoryController();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;controller.Context = &lt;font color="#0000ff"&gt;this&lt;/font&gt;.ViewContext;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;var&lt;/font&gt; ex = action.Compile();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ex.Invoke(controller);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;return&lt;/font&gt; controller.RenderedHtml;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;Вызов будет выглядеть соответственно:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;&amp;#60;% =RenderComponent(c =&amp;#62; c.RenderControl(ViewData.Model.ParentCategoryName)) %&amp;#62;&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;Все работает!&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Способ второй. Подтягиваем разметку Html из базы данных&lt;/strong&gt;&lt;br /&gt;Первый способ, безусловно, хорош. Но у него есть один существенный недостаток - что, если состав вложенных контролов постоянно меняется, т.е. попросту говоря, не является детерменированным? Ничего такого, надо просто каждый раз обновлять версию dll на сервере, и все. Тоже выход, но можно ли обойтись без этого? Можно - например, если хранить код разметки в поле таблицы. Этот вариант подходит для не очень большой и сложной разметки, но зато позволяет обойтись без постоянного деплоя. &lt;br /&gt;&lt;br /&gt;Как это сделать? Очень просто - не забываем, что мы можем передать представлению любой класс, а уже в этом классе не составит труда загрузить любое поле из таблицы. Например, так:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;&lt;font color="#0000ff"&gt;private&lt;/font&gt; &lt;font color="#0000ff"&gt;string&lt;/font&gt; html;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;public&lt;/font&gt; CategoryParameters(&lt;font color="#0000ff"&gt;int&lt;/font&gt;? ParentCategoryId)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;using&lt;/font&gt; (DataCoreConnection context = &lt;font color="#0000ff"&gt;new&lt;/font&gt; DataCoreConnection())&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;var&lt;/font&gt; htm = (&lt;font color="#0000ff"&gt;from&lt;/font&gt; p &lt;font color="#0000ff"&gt;in&lt;/font&gt; context.ItemType&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;where&lt;/font&gt; p.Id == ParentCategoryId&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;select&lt;/font&gt; p.ParametersHTML).ToList();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; (((&lt;font color="#2B91AF"&gt;List&lt;/font&gt;&amp;#60;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&amp;#62;)htm).Count &amp;#62; 0)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;html = ((&lt;font color="#2B91AF"&gt;List&lt;/font&gt;&amp;#60;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&amp;#62;)htm)[0]; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt; &lt;font color="#0000ff"&gt;string&lt;/font&gt; Html &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{ &lt;font color="#0000ff"&gt;get&lt;/font&gt; { &lt;font color="#0000ff"&gt;return&lt;/font&gt; html;} }&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;А вот так выглядит вызов:&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;&amp;#60;%=ViewData.Model.Html %&amp;#62;&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Третий способ. Подтягивание представления с помощью Ajax&lt;/strong&gt;&lt;br /&gt;Подробно о том, как использовать Ajax в проектах MVC, рассказано в другой моей заметке (&lt;a href="http://pure-development.blogspot.com/2008/05/mvc-ajax.html"&gt;тут&lt;/a&gt;), здесь я лишь покажу, как это использовать в наших целях. Вот код JavaScript, который вызывает представление:&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;&lt;font color="#0000ff"&gt;function&lt;/font&gt; LoadCategory(parentCategory)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;var&lt;/font&gt; newDiv = &lt;font color="#0000ff"&gt;document&lt;/font&gt;.createElement(&lt;font color="#A31515"&gt;"div"&lt;/font&gt;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;newDiv.id = &lt;font color="#A31515"&gt;"ControlDiv_"&lt;/font&gt; + parentCategory;&lt;br /&gt;&amp;nbsp;&amp;nbsp;newDiv.className = &lt;font color="#A31515"&gt;"ControlBlock"&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;var&lt;/font&gt; url = &lt;font color="#A31515"&gt;"Controls/GetCategories/"&lt;/font&gt; + parentCategory;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;new&lt;/font&gt; Ajax.Updater(newDiv, url,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;method: &lt;font color="#A31515"&gt;'get'&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;$(&lt;font color="#A31515"&gt;'Container'&lt;/font&gt;).appendChild(newDiv);&lt;br /&gt;}&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;Т.е. здесь мы динамически создаем блок DIV, в который загружаем содержимое представления, а сам блок помещаем в ранее созданный DIV с Id= Container.&lt;br /&gt;&lt;br /&gt;На самой странице же достаточно сделать такой вызов:&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;&amp;#60;script language=&lt;font color="#A31515"&gt;"javascript"&lt;/font&gt; type=&lt;font color="#A31515"&gt;"text/javascript"&lt;/font&gt;&amp;#62;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LoadCategory(1);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;#60;/script&amp;#62;&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Полезные ссылки:&lt;br /&gt;&lt;a href="http://csainty.blogspot.com/2008/03/rendercomponent-or-renderusercontrol.html"&gt;Статья Chris Sainty, посвященная RenderComponent() и RenderUserControl() &lt;/a&gt;&lt;br /&gt;&lt;a href="http://weblogs.asp.net/mikebosch/archive/2008/03/10/using-the-componentcontroller-in-asp-net-mvc.aspx"&gt;Using the ComponentController in ASP.NET MVC CTP 2 - Mike Bosch&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-8666604398413448280?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/8666604398413448280/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=8666604398413448280' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/8666604398413448280'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/8666604398413448280'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2008/06/mvc.html' title='MVC - создание и использование повторно используемых компонентов'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-5967158833137825876</id><published>2008-06-20T12:14:00.001-07:00</published><updated>2008-06-20T12:14:38.940-07:00</updated><title type='text'></title><content type='html'>&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-5967158833137825876?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/5967158833137825876/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=5967158833137825876' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/5967158833137825876'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/5967158833137825876'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2008/06/blog-post.html' title=''/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-3172302041904044288</id><published>2008-06-06T06:22:00.000-07:00</published><updated>2008-06-10T10:09:26.068-07:00</updated><title type='text'>Вышла третья версия ASP.NET MVC (Preview) - перелопачиваем проект (а лучше создаем новый)</title><content type='html'>Значит, вышла, родная. Изменений немерено! &lt;br /&gt;Скачать новую версию можно &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=92F2A8F0-9243-4697-8F9A-FCF6BC9F66AB&amp;displaylang=en"&gt;здесь&lt;/a&gt;. Там на странице внизу список доступных файлов. Обязательно скачайте Readme - он содержит перечень всех изменений. А для тех, кто не очень дружит с английским (ну и вообще - для уяснения) перечислю эти изменения здесь.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Контроллеры&lt;/strong&gt;&lt;br /&gt;Методы действия теперь возвращают экземпляр типа ActionResult. Вернее, один из типов, наследующих ActionResult, потому что ActionResult - абстрактный класс. Вот какие это могут быть типы:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;ViewResult - такой тип возвращает метод контроллера View (то, что раньше было RenderView), параметры остаются те же&lt;/li&gt;&lt;br /&gt;&lt;li&gt;EmptyResult - ничего не делает - аналогичен тому, что метод действия возвращает null&lt;/li&gt;&lt;br /&gt;&lt;li&gt;RedirectResult - перенаправляет на другой URL&lt;/li&gt;&lt;br /&gt;&lt;li&gt;RedirectToRouteResult - перенаправление по указанному маршруту&lt;/li&gt;&lt;br /&gt;&lt;li&gt;JsonResult - содержимое ViewData сериализуется в формат JSON&lt;/li&gt;&lt;br /&gt;&lt;li&gt;ContentResult - рендерит некий текст (обычно это HTML)&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Объекты всех этих типов возвращают соответствующие методы контроллера:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;View&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Redirect - перенаправляет на новый URL&lt;/li&gt;&lt;br /&gt;&lt;li&gt;RedirectToAction - перенаправляет на другой метод действия&lt;/li&gt;&lt;br /&gt;&lt;li&gt;RedirectToRoute - перенаправляет по новому маршруту&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Json - возвращает экземпляр типа JsonResult&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Content - рендерит текст (обычно это HTML)&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Интересный момент. Если мы пытаемся вернуть объект, не наследующий ActionResult (теоритечески это может быть что угодно), то метод неявно сериализует этот объект в строку (вызывая его ToString()), после чего оборачивает полученный текст в ContentResult и передает его клиенту.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Представления&lt;/strong&gt;&lt;br /&gt;Теперь ViewData в представлении не заменяется объектом, из которого мы подтягиваем данные. А мы получаем к ним доступ через свойство Model, т.е. вместо &lt;strong&gt;ViewData.CitiesForCountry&lt;/strong&gt; пишем теперь &lt;strong&gt;ViewData.Model.CitiesForCountry&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Роутинг&lt;/strong&gt;&lt;br /&gt;Дефолтные значения теперь задаются проще. Вместо:&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;routes.Add(&lt;font color="#A31515"&gt;"route-name"&lt;/font&gt;, &lt;font color="#0000ff"&gt;new&lt;/font&gt; Route(&lt;font color="#A31515"&gt;"{locale}/{year}"&lt;/font&gt;, &lt;font color="#0000ff"&gt;new&lt;/font&gt; MvcRouteHandler()) {&lt;br /&gt;&amp;nbsp;Defaults = &lt;font color="#0000ff"&gt;new&lt;/font&gt; RouteValueDictionary(&lt;font color="#0000ff"&gt;new&lt;/font&gt; {locale=&lt;font color="#A31515"&gt;"en-US"&lt;/font&gt;, year=&lt;font color="#2B91AF"&gt;DateTime&lt;/font&gt;.Now.Year.ToString()})&lt;br /&gt;}&lt;br /&gt;&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;теперь чуть проще&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;routes.MapRoute(&lt;font color="#A31515"&gt;"route-name"&lt;/font&gt;, &lt;font color="#A31515"&gt;"{locale}/{year}"&lt;/font&gt;, &lt;font color="#0000ff"&gt;new&lt;/font&gt; {locale=&lt;font color="#A31515"&gt;"en-US"&lt;/font&gt;, year=&lt;font color="#2B91AF"&gt;DateTime&lt;/font&gt;.Now.Year.ToString()});&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Это основные новшества. Некоторые моменты касаются констрейнтов, контролов и т.д. &lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Установка&lt;/strong&gt;&lt;br /&gt;Поверх не ставится. Сначала надо удалить вторую версию.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Изменения в проекте&lt;/strong&gt;&lt;br /&gt;Попробуем скомпилировать наш маленький (пока еще) проект. Ну что ж, для начала неплохо - всего 24 ошибки.&lt;br /&gt;Первое, что делаем - удаляем из списка ссылок ссылки на System.Web.Abstractions, System.Web.MVC, System.Web.Routing и заново добавляем их из C:\Program Files\Microsoft ASP.NET\ASP.NET MVC Preview 3\Assemblies. Компилируем опять, уже лучше - всего 14 ошибок.&lt;br /&gt;Далее по тексту файла Readme меняем код. Все компилируется отлично, но одна маленькая проблема - полностью перестает работать роутинг. Т.е. вообще полностью - хотя маршруты и сохраняются в коллекции, однако перехода к методам контролов не осуществляется. Поэтому мне не осталось ничего другого, как создать новый проект, в котором это работает. Мне, к сожалению, так и не удалось найти "5 отличий" между измененным проектом и новым. Если кто-нибудь знает, как это пофиксить, сообщите пожалуйста.&lt;br /&gt;&lt;strong&gt;Update&lt;/strong&gt; Обнаружилась еще одна неприятная фича (которая, конечно, скорее бага). Если у вас модельная часть с edmx находится в отдельном проекте, то из проекта MVC не удалется получить к ней доступ. Т.е. компилируется проект нормально, но при обращении к странице, которая дергает данные из модели, вываливаются непонятные ошибки вроде "включите ссылку на namespace System.Data.Entity". После того, как на страницу импортируешь это пространство имен, начинает кричать, что он не знает, что такое System.Data.Objects. Как только модель была перенесена в проект MVC, подобные ошибки сразу же исчезли. Кто знает, как лечить по-другому, сообщайте.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-3172302041904044288?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/3172302041904044288/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=3172302041904044288' title='Комментарии: 4'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/3172302041904044288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/3172302041904044288'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2008/06/aspnet-mvc-preview.html' title='Вышла третья версия ASP.NET MVC (Preview) - перелопачиваем проект (а лучше создаем новый)'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-5343005509157953108</id><published>2008-05-18T13:07:00.000-07:00</published><updated>2008-05-22T12:22:20.775-07:00</updated><title type='text'>MVC и Ajax</title><content type='html'>Создавая проект на основе MVC, вы можете использовать Ajax, причем сделать это совсем несложно. Кто-то захочет использовать самописный код, а может быть, библиотеку Microsoft Ajax, я же предпочитаю использовать простую и удобную библиотеку &lt;a href="http://prototypejs.org/"&gt;Prototype&lt;/a&gt;. Единственный видимый ее недостаток - размер (123 К), который устраняется сжатием. Например, можно воспользоваться &lt;a href="http://javascriptcompressor.com/"&gt;вот этим онлайновым компрессором&lt;/a&gt;, после которого файл уменьшается до 50К, что вполне приемлемо.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Задача&lt;/strong&gt;&lt;br /&gt;Мне требуется сделать следующую, довольно простую штуку: есть два комбо, в одном перечислены страны, а в другом надо обновлять список городов для данной страны. Т.е. выбрали мы, например, Russia в первом комбо, во втором должны появиться Moscow, SPb и так далее. И естественно, без перезагрузки страницы. &lt;br /&gt;&lt;br /&gt;&lt;strong&gt;План&lt;/strong&gt;&lt;br /&gt;Задача на первый взгляд простая и даже тривиальная, и решается она действительно просто. Но давайте в учебных целях подробно рассмотрим план решения.&lt;br /&gt;1. БД - в базе должны были страны и города&lt;br /&gt;2. Entity Framework - интерфейс для доступа к данным.&lt;br /&gt;3. Модель - должен обеспечивать как выдачу списка доступных стран, так и списка городов для страны.&lt;br /&gt;4. Контроллер - должен иметь методы действий для отображения всей страницы (на которой размещаются комбо) и части (обновление второго комбо).&lt;br /&gt;5. Представления - отображение данных.&lt;br /&gt;6. Ajax (prototype) - код для подтягивания данных по событию комбо.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;1. База данных&lt;/strong&gt;&lt;br /&gt;Заведем две таблицы - города и страны. Я не очень доверяю числовым идентификаторам, т.е. я им доверяю, но связывать такие справочные таблицы предпочитаю с помощью более долговечных вещей. Поэтому я ввожу поле кода для страны, а в таблице городов делаю ссылку на это поле (FK т.е.).&lt;br /&gt;&lt;a href="http://bp0.blogger.com/_STb7hsxmscM/SDHfKh3TqaI/AAAAAAAAADU/YkqgumvWV1I/s1600-h/CitiesCountries.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_STb7hsxmscM/SDHfKh3TqaI/AAAAAAAAADU/YkqgumvWV1I/s400/CitiesCountries.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5202184416571664802" /&gt;&lt;/a&gt;&lt;br /&gt;Тут есть еще дистрикты, но они нам в данный момент не интересны.&lt;br /&gt;После этого можно забить нужные вам данные (в дальнейшем это, конечно, надо делать с помощью админского сайта).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;2. Entity Framework&lt;/strong&gt;&lt;br /&gt;После создания таблиц надо сгенерировать код для доступа к ним. Можно просто апдейтить нашу модель, но у меня, например, студия сглюкнула и пришлось удалять модель полностью, а затем создавать заново. Чтобы обновить модель, надо перейти в  режим Model browser (дважды щелкнуть на файле .edmx - мне такой способ не очень нравится, но другого я не знаю). Затем выбрать корневой элемент в дереве и выполнить команду Update model from database. Далее вы просто выбираете новые таблицы и жмете Finish. Если все пройдет нормально, вы получите обновленную модель и обновленный код доступа к данным.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;3. Модель&lt;/strong&gt;&lt;br /&gt;В каталоге Models проекта создадим два класса - с помощью первого будем извлекать статические данные для первого комбобокса (страны), с помощью второго - динамического для комбо с городами. &lt;br /&gt;Итак, первый класс - StaticData - содержит одно публичное свойство и приватный метод, который забирает из базы список стран.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;public&lt;/font&gt; &lt;font color="#0000ff"&gt;class&lt;/font&gt; StaticData&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;public&lt;/font&gt; &lt;font color="#2B91AF"&gt;List&lt;/font&gt;&amp;#60;Country&amp;#62; CountryList&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;get&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;return&lt;/font&gt; GetAllCountries();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;private&lt;/font&gt; &lt;font color="#0000ff"&gt;static&lt;/font&gt; &lt;font color="#2B91AF"&gt;List&lt;/font&gt;&amp;#60;Country&amp;#62; GetAllCountries()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#2B91AF"&gt;List&lt;/font&gt;&amp;#60;Country&amp;#62; countries = &lt;font color="#0000ff"&gt;null&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;using&lt;/font&gt; (DataCoreConnection context = &lt;font color="#0000ff"&gt;new&lt;/font&gt; DataCoreConnection())&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;countries = (&lt;font color="#0000ff"&gt;from&lt;/font&gt; c &lt;font color="#0000ff"&gt;in&lt;/font&gt; context.Country&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;select&lt;/font&gt; c).ToList();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;return&lt;/font&gt; countries;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;/font&gt;&lt;font size='1' color='gray'&gt;&lt;br /&gt;&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Код простейший, добавлю лишь, что DataCoreConnection - это класс типа System.Data.Objects.ObjectContext из файла, сгенерированного при построении модели. &lt;br /&gt;Второй класс аналогичен первому, за исключением конструктора, в который передается код страны:&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt; &lt;font color="#0000ff"&gt;class&lt;/font&gt; Cities&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;private&lt;/font&gt; &lt;font color="#0000ff"&gt;string&lt;/font&gt; countryCode;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;public&lt;/font&gt; Cities(&lt;font color="#0000ff"&gt;string&lt;/font&gt; CountryCode)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;countryCode = CountryCode;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;public&lt;/font&gt; &lt;font color="#2B91AF"&gt;List&lt;/font&gt;&amp;#60;City&amp;#62; CitiesForCountry&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;get&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;return&lt;/font&gt; GetCitiesForCountry(countryCode);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;private&lt;/font&gt; &lt;font color="#2B91AF"&gt;List&lt;/font&gt;&amp;#60;City&amp;#62; GetCitiesForCountry(&lt;font color="#0000ff"&gt;string&lt;/font&gt; CountryCode)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#2B91AF"&gt;List&lt;/font&gt;&amp;#60;City&amp;#62; list = &lt;font color="#0000ff"&gt;null&lt;/font&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;using&lt;/font&gt; (DataCoreConnection context = &lt;font color="#0000ff"&gt;new&lt;/font&gt; DataCoreConnection())&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;list = (&lt;font color="#0000ff"&gt;from&lt;/font&gt; c &lt;font color="#0000ff"&gt;in&lt;/font&gt; context.City&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;where&lt;/font&gt; c.Country.Code == CountryCode&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;select&lt;/font&gt; c).ToList();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;return&lt;/font&gt; list;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;/font&gt;&lt;font size='1' color='gray'&gt;&lt;br /&gt;&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;4. Контроллер&lt;/strong&gt;&lt;br /&gt;Контроллер у нас уже есть - HomeController (поскольку наши комбобоксы располагаются на главной странице). Сюда мы добавим один-единственный метод:&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt; &lt;font color="#0000ff"&gt;void&lt;/font&gt; UpdateCities(&lt;font color="#0000ff"&gt;string&lt;/font&gt; code)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Cities cities = &lt;font color="#0000ff"&gt;new&lt;/font&gt; Cities(code);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RenderView(&lt;font color="#A31515"&gt;"SelectCity"&lt;/font&gt;, cities);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/font&gt;&lt;font size='1' color='gray'&gt;&lt;br /&gt;&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Здесь мы всего лишь получаем наши города и передаем экземпляр объекта представлению SelectCity.&lt;br /&gt;Не забываем о роутинге - в файле Global.asax добавим строку:&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;routes.Add(&lt;font color="#0000ff"&gt;new&lt;/font&gt; Route(&lt;font color="#A31515"&gt;"Home/UpdateCities/{code}"&lt;/font&gt;, &lt;font color="#0000ff"&gt;new&lt;/font&gt; RouteValueDictionary(&lt;font color="#0000ff"&gt;new&lt;/font&gt; { controller = &lt;font color="#A31515"&gt;"Home"&lt;/font&gt;, action = &lt;font color="#A31515"&gt;"UpdateCities"&lt;/font&gt; }), &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;new&lt;/font&gt; MvcRouteHandler()));&lt;/font&gt;&lt;font size='1' color='gray'&gt;&lt;br /&gt;&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;Обратите внимание на последний элемент - {code} - это код города, который передается контроллеру. Контроллер принимает его как string code.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;5. Представления - отображение данных.&lt;/strong&gt;&lt;br /&gt;Контролы помещаются на странице (т.е. представлении) Index.aspx. Поскольку страница с момента загрузки должна содержать некоторые статические данные, это должна быть не просто ViewPage, а в нашем случае ViewPage&amp;lt;StaticData&amp;gt;. Сначала отобразим комбобокс со списком доступных стран:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://bp2.blogger.com/_STb7hsxmscM/SDXGRWty0ZI/AAAAAAAAAD8/o2LcYm6VbtM/s1600-h/Index.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_STb7hsxmscM/SDXGRWty0ZI/AAAAAAAAAD8/o2LcYm6VbtM/s400/Index.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5203282945954206098" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Как видим, мы получаем данные напрямую из ViewData, как будто ViewData - это объект типа StaticData (так оно и есть). Очень удобно. Кого-то может смутить тот факт, что разметка перемешивается с кодом. Как же так? Ведь обещали полное отделение кода! Мммм... ну.... в-общем, ничего страшного. Кода все-таки очень мало, и мне он кажется логическим дополнением к разметке.&lt;br /&gt;Обращаю внимание на объявление обработчика:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;onchange=&lt;font color="#A31515"&gt;"UpdateCitiesList();"&lt;/font&gt;&lt;/font&gt;&lt;font size='1' color='gray'&gt;&lt;br /&gt;&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Это функция JavaScript, которая будет подтягивать список городов при изменении значения комбобокса со странами (эту функцию рассмотрим чуть позже).&lt;br /&gt;А пока займемся представлением, обновляющим второй комбобокс. Да-да, комбобокс будет обновлен целым представлением! Добавляем новое представление SelectCity, которое, конечно, объявляем так:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;public&lt;/font&gt; partial &lt;font color="#0000ff"&gt;class&lt;/font&gt; SelectCity : ViewPage&amp;#60;Cities&amp;#62;&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;/font&gt;&lt;font size='1' color='gray'&gt;&lt;br /&gt;&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;А вот код, генерирующий содержимое комбобокса:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://bp1.blogger.com/_STb7hsxmscM/SDXG7Gty0aI/AAAAAAAAAEE/WZX4mAtNXDw/s1600-h/SelCity.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_STb7hsxmscM/SDXG7Gty0aI/AAAAAAAAAEE/WZX4mAtNXDw/s400/SelCity.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5203283663213744546" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;6. Ajax&lt;br /&gt;Ну а теперь посмотрим, какая функция подтягивает список городов (вернемся к Index.aspx):&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;&lt;font color="#606060"&gt;&amp;nbsp; 1:&amp;nbsp;&lt;/font&gt;&amp;#60;script type=&lt;font color="#A31515"&gt;"text/javascript"&lt;/font&gt; language=&lt;font color="#A31515"&gt;"javascript"&lt;/font&gt;&amp;#62;&lt;br /&gt;&lt;font color="#606060"&gt;&amp;nbsp; 2:&amp;nbsp;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;function&lt;/font&gt; UpdateCitiesList()&lt;br /&gt;&lt;font color="#606060"&gt;&amp;nbsp; 3:&amp;nbsp;&lt;/font&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&lt;font color="#606060"&gt;&amp;nbsp; 4:&amp;nbsp;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;if&lt;/font&gt; ($(&lt;font color="#A31515"&gt;'selCountry'&lt;/font&gt;).value == &lt;font color="#A31515"&gt;"0"&lt;/font&gt;)&lt;br /&gt;&lt;font color="#606060"&gt;&amp;nbsp; 5:&amp;nbsp;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;return&lt;/font&gt;;&lt;br /&gt;&lt;font color="#606060"&gt;&amp;nbsp; 6:&amp;nbsp;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;var&lt;/font&gt; url = &lt;font color="#A31515"&gt;'/Home/UpdateCities/'&lt;/font&gt; + $(&lt;font color="#A31515"&gt;'selCountry'&lt;/font&gt;).value;&lt;br /&gt;&lt;font color="#606060"&gt;&amp;nbsp; 7:&amp;nbsp;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font color="#0000ff"&gt;new&lt;/font&gt; Ajax.Updater(&lt;font color="#A31515"&gt;'selCities'&lt;/font&gt;, url,&lt;br /&gt;&lt;font color="#606060"&gt;&amp;nbsp; 8:&amp;nbsp;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&lt;font color="#606060"&gt;&amp;nbsp; 9:&amp;nbsp;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;method: &lt;font color="#A31515"&gt;'get'&lt;/font&gt;&lt;br /&gt;&lt;font color="#606060"&gt;&amp;nbsp;10:&amp;nbsp;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;font color="#606060"&gt;&amp;nbsp;11:&amp;nbsp;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br /&gt;&lt;font color="#606060"&gt;&amp;nbsp;12:&amp;nbsp;&lt;/font&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;font color="#606060"&gt;&amp;nbsp;13:&amp;nbsp;&lt;/font&gt;&amp;#60;/script&amp;#62;&lt;br /&gt;&lt;/font&gt;&lt;font size='1' color='gray'&gt;&lt;br /&gt;&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;В строке 4 мы проверяем, не выбран ли нулевой элемент выпадающего списка (тот, который содержит текст "Select ..."). В этом случае просто покидаем функцию. Далее формируем url, к которому будем обращаться - это обычный адрес в MVC, нам надо вызвать контроллер Home, метод UpdateCities, и передать код страны. В строке 7 мы вызываем очень удобный метод Ajax.Updater - он обновляет содержимое нужного нам контрола. Сигнатура метода проста: &lt;br /&gt;&lt;blockquote&gt;&lt;font size="2" face="Courier New" color="black"&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt; Ajax.Updater(container, url[, options])&lt;/font&gt;&lt;font size='1' color='gray'&gt;&lt;br /&gt;&lt;/font&gt;&lt;/blockquote&gt;&lt;br /&gt;Container - это контрол, который требуется обновить; url - вызываемый url; options - мы указали только метод передачи запроса (полный список параметров см. на &lt;a href="http://prototypejs.org/api/ajax/options"&gt;сайте Prototype&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Как только мы выбираем страну, срабатывает событие onchange выпадающего списка, и Ajax.Updater обновляет комбобокс городов. Все! По-моему, все выглядит просто и красиво.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-5343005509157953108?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/5343005509157953108/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=5343005509157953108' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/5343005509157953108'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/5343005509157953108'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2008/05/mvc-ajax.html' title='MVC и Ajax'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_STb7hsxmscM/SDHfKh3TqaI/AAAAAAAAADU/YkqgumvWV1I/s72-c/CitiesCountries.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-1874829603286089334</id><published>2008-05-17T08:43:00.000-07:00</published><updated>2008-05-18T13:06:04.786-07:00</updated><title type='text'>MVC. Представления</title><content type='html'>А теперь о том, что видит пользователь, и что видит программист, создающий страницу.&lt;br /&gt;В MVC можно использовать обычные веб-страницы с обычными веб-контролами, но это (лично мне) уже совершенно неинтересно. Технология MVC очаровала меня своей "неподкупной простотой", после которой очень трудно вернуться в прошлое тяжеловесных веб-контролов. &lt;br /&gt;&lt;br /&gt;Вообще обычные веб-страницы MVC, т.е. представления являются наследниками класса ViewPagе, а тот, в свою очередь, наследует обычный Page и, кроме того, реализует интерфейс IViewDataContainer. По умолчанию экземпляр ViewPage не включает тэг &amp;lt;form&amp;gt;, поэтому, если вы захотите добавить серверные контролы, не забудьте добавить и форму. &lt;br /&gt;Следует отметить, что представление должно служить только для отображения данных, оно не должно содержать никакой логики или кода доступа к данным. Просто взять данные и отобразить их. &lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Передача данных&lt;/strong&gt;&lt;br /&gt;Главное, ради чего создаются веб-страницы (и вообще приложение) - это данные, которые должны быть переданы форме (в нашем случае представлению). В MVC существует несколько способов передать странице данные. Рассмотрим их по порядку.&lt;br /&gt;&lt;strong&gt;1. Использование словаря ViewData&lt;/strong&gt;&lt;br /&gt;Класс Controller содержит член ViewData, он объявлен следующим образом:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt; public IDictionary&amp;lt;string, object&amp;gt; ViewData { get; }&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Как видим, это простая дженерик коллекция, доступ к ней получаем из самого контроллера:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;ViewData["CategoryName"] = Category.Name;&lt;br /&gt;ViewData["Categories"] = categories; //categories - это &amp;lt;List&amp;gt;Category&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Этот же словарь доступен из представления, поэтому мы можем обратиться прямо на странице к нему так:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;&amp;lt;h2&amp;gt;&amp;lt;% = ViewData["CategoryName"] %&amp;gt;&amp;lt;/h2&amp;gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Для отображения повторяющихся частей страницы (как в Repeater) можно использовать цикл foreach:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;&amp;lt;select&amp;gt;&lt;br /&gt;&amp;lt;%foreach (var cat in ((&amp;lt;List&amp;gt;)ViewData["Categories"]) ){ %&amp;gt;&lt;br /&gt;   &amp;lt;option value='&amp;lt;%=cat.id %&amp;gt;'&amp;gt; &amp;lt;%= cat.Name %&amp;gt; &amp;lt;/option&amp;gt;&lt;br /&gt;&amp;lt;/select&amp;gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Обращаю ваше внимание на две вещи:&lt;br /&gt;1. Несмотря на то, что cat имеет "неопределенный" тип var, мы можем обращаться к полям его настоящего типа, поскольку к нему мы привели объект ViewData["Categories"].&lt;br /&gt;2. Мы использовали встроенный код как для содержимого контейнера (для option), так и для определения атрибута тэга (value) - во втором случае кавычки обязательны (даже несмотря на то, что в обычных тэгах мы можем их и не ставить).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;2. Использование типизированных объектов&lt;/strong&gt;&lt;br /&gt;Вы можете создавать слой DAL самостоятельно, я пытаюсь использовать MS EntityFramework, в любом случае у нас есть доступ к типизированным объектам и коллекциям, которые мы прекрасно можем передать странице. Делается это с помощью перегруженного метода RenderView, который в качестве второго параметра принимает любой объект:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;protected void RenderView(string viewName, object viewData);&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Вот туда-то мы и можем передать, например, список категорий:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;RenderView("Category", categories); //categories - это &amp;lt;List&amp;gt;Category&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;На страницу этот параметр приходит в виде ViewData, поэтому далее мы работаем с ним также, как было показано выше. &lt;br /&gt;&lt;br /&gt;Но есть еще более эффективный способ использовать типизированные объекты на странице (уже без всякого приведения). Дело в том, что в MS MVC существует еще один класс, наследующий ViewPage, определенный следующим образом:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;public class ViewPage&amp;lt;TViewData&amp;gt; : System.Web.Mvc.ViewPage&lt;br /&gt;    Member of System.Web.Mvc&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;TViewData - это шаблон, вместо него вы подставляете имя класса, содержащего необходимые данные.&lt;br /&gt;Все, что надо сделать - это наследовать представление от ViewPage&amp;lt;TViewData&amp;gt;, например:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;public partial class Index : ViewPage&amp;lt;Category&amp;gt;&lt;br /&gt;    {&lt;br /&gt;    }&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;После чего прямо на странице можно легко обращаться к ViewData, объекту, который в нашем примере имеет тип Category:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt; &amp;lt;%=ViewData.Subcategory %&amp;gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Может показаться, что данный подход не очень удобен для сложных страниц, которые отображают разные наборы данных, однако не составляет труда сделать дополнительные классы, которые будут иметь в своем составе все необходимые коллекции, и обращаться к ним через ViewData.&lt;br /&gt;&lt;br /&gt;Ссылки по теме:&lt;br /&gt;&lt;a href="http://weblogs.asp.net/scottgu/archive/2007/12/06/asp-net-mvc-framework-part-3-passing-viewdata-from-controllers-to-views.aspx"&gt;ScottGu о контроллерах (и представлениях)&lt;/a&gt;&lt;br /&gt;&lt;a href="http://quickstarts.asp.net/3-5-extensions/mvc/MVCViews.aspx"&gt;ASP.NET Model View Controller Applications &gt; Views and Rendering Helpers&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-1874829603286089334?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/1874829603286089334/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=1874829603286089334' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/1874829603286089334'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/1874829603286089334'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2008/05/mvc_17.html' title='MVC. Представления'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-4607783167611054706</id><published>2008-05-15T14:08:00.000-07:00</published><updated>2008-05-17T08:39:23.311-07:00</updated><title type='text'>MVC. Контроллеры.</title><content type='html'>Итак, добрались мы наконец до самого, на мой взгляд, "вкусного" в этом новом фреймворке - до контроллеров. Это, можно сказать, сердце вашего проекта, построенного на основе MVC. Надо сказать, что концепция контроллеров не кажется сложной, но содержит некоторые нюансы, без знания которых сложно создать что-то стоящее.&lt;br /&gt;&lt;strong&gt;1. Как работают контроллеры&lt;/strong&gt;&lt;br /&gt;Контроллеры - это специальные классы, в которых вы определяете внутреннюю логику работы приложения - откуда взять данные, куда их передать, и какую страницу показать.&lt;br /&gt;Например, если у вас должна быть страница, которая показывает список категорий (чего-нибудь), а также отдельно содержимое категории, то логично создать контроллер Category, в котором определить методы ShowAll и Show, принимающий в качестве параметра id категории. &lt;br /&gt;Подобные методы носят название action methods (методы действия). Вызов метода производится пользователем, когда он кликает ссылку или набирает в адресной строке /Category/Show/5 - показать содержимое категории с id=5. Методы вызываются в соответствии с правилами, определенными в файле Global.asax.&lt;br /&gt;&lt;div class="NB"&gt;&lt;br /&gt;&lt;strong&gt;О чем надо помнить.&lt;/strong&gt; Имя самого класса контроллера должно заканчивать словом controller, а при вызове вы указываете непосредственно имя контроллера, без этого постфикса. Т.е. если вызов /Category/Show/5, то класс контроллера должен называться CategoryСontroller.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Если контроллер содержит публичный метод, который не является методом действия, пометьте его атрибутом [NonActionAttribute].&lt;br /&gt;&lt;strong&gt;2. Параметры метода действия&lt;/strong&gt;&lt;br /&gt;Все параметры, переданные контроллеру, находятся в объекте Request. Кроме значений параметров, оттуда можно извлечь куки и строку запроса. Извлекать параметры очень просто:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;int id = Convert.ToInt32(Request["id"]);&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Также доступен объект Response. В отличие от стандартных объетов Response и Request ASP.NET (которые являются запечатанными объектами) эти реализуют соответствующие интерфейсы System.Web.IHttpResponse и System.Web.IHttpRequest. Это обстоятельно позволяет создавать псевдообъекты для тестирования контроллеров.&lt;br /&gt;Есть еще более удобный способ извлекать параметры. Для этого метод контроллера должен просто принимать параметр соответствующего типа. &lt;br /&gt;&lt;div class="NB"&gt;&lt;br /&gt;&lt;strong&gt;Важно!&lt;/strong&gt; Не забывайте простую вещь, что именно эти правила определяют роутинг. Если у вас в правилах по дефолту идентификатор передается как id, то в методы вы должны определить параметр точно с таким именем (id, а не CategoryId, например). Если вам нужно передавать именно CategoryId, добавляйте соответствующее правило.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="NB"&gt;&lt;br /&gt;Второй важный момент - тип параметра. Если метод принимает int, а вы передаете ему "hello", возникнет исключение. &lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Если параметр не совпадает с шаблоном роутинга, или если он вообще не передан, его значение в методе будет null. &lt;br /&gt;Вызывать контроллер можно двумя способами: обычным для MVC (Category/Show/5) и обычным для ASP.NET (Category/Show?id=5).&lt;br /&gt;В конце кода метода действия обычно идет метод RenderView("..."), в качестве параметра указывается имя представления (т.е. страницы, попросту говоря, которая должна быть отображена в браузера при обращении к данному методу).&lt;br /&gt;Кстати, если вызывается метод, которого на самом деле нет, то естественно, возникает ошибка 404. Можно переопределить метод HandleUnknownAction(string actionName) и перенаправить запрос к другому методу действия, используя RedirectToAction (этот метод позволяет вызвать не только другой метод действия в данном контроллере, но и метод любого доступного контроллера).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Ссылки по теме:&lt;br /&gt;&lt;a href="http://weblogs.asp.net/scottgu/archive/2007/12/06/asp-net-mvc-framework-part-3-passing-viewdata-from-controllers-to-views.aspx"&gt;ScottGu о контроллерах&lt;/a&gt;&lt;br /&gt;&lt;a href="http://quickstarts.asp.net/3-5-extensions/mvc/MVCControllerActions.aspx"&gt;ASP.NET Model View Controller Applications &gt; Controllers and Controller Actions &lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-4607783167611054706?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/4607783167611054706/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=4607783167611054706' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/4607783167611054706'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/4607783167611054706'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2008/05/mvc.html' title='MVC. Контроллеры.'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-3614945546156164552</id><published>2008-05-15T13:48:00.000-07:00</published><updated>2008-05-23T13:52:47.572-07:00</updated><title type='text'>VS 2008 Service Pack 1. Решает одни проблемы и добавляет другие</title><content type='html'>Итак, вышли два новеньких сервис-пака, призванные устранить немеренное количество багов и добавить новые. Качать &lt;a href="http://msdn.microsoft.com/en-us/vstudio/cc533448.aspx"&gt;здесь&lt;/a&gt;. Прошу учесть, что эти сервис паки - тоже беты, что позволяет их разработчикам выпускать бесчисленное множество последующих версий.&lt;br /&gt;Итак, какие баги, видимые невооруженным глазом, устраняет VS SP1 ?&lt;br /&gt;1. Устранена белиберда с копированием файлов Entity Framework (я имею в виду, что теперь, если ваша модель edmx размещена в одном проекте, а вызываете вы ее в другом, то теперь не надо копировать файлы ssdl, csdl и msl в этот проект). Но копировать строку подключения надо по-прежнему (а то получите сообщение об ошибке &lt;b&gt;Unable to load the specified metadata resource&lt;/b&gt;).&lt;br /&gt;2. Устранен дурацкий баг с закрытием файла конфига после его сохранения.&lt;br /&gt;3. Диаграммы EF стали еще красивее... (все изменения EF приведены самими разработчиками в &lt;a href="http://blogs.msdn.com/adonet/pages/entity-framework-breaking-changes-visual-studio-2008-net-3-5-sp1-beta.aspx"&gt;соответствующем блоге&lt;/a&gt;).&lt;br /&gt;Больше пока не вспомню.&lt;br /&gt;А вот что появилось:&lt;br /&gt;1. Перестал работать роутинг для страницы Default.aspx. Т.е. если раньше вы спокойно задавали&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;routes.Add(new Route("Default.aspx", new MvcRouteHandler())&lt;br /&gt;            {&lt;br /&gt;                Defaults = new RouteValueDictionary(new { controller = "Home", action = "Index", id = "" }),&lt;br /&gt;            });&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;то действительно, после набора в строке localhost:1111/ вы переходили по адресу Home/Index. Теперь браузер упорно зависает на пустой странице default.aspx и ни с места. Пока я эту проблему решаю так: добавляю в default.aspx.cs обработчик Page_Load, где делаю явную переадресацию&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;Response.Redirect("~/Home");&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Топорно, но, по крайней мере, работает.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-3614945546156164552?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/3614945546156164552/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=3614945546156164552' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/3614945546156164552'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/3614945546156164552'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2008/05/vs-2008-service-pack-1.html' title='VS 2008 Service Pack 1. Решает одни проблемы и добавляет другие'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-3349226301993859511</id><published>2008-04-27T06:00:00.000-07:00</published><updated>2008-05-15T13:48:42.562-07:00</updated><title type='text'>Разбираемся с ADO.NET Entity Framework</title><content type='html'>Итак, в этом проекте мы не будем использовать никаких посторонных систем ORM (про ORM &lt;a href="http://ru.wikipedia.org/wiki/ORM"&gt;здесь&lt;/a&gt;), а попробуем построить слой DAL на основе новой технологии ADO.NET Entity Framework, предложенной Microsoft. Говорят, что это не просто ORM, а нечто большее. Что ж, попробуем понять, что именно. Честно говоря, не совсем понимаю, почему вокруг этой ORM столько шума. Мне, например, очень нравится ORM LLBLGen, я думаю, что эта система ни в чем не уступает майкрософтовской, за исключением, разве что тем, что в ней нельзя использовать LINQ (хотя может быть, и можно).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Entity Data Model&lt;/strong&gt;&lt;br /&gt;В основе ADO.NET EF лежит модель сущностей, т.е. Entity Data Model. Как и предполагалось, основные объекты модели - это сущности и связи. Все объекты и их связи представлены в виде XML-схемы - в файле с расширением .edmx. Таблицы представлены объектами EntitySet, поля - свойствами Property (обычные поля) и NavigationProperty (поля, участвующие в ассоциациях - ключи, foreign keys).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Служебные объекты (Object Services)&lt;/strong&gt;&lt;br /&gt;В ADO.NET EF уществует несколько полезных объектов, с помощью которых можно получать доступ к соединению с БД или, например, к выборке из базы, представленной в виде типизированной коллекции.&lt;br /&gt;&lt;strong&gt;ObjectContext &lt;/strong&gt; позволяет получить доступ к соединению, метаданным и еще кое-каким службам.&lt;br /&gt;&lt;strong&gt;ObjectQuery&lt;/strong&gt; - это, по сути, запрос, который возвращает коллекцию типизированных сущностей.&lt;br /&gt;&lt;strong&gt;ObjectStateManager&lt;/strong&gt; кэширует запросы, а также отслеживает изменения в сущностях (с целью дальнейшей синхронизацией с БД).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Mapping&lt;/strong&gt;&lt;br /&gt;Это то, как полученные сущности связаны с реальными объектами базы данных. &lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Как все это выглядит&lt;/strong&gt;&lt;br /&gt;Добавим в наш солюшн еще один проект, который назовем DataCore типа ClassLibrary. Затем в проект добавим новый айтем типа ADO.NET Entity Data model. Сразу же на экране возникает визард, предлагающий нам выбрать, будет ли наша модель построена на основе базы данных, или она будет пустой. Конечно, выбираем Generate from database. Дальше все просто. Визард автоматически подключается к серверу, вернее, сразу к самой базе, после чего мы выбираем таблицы и другие объекты, которые хотим включить в модель. В нашем проекте мы включим следующие таблицы:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;aspnet_Applications&lt;/li&gt;&lt;br /&gt;&lt;li&gt;aspnet_Membership&lt;/li&gt;&lt;br /&gt;&lt;li&gt;aspnet_Users&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Category&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Comment&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Question&lt;/li&gt;&lt;br /&gt;&lt;li&gt;UserDetails&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Ни представления, ни хранимые процедуры пока включать в модель не будем. После этого жмем Finish и вот перед нами диаграмма сгенерированных сущностей. Вот как она выглядит:&lt;br /&gt;&lt;a href="http://bp0.blogger.com/_STb7hsxmscM/SBjfT7jXr_I/AAAAAAAAAC0/WIX7x_aqHlU/s1600-h/EDMX.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_STb7hsxmscM/SBjfT7jXr_I/AAAAAAAAAC0/WIX7x_aqHlU/s400/EDMX.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5195147703668420594" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Если теперь выбрать файл DataModel.edmx в SolutionExplorer, а затем Open with... XML Editor, то мы увидим, как в действительности выглядит этот файл. Это обычный XML, точнее схема, где описаны все элементы: сущности идут здесь как EntitySet, связи между таблицами (ассоциации) как AssociationSet, а отдельные поля - это Properties. &lt;br /&gt;&lt;br /&gt;Помимо этой схемы, студия генерирует еще три файла DataModel.csdl, DataModel.msl и DataModel.ssdl. Файл CSDL представляет собой концептуальную схему - это XML файл, в котором представлены объекты на уровне сущностей. Файл SSDL - схема объектов (таблиц и пр.), как они представлены в базе данных.Файл MSL - схема сопоставления - содержит данные о том, как связаны между собой концептуальная схема и схема объектов БД.&lt;br /&gt;&lt;br /&gt;Теперь вернемся в Solution Explorer и рассмотрим файл DataModel.Designer.cs. Это самый важный для нас файл, потому что здесь сосредоточены все сгенерированные объекты. Если мы откроем наш первый проект (MVC), то в любом месте сможем, например, написать (не забудьте добавить ссылку на проект):&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;using (DataCoreConnection context = new DataCoreConnection())&lt;br /&gt;            {&lt;br /&gt;                Category cat = new FAQEngineModel.Category();&lt;br /&gt;                cat.Name = "Web";&lt;br /&gt;                context.AddObject("Category",cat);&lt;br /&gt;                context.SaveChanges();&lt;br /&gt;            }&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Итак, что же делает этот код?&lt;br /&gt;В первую очередь он создает объект контекста (DataCoreConnection), который будет уничтожен после выхода за пределы фигурных скобок. Этот объект идет первым в нашем файле DataModel.Designer.cs и является по сути связующим звеном между нашим кодом (объектами) и базой данных. С его помощью выполняются все стандартные операции с данными - выборка, вставка, обновление и удаление.&lt;br /&gt;&lt;br /&gt;Затем мы создаем новый объект категории, присваиваем ей имя Web, добавляем объект в контекст и сохраняем изменения. Это, конечно, всего лишь пример кода, поскольку настоящий код должен обязательно содержать конструкцию try-catch.&lt;br /&gt;&lt;br /&gt;Однако в этом коротком примере кроется одна проблема, которая, как я надеюсь, будет в дальнейшем решена разработчиками студии. Дело в том, что все проходит гладко, когда мы имеем один проект, из которого вызываем наш контекст. Но как только мы выделяем обработку с данными в отдельный проект, а вызываем код из другого проекта, начинаются проблемы. Во-первых, поскольку вызывающий проект (у нас это веб-проект) имеет свой файл конфигурации, в котором содержатся свои строки подключения к БД, он в упор не видит файла конфигурации App.config проекта DataCore, и следовательно, не может подключиться к базе. Как результат, мы видим на экране сообщение &lt;strong&gt;EntityConnection error: The specified named connection is either not found in the configuration&lt;/strong&gt;. Это исправляется легко - достаточно скопировать строку подключения из файла App.config в файл Web.config нашего веб-проекта.&lt;br /&gt;&lt;br /&gt;Но тут вылезает другая проблема. Давайте взглянем на строку подключения, которую мы только что скопировали:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;    &amp;lt;add name="DataCoreConnection" connectionString="metadata=.\FAQEngineModel.csdl|.\FAQEngineModel.ssdl|.\FAQEngineModel.msl;provider=System.Data.SqlClient;provider connection string=&amp;quot;Data Source=.\SQLEXPRESS;Initial Catalog=FAQEngine;Integrated Security=True;MultipleActiveResultSets=True&amp;quot;" providerName="System.Data.EntityClient" /&amp;gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Обратите внимание, на какие файлы метаданных она ссылается. Все эти файлы лежат в каталоге bin/Debug проекта DataCore (.\) - т.е. в каталоге, куда помещается dll. Но поскольку мы вызываем эту dll из другого проекта, то получается, что мы ищем эти файлы там, где их нет, и получаем новую ошибку: &lt;strong&gt;"The specified metadata path is not valid. A valid path must be either an existing directory, an existing file with extension '.csdl', '.ssdl', or '.msl', or a URI that identifies an embedded resource.".  &lt;/strong&gt; Нам остается два выхода - либо  исправить строку подключения ,либо каждый раз копировать нужные файлы в какой-либо каталог веб-проекта. &lt;br /&gt;&lt;br /&gt;Попробуем сначала первый способ. Лучше всего, если бы можно было указать относительный путь, например, с использованием какого-нибудь макроса. Единственное, что мне удается найти - это DataDirectory. Но, поскольку мы вызываем файлы из веб-приложения, этот макрос будет заменен каталогом App_Data, который нам вовсе ни к чему. Поэтому можно просто указать абсолютный путь к файлам на диске. Но потом, после деплоймента, эту строку придется менять. Поэтому этот способ мне не нравится.&lt;br /&gt;&lt;br /&gt;Второй способ - копирование файлов - тоже не слишком хорош. Получается, что каждый раз, как только я изменяю модель, мне надо куда-то еще копировать сгенерированные файлы? Получается, что так... ладно, пусть это делает за меня студия. Открываем свойства проекта DataCore, идем в Post-Build event и пишем следующее:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;copy $(TargetDir)FAQEngineModel.csdl $(SolutionDir)FAQEngine\App_Data\&lt;br /&gt;copy $(TargetDir)FAQEngineModel.msl $(SolutionDir)FAQEngine\App_Data\&lt;br /&gt;copy $(TargetDir)FAQEngineModel.ssdl $(SolutionDir)FAQEngine\App_Data\&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Здесь мы просто копируем нужные файлы в каталог App_Data нашего веб-приложения. Теперь остается чуть подправить строку подключения (в файле Web.config):&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;    &amp;lt;add name="DataCoreConnection" connectionString="metadata=|DataDirectory|\FAQEngineModel.csdl||DataDirectory|\FAQEngineModel.ssdl||DataDirectory|\FAQEngineModel.msl;provider=System.Data.SqlClient;provider connection string=&amp;quot;Data Source=.\SQLEXPRESS;Initial Catalog=FAQEngine;Integrated Security=True;MultipleActiveResultSets=True&amp;quot;" providerName="System.Data.EntityClient" /&amp;gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Все работает!&lt;br /&gt;Решение данной проблемы описано на &lt;a href="http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2114362&amp;SiteID=1"&gt;форуме ADO.NET Entity Framework&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Ссылки по теме:&lt;br /&gt;&lt;a href="http://msdn.microsoft.com/ru-ru/magazine/cc163399.aspx"&gt;Обзор ADO.NET Entity Framework&lt;/a&gt;&lt;br /&gt;&lt;a href="http://stump-workshop.blogspot.com/search/label/Entity%20Framework"&gt;Блог Sergey Rozovik - посты, посвященные EF&lt;/a&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/42x5kfw4(VS.80).aspx"&gt;&lt;br /&gt;Работа с макросами в Pre- и Post-Build events&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-3349226301993859511?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/3349226301993859511/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=3349226301993859511' title='Комментарии: 8'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/3349226301993859511'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/3349226301993859511'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2008/04/5-adonet-entity-framework.html' title='Разбираемся с ADO.NET Entity Framework'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_STb7hsxmscM/SBjfT7jXr_I/AAAAAAAAAC0/WIX7x_aqHlU/s72-c/EDMX.gif' height='72' width='72'/><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-4966946313188382318</id><published>2008-04-12T13:05:00.000-07:00</published><updated>2008-05-15T13:46:02.790-07:00</updated><title type='text'>Microsoft MVC Framework - роутинг</title><content type='html'>&lt;strong&gt;Правила&lt;/strong&gt;&lt;br /&gt;В основе роутинга в MS MVC Framework лежит следующий принцип - "мы не обращаемся к файлу, мы обращаемся к классу контроллера, и делаем это через паттерны". Что здесь имеется в виду? А то, что мы действительно не вызываем никакие файлы, т.е. мы пишем в адресной строке не mysite/products/details.aspx?productid=6, а вместо этого указываем "дружественный" URL - mysite/products/details/6. Второй момент - это, конечно, никакой не URL. Т.е. слэши здесь играют роль разделения не папок (как в настоящем URL), а частей (элементов) паттерна. Суть в том, что запрос на сервере разбирается, и каждая его часть (отделенная слэшем) интерпретируется так, как вы задали в паттерне.&lt;br /&gt;&lt;br /&gt;Паттерн (или маршрут) это последовательность элементов-заполнителей (placeholders), в соответствие с которыми производится разбор строки запроса. Паттерн может содержать не только некоторые элементы-переменные, но и константные значения. Каждый элемент заключается в фигурные скобки { и  }. Элементы отделяются друг от друга слэшем или точкой. Все, что не заключено в фигурные скобки, воспринимается как константа. Типичный пример паттерна:&lt;br /&gt;&lt;br /&gt;{controller}/{action}/{id}&lt;br /&gt;&lt;br /&gt;А вот строка, соответствующая этому паттерну:&lt;br /&gt;&lt;br /&gt;Products/Details/55&lt;br /&gt;&lt;br /&gt;Для того, чтобы приложение знало о том, как обрабатывать запрос, паттерн надо зарегистрировать. Лучше всего его регистрировать в событии Application_Start в файле Global.asax. Если вы хотите, чтобы регистрация паттерна была доступна и в проекте, предназначенном для тестирования, то метод регистрации должен быть статическим и принимать в качестве параметра RouteCollection.&lt;br /&gt;Например, регистрация маршрута может выглядеть так:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;protected void Application_Start(object sender, EventArgs e)&lt;br /&gt;{&lt;br /&gt;    RegisterRoutes(RouteTable.Routes);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public static void RegisterRoutes(RouteCollection routes)&lt;br /&gt;{&lt;br /&gt;    routes.Add(new Route&lt;br /&gt;    (&lt;br /&gt;         "Category/{action}/{categoryName}"&lt;br /&gt;         , new CategoryRouteHandler()&lt;br /&gt;    ));&lt;br /&gt;}&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Какие запросы могут подходить под этот паттерн? Например, localhost/Category/View/Yachts. А вот запрос localhost/Category/View не подходит, поскольку во-первых, в строке не хватает третьего элемента, а во-вторых, потому, что паттерн не предусматривает дефолтного значения для него (об этом чуть ниже).&lt;br /&gt;Следует учесть, что в процессе регистрации все паттерны складываются в коллекцию, из которой извлекаются в порядке очереди. Это значит, что размещать паттерны надо по степени убывания специализации, как кэтчи в блоке try-catch, и дефолтный паттерн должен идти последним.&lt;br /&gt;&lt;br /&gt;Теперь посмотрим, как задаются дефолтные значения для элемента (они подставляются в случае отсутствия элемента в строке запроса):&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;void Application_Start(object sender, EventArgs e) &lt;br /&gt;{&lt;br /&gt;    RegisterRoutes(RouteTable.Routes);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public static void RegisterRoutes(RouteCollection routes)&lt;br /&gt;{&lt;br /&gt;  routes.Add(new Route&lt;br /&gt;  (&lt;br /&gt;     "Category/{action}/{categoryName}"&lt;br /&gt;          new CategoryRouteHandler()&lt;br /&gt;  )&lt;br /&gt;    {&lt;br /&gt;       Defaults = new RouteValueDictionary &lt;br /&gt;           {{"categoryName", "food"}, {"action", "view"}}&lt;br /&gt;     }&lt;br /&gt;  );&lt;br /&gt;}&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Как видим, все достаточно просто - если отсутствует имя категории, подставляем food, если нет action, подставляем view.&lt;br /&gt;&lt;br /&gt;Если мы не знаем заранее точно, сколько элементов может содержать строка запроса, но хотим все же как-то обрабатывать запрос, то можно записать паттерн следующим образом:&lt;br /&gt;"Category/{action}/{*titles}" . Звездочка указывает на то, что начиная с этого элемента число последующих элементов может быть любым. Но при этом все они будут трактоваться как третий элемент. Например, для строки запроса localhost/Category/View/Maxi/55 Category - это Category, action - это View, а titles - это Maxi/55. Что делать с таким параметром, мы уже решаем сами.&lt;br /&gt;&lt;br /&gt;На элементы запроса можно накладывать ограничения (констрейнты), которые представляют собой обыкновенные регулярные выражения. Если задано такое ограничение, то сервер будет обязательно проверять, подходит ли элемент строки под это выражение. Вот пример, взятый с того же Quickstart:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;void Application_Start(object sender, EventArgs e) &lt;br /&gt;{&lt;br /&gt;    RegisterRoutes(RouteTable.Routes);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public static void RegisterRoutes(RouteCollection routes)&lt;br /&gt;{&lt;br /&gt;    routes.Add(new Route&lt;br /&gt;    (&lt;br /&gt;      "{locale}/{year}"&lt;br /&gt;         , new ReportRouteHandler()&lt;br /&gt;    )&lt;br /&gt;       {&lt;br /&gt;          Constraints = new RouteValueDictionary &lt;br /&gt;          {{"locale", "{a-z}{2}-{A-Z}{2}"},{"year", @"\d{4}"}}&lt;br /&gt;       });&lt;br /&gt;}&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Мы видим, что элемент year должен содержать 4 цифры, только в этом случае он будет распознан.&lt;br /&gt;&lt;br /&gt;Итак, главное, что надо уяснить с роутингом - это то, что строка запроса всегда разбивается ровно на три логические части - controller, action и id. Если вам нужно передать контроллеру (вернее, его методу) параметров больше одного, то вы просто удлиняете запрос, как будто добавляете еще одну папку, а уже в методе разбиваете параметр на нужное количество.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;IIS 6 и II7 - замечания по использованию&lt;/strong&gt;&lt;br /&gt;Необходимо учитывать момент, связанный с тем, какую именно версию IIS вы используете. Пока мы тестируем наше приложение с помощью сервера, встроенного в студию, все идет хорошо. Но как только мы запускаем приложение через IIS 6, то все перестает работать - адреса попросту не находятся.&lt;br /&gt;&lt;br /&gt;&lt;div class="NB"&gt;&lt;br /&gt;Кстати, если при попытке отладки через сервер у вас начнут вылезать странные ошибки вроде &lt;strong&gt;Failed to access IIS metabase&lt;/strong&gt; или &lt;strong&gt;Unable to start debugging on the web server. The web server is not configured correctly.&lt;/strong&gt;, просто снесите asp.net 2 и установите его заново - aspnet_regiis.exe -u, потом aspnet_regiis.exe -i .&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Да, речь идет только об IIS6. Потому что IIS7 уже умеет работать с приложениями, основанными на MVC Frm, и понимает запросы без расширений файлов, а шестой еще нет. Поэтому надо ему помочь. Для этого надо в код регистрации паттерна включить версию для IIS6. Но для начала в конфиге пропишем явно, на какой версии IIS запускаем наше приложение. Поскольку этот параметр может быть изменен (например, после деплоймента на рабочий сервер), заносим этот момент в наш список TODO. Вот примерный код:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;&amp;lt;add key="IISVersion" value="6" /&amp;gt;&lt;br /&gt;&lt;/div&gt; &lt;br /&gt;Теперь в код Global.asax добавляем код, с помощью которого сначала производим выбор версии IIS:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;protected void Application_Start(object sender, EventArgs e)&lt;br /&gt;        {&lt;br /&gt;            RegisterRoutes(RouteTable.Routes);&lt;br /&gt;        }&lt;br /&gt;public static void RegisterRoutes(RouteCollection routes)&lt;br /&gt;        {&lt;br /&gt;            int iisVersion = Convert.ToInt32(ConfigurationManager.AppSettings["IISVersion"], System.Globalization.CultureInfo.InvariantCulture);&lt;br /&gt;&lt;br /&gt;            if (iisVersion &gt;= 7)&lt;br /&gt;            {&lt;br /&gt;                RegisterRoutesForIIS6(routes);&lt;br /&gt;            }&lt;br /&gt;            else&lt;br /&gt;            {&lt;br /&gt;                RegisterRoutesForIIS7(routes);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;/div&gt; &lt;br /&gt;&lt;br /&gt;Затем мы в соответствующих методах регистрируем наши паттерны. Код в методах отличается только наличием расширения .mvc для IIS6. Мне не очень нравится подобное дублирование, но ничего лучше мне придумать не удалось (если придумаете, напишите мне, а я расскажу здесь о вашей идее - с сохранением всех копирайтов, разумеется). Итак, получился примерно такой код:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;private static void RegisterRoutesForIIS7(ICollection&lt;RouteBase&gt; routes)&lt;br /&gt;        {&lt;br /&gt;            routes.Add(new Route("{controler}/{action}/{id}", new MvcRouteHandler())&lt;br /&gt;            {&lt;br /&gt;                Defaults = new RouteValueDictionary(new { controller = "Home", action = "Index", id = "" }),&lt;br /&gt;            });&lt;br /&gt;...&lt;br /&gt;        }&lt;br /&gt;private static void RegisterRoutesForIIS6(ICollection&lt;RouteBase&gt; routes)&lt;br /&gt;        {&lt;br /&gt;            routes.Add(new Route("{controler}.mvc/{action}/{id}", new MvcRouteHandler())&lt;br /&gt;            {&lt;br /&gt;                Defaults = new RouteValueDictionary(new { controller = "Home", action = "Index", id = "" }),&lt;br /&gt;            });&lt;br /&gt;...&lt;br /&gt;         }&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Неудобство использования IIS6 проявляется и в том, что нормальные адреса (как предполагалось, без расширений) все равно не получаются. Более того, скажу честно, мне так и не удалось заставить всю эту кухню работать под IIS6. На встроенном в студию сервере - пожалуйста, а под IIS6 - ни в какую. Однако не будем терять надежды. Во-первых, может быть, это всего лишь баг, и его в релизе исправят, а во-вторых, может, к тому времени на серверах отомрут IISы-6...&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Хозяйке на заметку&lt;br /&gt;&lt;/strong&gt;&lt;br /&gt;С контроллерами мы будем разбираться позже, сейчас же буквально два слова о том, каким образом MVC Frm узнает о том, что и кому передавать. В основе всего лежит объект System.Web.Routing.UrlRoutingModule, который реализует ничто иное, как интерфейс IHttpModule. Http-модуль характеризуется тем, что вставивается в конвейер обработки запросов. Данный модуль перехватывает запросы и интерпретирует их по-своему. Этот модуль, как полагается, зарегистрирован в файле конфига:&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;&amp;lt;httpModules&amp;gt;&lt;br /&gt;   &amp;lt;add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/&amp;gt;&lt;br /&gt;   &amp;lt;add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/&amp;gt;&lt;br /&gt;  &amp;lt;httpModules&amp;gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;А Http-хандлером является MvcHandler, он решает, какой контроллер вызывать и, соответственно, какой метод. Надо знать, что если в строке запроса указан контроллер, например, Products, то реально сам контроллер (это вообще класс) должен называться ProductController. Имя метода же должно точно соответствовать элементу action запроса.&lt;br /&gt;&lt;br /&gt;Полезные ссылки:&lt;br /&gt;&lt;a href="http://dotnetslackers.com/articles/aspnet/KiggBuildingADiggCloneWithASPNETMVC1.aspx"&gt;Kigg - первый стартер-кит, построенный на MS MVC Framework (клон Digg'a)&lt;/a&gt;&lt;br /&gt;&lt;a href="http://ryanheaney.info/index.php/category/mvc/"&gt;Еще кое-что о роутинге&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Куда дальше?&lt;/strong&gt;&lt;br /&gt;В следующей части я рассмотрю архитектуру приложения.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-4966946313188382318?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/4966946313188382318/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=4966946313188382318' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/4966946313188382318'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/4966946313188382318'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2008/04/3.html' title='Microsoft MVC Framework - роутинг'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-5777809178414092516</id><published>2008-04-11T12:16:00.000-07:00</published><updated>2008-05-15T13:45:25.029-07:00</updated><title type='text'>Microsoft MVC Framework - первый взгляд</title><content type='html'>&lt;strong&gt;Итак, что же это такое - Microsoft MVC Framework ?&lt;/strong&gt;&lt;br /&gt;Многие (а может быть и все) из нас знают, что совет "разделяй и властвуй" как нельзя лучше подходит к программированию. Веб-программирование проделало долгий путь, постепенно приближаясь к воплощению данного совета в жизнь. Вспомним классический ASP, в котором текст страницы перемежался с кодом, или файлы Perl, в которых, наоборот, код перемежается раздражающими вставками разметки. С появлением ASP.NET и модели "code-behind" кажется, мы приблизились к заветному разделению - код в одном файле, разметка в другом. И все же нет в мире совершенства... лично меня периодически раздражал тот факт, что в коде, обрабатывающем какой-нибудь Postback, надо обращаться к базе (или к слою DAL), и тут же - к элементам интерфейса, и все перемешивается в одну кучу...&lt;br /&gt;&lt;br /&gt;Кажется, MVC призвана решить именно эту проблему. Во-первых, что такое MVC? Это такой принцип разработки (он же паттерн), при котором все разделено: интерфейс (View), управление (Controller) и внутренняя логика и данные (Model). Подробнее описано в &lt;a href="http://ru.wikipedia.org/wiki/Model-view-controller"&gt;википедии&lt;/a&gt;. Там же, кстати, есть ссылки на более подробные статьи на эту тему.&lt;br /&gt;&lt;br /&gt;Microsoft MVC Framework решает данную задачу по-своему, и сейчас мы попробуем разобраться, как именно. В контексте этого фреймворка Model - это некий набор классов, позволяющих манипулировать данными и логикой. Например, это могут быть классы, представляющие некоторые сущности (entities). View - это, естественно, интерфейс - т.е. файл страницы с разметкой. Наконец, Controller - это классы, с помощью которых осуществляется взаимодействие с пользователем, обработка пользовательских данных. Пока не очень понятно, что за всем этим стоит (кроме представления).&lt;br /&gt;&lt;br /&gt;Обработка урлов в MVC идет по-особенному, т.е. сервер обращается не к конкретной странице, как это было в ASP.NET, а к классу контроллера, и вот этот-то контроллер и обрабатывает запрос пользователя, а также пользовательский ввод. Кроме того, он, с одной стороны, обращается к модели (т.е. использует логику и данные), с другой стороны, вызывает уже определенный класс представления, т.е., попросту говоря, файл .aspx. С этими процессами связан один интереснейший момент, а именно routing, позволяющий в адресной строке браузера указывать не имена файлов, а путь к некоторому вложенному каталогу (на самом деле это не каталоги, но об этом позже). Т.е. вместо Products/Product.aspx?id=5 мы указываем Products/Details/5. Очень удобно и красиво, а самое главное, просто! Если кто-то пытался сделать подобное в классическом ASP.NET, он сталкивался с невероятными трудностями, о преодолении которых можно почитать в MSDN (&lt;a href="http://msdn2.microsoft.com/en-us/library/ms972974.aspx"&gt;URL Rewriting in ASP.NET&lt;/a&gt;). Организацию роутинга мы подробно разберем позже, а сейчас продолжим исследование MVC.&lt;br /&gt;&lt;br /&gt;Еще одна очень любопытная деталь - в MVC полностью отказались от постбэков. Это означает две вещи: во-первых, поскольку каждый раз обращение идет к определенному классу контрола, он вызывает конкретную страницу, и следовательно, эта страница загружается всегда заново; а во-вторых, как следствие, здесь не существует такого понятия, как жизненный цикл страницы, вернее, он существует, но нам совершенно не интересен - мы не можем использовать Viewstate и события, связанные с жизненным циклом...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Полезные ссылки:&lt;br /&gt;&lt;a href="http://weblogs.asp.net/scottgu/archive/2007/11/13/asp-net-mvc-framework-part-1.aspx"&gt;дневник ScottGu по теме &lt;/a&gt;&lt;br /&gt;&lt;a href="http://msdn2.microsoft.com/ru-ru/magazine/cc337884.aspx"&gt;Создание веб-приложений без форм&lt;/a&gt;&lt;br /&gt;&lt;a href="http://quickstarts.asp.net/3-5-extensions/mvc/MVCOverview.aspx"&gt;ASP.NET Model View Controller Applications&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-5777809178414092516?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/5777809178414092516/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=5777809178414092516' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/5777809178414092516'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/5777809178414092516'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2008/04/2-mvc.html' title='Microsoft MVC Framework - первый взгляд'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-1840175270397462834</id><published>2008-04-10T07:56:00.000-07:00</published><updated>2008-05-18T05:55:09.230-07:00</updated><title type='text'>MVC, Entity Framework и Membership provider. Подготовка.</title><content type='html'>Чтобы в дальнейшем работать с пользователями - вход, регистрация, выход, администрирование и т.д. - необходимо использовать какой-то интерфейс. Самым удобным мне кажется Membership provider. Но чтобы заставить работать и его, надо проделать некоторую предварительную работу, о которой я и расскажу.&lt;br /&gt;&lt;strong&gt;Всякие установки&lt;/strong&gt;&lt;br /&gt;Начнем с того, что установим VS 2008, который достался совершенно бесплатно на конференции разработчиков. В пакет также входит SQL Server 2005, а SQL Server Management Studio Express можно также скачать бесплатно.&lt;br /&gt;Теперь надо установить необходимые дополнения - MVC Framework (&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=38CC4CF1-773A-47E1-8125-BA3369BF54A3&amp;displaylang=en"&gt;здесь&lt;/a&gt;) и ADO.NET Entity Framework (&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=15db9989-1621-444d-9b18-d1a04a21b519&amp;DisplayLang=en"&gt;здесь&lt;/a&gt;). После установки при создании нового приложения выбираем Web -&gt; ASP.NET MVC Web Application. Все, студия создала для нас заготовку приложения! &lt;br /&gt;Двигаемся дальше.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;База данных&lt;/strong&gt;&lt;br /&gt;Создаем в студии SQL Server новую базу. После этого в Web.config нашего приложения добавляем строку подсоединения к базе, например:&lt;br /&gt;&lt;br /&gt;&amp;lt;add name="Main.ConnectionString" connectionString="Data Source=5D734033\SQLEXPRESS;Initial Catalog=DB;Integrated Security=True;User Instance=True" providerName="System.Data.SqlClient" /&amp;gt;&lt;br /&gt;&lt;br /&gt;Строку эту, естественно, добавляем в раздел connectionStrings.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Membership&lt;/strong&gt;&lt;br /&gt;Теперь добавляем настройки для нашего провайдера. Например, такие:&lt;br /&gt;&lt;br /&gt;&amp;lt;membership defaultProvider="FProvider" userIsOnlineTimeWindow="1"&amp;gt;&lt;br /&gt;      &amp;lt;providers&amp;gt;&lt;br /&gt;        &amp;lt;clear /&amp;gt;&lt;br /&gt;        &amp;lt;add name="FProvider"&lt;br /&gt;             applicationName="FEngine"&lt;br /&gt;             connectionStringName="Main.ConnectionString"&lt;br /&gt;             type="System.Web.Security.SqlMembershipProvider"&lt;br /&gt;             enablePasswordRetrieval="false"&lt;br /&gt;             enablePasswordReset="true"&lt;br /&gt;             minRequiredPasswordLength="6"&lt;br /&gt;             minRequiredNonalphanumericCharacters="0"&lt;br /&gt;             passwordFormat="Hashed"&lt;br /&gt;             requiresQuestionAndAnswer="false" &lt;br /&gt;             requiresUniqueEmail="true" /&amp;gt;&lt;br /&gt;      &amp;lt;/providers&amp;gt;&lt;br /&gt;    &amp;lt;/membership&amp;gt;&lt;br /&gt;&lt;br /&gt;Далее создаем настройки для провайдера ролей. Например, так:&lt;br /&gt;&lt;br /&gt; &amp;lt;roleManager enabled="true"&lt;br /&gt;      defaultProvider="rprovider"&lt;br /&gt;      cookieName="RC"&lt;br /&gt;      cookieTimeout="30"&lt;br /&gt;      cookieSlidingExpiration="true"&lt;br /&gt;      cookieProtection="All"&lt;br /&gt;      cacheRolesInCookie="false"&lt;br /&gt;      &amp;gt;&lt;br /&gt;      &amp;lt;providers&gt;&lt;br /&gt;        &amp;lt;add name="rprovider"&lt;br /&gt;          type="System.Web.Security.SqlRoleProvider"&lt;br /&gt;          applicationName="FEngine"&lt;br /&gt;          connectionStringName="Main.ConnectionString"/&amp;gt;&lt;br /&gt;      &amp;lt;/providers&amp;gt;&lt;br /&gt;    &amp;lt;/roleManager&amp;gt;&lt;br /&gt;Все эти настройки нужны для того, чтобы Membership API знал, откуда брать все данные. Если этого не сделать, то он создаст свою собственную базу данных, но нам ведь не это надо, верно?&lt;br /&gt;&lt;br /&gt;Заодно не забываем установить authentication mode равным Forms.&lt;br /&gt;Теперь выполним важный шаг, о котором я практически всегда забываю. Надо сгенерировать все необходимые таблицы - для этого находим утилиту aspnet_reqsql.exe и запускаем ее. Эта утилита лежит всегда в одном месте - C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727 .&lt;br /&gt;В результате откроется вполне нормальное окошко Windows, где мы сначала нажимаем Next, потом выбираем Configure SQL Server for application services, снова нажимаем Next. Здесь нас просят выбрать сервер базы, тип аутентификации (обычно Windows, особенно на локальном компьютере), имя базы. Сервер у меня определился неправильно, поэтому сначала выскочило сообщение об ошибке. После введение в поля правильного имени в выпадающем списке появились все базы. Выбираем базу, жмем Next, потом опять Next, потом Finish.&lt;br /&gt;&lt;br /&gt;Проверим, все ли получилось. Зайдем на сервер, выберем нашу базу данных и посмотрим ее содержимое. Если все в порядке, то в базе должна появиться куча новых таблиц, названия которых начинаются с aspnet_, например, aspnet_Roles.&lt;br /&gt;Вот теперь можно проверить, работает ли наш мембершип провайдер. Для этого возвращаемся в студию, компилируем проект, затем выбираем Project -&gt;ASP.NET Configuration. В результате студия сгенерирует и запустит специальный проект, с помощью которого можно делать некоторые административные вещи, например, устанавливать роли, создавать пользователей. Не думаю, что кто-нибудь пользуется им для реальной работы, но для проверки и установки начальных параметров вполне сойдет.&lt;br /&gt;Итак, после некоторой паузы наконец открывается окно браузера с этим проектом. Внешне все выглядит нормально, но зайдем на вкладку Security. Так и есть! Сообщение об ошибке: &lt;br /&gt;&lt;br /&gt;There is a problem with your selected data store. This can be caused by an invalid server name or credentials, or by insufficient permission. It can also be caused by the role manager feature not being enabled. Click the button below to be redirected to a page where you can choose a new data store. &lt;br /&gt;&lt;br /&gt;The following message may help in diagnosing the problem: &lt;b&gt;Cannot open database "FEngine" requested by the login. The login failed. Login failed for user 'S-5D7E4033\S'&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Итак, делаю две вещи:&lt;br /&gt;Во-первых, изменяю в свойствах сервера тип аутентификации с Windows на смешанный.&lt;br /&gt;Во-вторых, убираю из строки соединения в файле web.config параметр User Instance=True. Нам это совсем не нужно (насколько я знаю, этот параметр позволяет запускать локальную версию сервера).&lt;br /&gt;&lt;br /&gt;Снова компилируем, запускаем проект конфигурации, и ура! - все работает, это видно даже на первой странице, где появилась информация о количестве существующих пользователей (0). &lt;br /&gt;&lt;br /&gt;Идем на вкладку Provider и щелкаем ссылку Select a different provider for each feature (advanced). В результате оказываемся на странице, которая показывает нам отмеченными оба провайдера. Значит, все в порядке.&lt;br /&gt;&lt;br /&gt;Идем на вкладку Sequrity и создаем необходимые роли. Я пока вижу только две роли: admin и member. Создаем обе роли, затем пару пользователей - один пусть будет админ, другой - member, т.е. зарегистрированный пользователь.&lt;br /&gt;На этом подготовительную работу можно считать сделанной.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-1840175270397462834?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/1840175270397462834/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=1840175270397462834' title='Комментарии: 3'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/1840175270397462834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/1840175270397462834'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2008/04/1.html' title='MVC, Entity Framework и Membership provider. Подготовка.'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3325710389670957908.post-4124401393420020033</id><published>2008-04-10T07:51:00.000-07:00</published><updated>2008-05-04T12:12:53.046-07:00</updated><title type='text'>О чем этот блог</title><content type='html'>Этот блог посвящен программированию, точнее, одному проекту, который я в данное время и разрабатываю. Проект этот делается на основе достаточно новых технологий - таких, как Microsoft MVC Framework и ADO.NET Entity Framework. По мере разработки я буду писать в блог - описывать очередной этап и проблемы, с ним связанные. &lt;br /&gt;Надеюсь, он (т.е. блог) поможет вам не наступать на те же самые грабли.&lt;br /&gt;Поехали...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3325710389670957908-4124401393420020033?l=pure-development.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pure-development.blogspot.com/feeds/4124401393420020033/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3325710389670957908&amp;postID=4124401393420020033' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/4124401393420020033'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3325710389670957908/posts/default/4124401393420020033'/><link rel='alternate' type='text/html' href='http://pure-development.blogspot.com/2008/04/blog-post.html' title='О чем этот блог'/><author><name>Electric Cat</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
