<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-1624027437398699573</atom:id><lastBuildDate>Thu, 01 Mar 2012 03:08:51 +0000</lastBuildDate><category>mvc</category><category>xUnit</category><category>Sockets</category><category>поисковая оптимизация</category><category>SEO</category><category>silverlight</category><category>php</category><category>Асинхронный ввод-вывод</category><category>google sitemap</category><category>business rules</category><category>PHPUnit</category><category>asp.net</category><category>network</category><category>DDD</category><category>опрос</category><category>phalanger</category><category>llblgen</category><category>sitemap.xml</category><category>questions</category><category>nhibernate</category><category>asp.net mvc</category><category>.NET 3.5</category><category>mvcextensions</category><category>.NET</category><title>hazzik's technoblog</title><description /><link>http://blog.hazzik.ru/</link><managingEditor>noreply@blogger.com (hazzik)</managingEditor><generator>Blogger</generator><openSearch:totalResults>13</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/hazzik" /><feedburner:info uri="hazzik" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1624027437398699573.post-3477034535813804388</guid><pubDate>Sun, 09 Oct 2011 19:46:00 +0000</pubDate><atom:updated>2012-03-01T08:54:09.452+06:00</atom:updated><title>Web.Requre - client script dependency framework</title><description>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;
&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;
&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;
&lt;h1&gt;















Что это?&lt;/h1&gt;
&lt;h1&gt;














&lt;span class="Apple-style-span" style="font-size: small; font-weight: normal;"&gt;&lt;i&gt;Web.Require&lt;/i&gt; - небольшая библиотечка, для облегчения подключения скриптов в ваше Web-приложение.&lt;/span&gt;&lt;/h1&gt;
&lt;div&gt;
&lt;span class="Apple-style-span" style="font-size: small; font-weight: normal;"&gt;&lt;/span&gt;&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h1&gt;





Давай примеры!&lt;/h1&gt;
&lt;pre class="brush:html"&gt;&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;@ViewBag.Title&amp;lt;/title&amp;gt;
    @Html.RequireStyleSheet(
        Url.Content("~/Content/reset.css"),
        Url.Content("~/Content/Site.less"),
        Url.Content("~/Content/themes/base/minified/jquery.ui.all.min.css"))

    @Html.RequireScript(
        Url.Content("~/Scripts/jquery-1.6.4.min.js"), 
        Url.Content("~/Scripts/jquery-ui-1.8.16.min.js"))

    @Html.OutputRequiredStyleSheets()
    @Html.OutputRequiredScripts()
&amp;lt;/head&amp;gt;
&lt;/pre&gt;
&lt;br /&gt;
Что тут происходит? Все очень просто: мы подключаем три css и два js файла. Что нам дает такой подход? Почему просто нельзя подключить скрипты в лоб?&lt;br /&gt;
&lt;br /&gt;
Давайте представим, что какой-то контрол, например ваш кастомный диалог или, datepicker требует для своей работы отдельного скрпта? Как быть?&lt;br /&gt;
&lt;ul style="text-align: left;"&gt;
&lt;li&gt;Первый вариант - просто в контроле написать &amp;lt;script src=.../&amp;gt;. А если этот контрол на странице используется несколько раз?&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style="text-align: left;"&gt;
&lt;li&gt;Второй вариант - просто включить этот скрипт в &amp;lt;head&amp;gt;. А если этот скрпит очень большой и прожорливый и используется не везде?&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
Все очень просто: с помощью &lt;i&gt;Web.Require&lt;/i&gt; совместим оба варианта и решим их проблемы: скрипт подгружается только один раз и только тогда, когда это необходимо. Например:&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre class="brush:csharp brush:html"&gt;&amp;lt;!--DateTimePicker.cshtml--&amp;gt;
@Html.RequireScript(
    Url.Content("~/Scripts/jquery-1.6.4.min.js"),
    Url.Content("~/Scripts/jquery-ui-1.8.16.min.js"),
    Url.Content("~/Scripts/jquery.ui.datepicker-ru.js"),
    Url.Content("~/Scripts/DateTimePicker.js"))&lt;/pre&gt;
В итоге DateTimePicker.js добавиться в тэг  и наступит всеобщее счастье;)

&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;


















Как установить?&lt;/h1&gt;
&lt;div&gt;
&lt;span class="Apple-style-span" style="background-color: white; color: #333333; font-family: 'Segoe UI', Tahoma, 'Helvetica Neue', Arial, Helvetica, sans-serif; font-size: 12px; line-height: 12px;"&gt;&lt;/span&gt;&lt;br /&gt;
&lt;div class="commandWrapper" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: -webkit-gradient(linear, 0 0, 0 100%, from(rgb(214, 214, 214)), to(rgb(80, 80, 80))); background-origin: initial; border-bottom-color: rgb(255, 255, 255); border-bottom-left-radius: 8px 8px; border-bottom-right-radius: 8px 8px; border-bottom-style: solid; border-bottom-width: 0px; border-color: initial; border-left-color: rgb(255, 255, 255); border-left-style: solid; border-left-width: 0px; border-right-color: rgb(255, 255, 255); border-right-style: solid; border-right-width: 0px; border-style: initial; border-top-color: rgb(255, 255, 255); border-top-left-radius: 8px 8px; border-top-right-radius: 8px 8px; border-top-style: solid; border-top-width: 0px; font-family: inherit; font-size: 12px; font-style: inherit; margin-bottom: 36px; margin-left: 0px; margin-right: 0px; margin-top: 36px; outline-color: initial; outline-style: initial; outline-width: 0px; padding-bottom: 4px; padding-left: 4px; padding-right: 4px; padding-top: 4px; vertical-align: baseline;"&gt;
&lt;div class="commandPrompt" style="-webkit-box-shadow: rgba(0, 0, 0, 0.59375) 6px 6px 14px inset, rgb(102, 102, 102) 1px 1px 4px; background-attachment: initial; background-clip: initial; background-color: initial; background-image: -webkit-gradient(linear, 0 0, 0 100%, from(rgb(94, 94, 94)), to(rgb(0, 0, 0))); background-origin: initial; border-bottom-color: rgb(196, 196, 196); border-bottom-left-radius: 6px 6px; border-bottom-right-radius: 6px 6px; border-bottom-style: solid; border-bottom-width: 1px; border-color: initial; border-left-color: rgb(196, 196, 196); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(196, 196, 196); border-right-style: solid; border-right-width: 1px; border-style: initial; border-top-color: rgb(196, 196, 196); border-top-left-radius: 6px 6px; border-top-right-radius: 6px 6px; border-top-style: solid; border-top-width: 1px; box-shadow: rgba(0, 0, 0, 0.59375) 6px 6px 14px inset, rgb(102, 102, 102) 1px 1px 4px; font-family: inherit; font-size: 12px; font-style: inherit; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; outline-color: initial; outline-style: initial; outline-width: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-shadow: rgb(0, 0, 0) 1px 1px 1px; vertical-align: baseline;"&gt;
&lt;div class="command" style="border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #e2e2e2; font-family: Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Liberation Mono', 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace; font-size: 24px; font-style: inherit; line-height: 24px; margin-bottom: 24px; margin-left: 8px; margin-right: 8px; margin-top: 24px; outline-color: initial; outline-style: initial; outline-width: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;"&gt;
PM&amp;gt; Install-Package Web.Require&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h1&gt;





P.S.&lt;/h1&gt;
В будущем ожидаются следующие фишки:

&lt;br /&gt;
&lt;br /&gt;
&lt;ul style="text-align: left;"&gt;
&lt;li&gt;Асинхронное подключение js&lt;/li&gt;
&lt;li&gt;Конкатенация&amp;nbsp;и минификация js и css + кэширование&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;



P.P.S.&lt;/h1&gt;
&lt;div&gt;
Пример использования можно посмотреть &lt;a href="https://github.com/hazzik/beerconf-website/"&gt;здесь&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1624027437398699573-3477034535813804388?l=blog.hazzik.ru' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/92Cmyong4oqDrBPa1xpksJohE6c/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/92Cmyong4oqDrBPa1xpksJohE6c/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/92Cmyong4oqDrBPa1xpksJohE6c/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/92Cmyong4oqDrBPa1xpksJohE6c/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/hazzik/~4/eaT-aw5XnVo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/hazzik/~3/eaT-aw5XnVo/webrequre-client-script-dependency.html</link><author>noreply@blogger.com (hazzik)</author><thr:total>9</thr:total><feedburner:origLink>http://blog.hazzik.ru/2011/10/webrequre-client-script-dependency.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1624027437398699573.post-6944576539366448979</guid><pubDate>Wed, 24 Aug 2011 11:52:00 +0000</pubDate><atom:updated>2011-08-24T18:00:56.766+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">mvcextensions</category><category domain="http://www.blogger.com/atom/ns#">asp.net mvc</category><title>MvcExtensions: bootstrapping</title><description>Как я уже &lt;a href="/2011/08/mvcextensions.html"&gt;писал во введении&lt;/a&gt;, весь код, выполняющийся при старте приложения необходимо помещать в, BootstrapperTask. &lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Bootstrapper tasks&lt;/h1&gt;В MvcExtensions для размещения кода, выполняющегося при запуске приложения существует понятие &lt;a href="http://en.wikipedia.org/wiki/Bootstrapping_(computing)"&gt;Bootstrapper&lt;/a&gt;, которому можно назначать задачи.&lt;br /&gt;
&lt;br /&gt;
Назначение задачи происходит следующим образом:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:csharp"&gt;//Global.asax.cs
    public class MvcApplication : WindsorMvcApplication
    {
        public MvcApplication()
        {
            Bootstrapper.BootstrapperTasks
// задача, для регистрации контроллеров в IoC контейнер.
                .Include&lt;registercontrollers&gt;(); 
        }
    }
&lt;/pre&gt;&lt;br /&gt;
Кроме стандартной задачи для регистрации контроллеров существует еще несколько стандартных задач, которые подключают дополнительные возможности. Также есть несколько базовых классов, которые позволяют выполняет такие полезные действия, как регистрация маршрутов, фильтров, модел-байндеров, etc.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Регистрация маршрутов&lt;/h2&gt;&lt;br /&gt;
Для регистрации маршрутов вам необходимо унаследоваться от абстрактного класса &lt;i&gt;RegisterRoutesBase&lt;/i&gt;, и подключить задачу к bootstrapper.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:csharp"&gt;public class RegisterRoutes : RegisterRoutesBase
{
    public RegisterRoutes(RouteCollection routes) : base(routes)
    {
    }

    protected override void Register()
    {
        Routes.IgnoreRoute("favicon.ico");
        Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        Routes.MapRoute("Default", 
            "{controller}/{action}/{id}", 
            new { controller = "Home", action = "Index", id = UrlParameter.Optional });
    }
}
&lt;/pre&gt;&lt;h2&gt;Конфигурация фильтров&lt;/h2&gt;Еще одной интересной возможностью MvcExtensions является возможность добавлять фильтры (IMvcFilter) к действиям контроллеров динамически. Этот подход позволяет писать фильтры не через атрибуты, а как обычные классы, при этом появляется возможность использовать инжекцию зависимостей через конструктор. Для регистрации фильтров необходимо унаследоваться от базового абстрактного класса &lt;i&gt;ConfigureFiltersBase&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим небольшой пример:&lt;br /&gt;
&lt;pre class="brush:csharp"&gt;public class ConfigureFilters : ConfigureFiltersBase
{
    public ConfigureFilters(IFilterRegistry registry) : base(registry) { }

    protected override void Configure()
    {
        Registry.Register&amp;lt;ProductController, PopulateCategories, PopulateSuppliers&gt;(c =&gt; c.Create())
                .Register&amp;lt;ProductController, PopulateCategories, PopulateSuppliers&gt;(c =&gt; c.Edit(0));
    }
}
&lt;/pre&gt;&lt;br /&gt;
Как видно из примера, для контроллера &lt;i&gt;ProductController&lt;/i&gt; для действий &lt;i&gt;Create&lt;/i&gt; и &lt;i&gt;Edit&lt;/i&gt; регистрируются два фильтра &lt;i&gt;PopulateCategories&lt;/i&gt; и &lt;i&gt;PopulateSuppliers&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Регистрация ModelBinder&lt;/h2&gt;В жизни бывает всякое и иногда необходимо написать свой ModelBinder, с помощью &lt;i&gt;ConfigureModelBindersBase&lt;/i&gt; вы можете привязать свой ModelBinder к модели.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:csharp"&gt;public class ConfigureModelBinders : ConfigureModelBindersBase
{
    public ConfigureModelBinders(TypeMappingRegistry&amp;lt;object, IModelBinder&gt; registry) 
        : base(registry) { }

    protected override void Configure()
    {
        Registry.Register&amp;lt;ProductEditModel, ProductEditModelBinder&gt;();
    }
} 
&lt;/pre&gt;&lt;br /&gt;
Здесь вроде все понятно.    &lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Собственные Bootstrapper tasks&lt;/h2&gt;Нет ничего проще, чем создать собственные задачи: необходимо просто унаследоваться от базового класса &lt;i&gt;BootstrapperTask&lt;/i&gt;. Также задачи могут зависеть от других задач, для этого вашу задачу нужно пометить атрибутом [DependsOn]. И главное: н&lt;i&gt;е забудьте зарегистрировать свои задачи в бутстрапер.&lt;/i&gt;  &lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:csharp"&gt;[DependsOn(typeof(RegisterRoutes))]
public class ConfigureFiltersBase : BootstrapperTask
{
    protected override void Configure() 
    {
// Ваш код будет здесь
    }
}
&lt;/pre&gt;&lt;br /&gt;
&lt;h1&gt;Вместо заключения&lt;/h1&gt;На сегодня все. В следующей статье я планирую подробно рассмотреть конфигурацию метаданых.&lt;br /&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1624027437398699573-6944576539366448979?l=blog.hazzik.ru' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/yyEx7k3c5nM9psCObgxj_hNd0fQ/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/yyEx7k3c5nM9psCObgxj_hNd0fQ/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/yyEx7k3c5nM9psCObgxj_hNd0fQ/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/yyEx7k3c5nM9psCObgxj_hNd0fQ/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/hazzik/~4/LS14c5OQklk" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/hazzik/~3/LS14c5OQklk/mvcextensions-bootstrapping.html</link><author>noreply@blogger.com (hazzik)</author><thr:total>7</thr:total><feedburner:origLink>http://blog.hazzik.ru/2011/08/mvcextensions-bootstrapping.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1624027437398699573.post-4034414293101430041</guid><pubDate>Sat, 20 Aug 2011 06:00:00 +0000</pubDate><atom:updated>2011-08-20T12:37:50.864+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">mvcextensions</category><category domain="http://www.blogger.com/atom/ns#">asp.net mvc</category><title>MvcExtensions: введение</title><description>&lt;a href="http://mvcextensions.codeplex.com/"&gt;MvcExtensions&lt;/a&gt; - это библиотека расширений для ASP.NET MVC. Библиотека предоставляет пользователю мощнейший инструмент для конфигурирования метаданных для ASP.NET MVC посредством fluent-интерфейса. Второй важной особенностью библиотеки является возведение использования IoC контейнера в абсолют (Можно инжектировать данные даже в атрибуты).&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;С чего начать?&lt;/h1&gt;&lt;br /&gt;
Для начала вам необходимо определится с IoC контейнером, который вы хотите использовать - в MvcExtensions есть адаптеры для следующих популярных IoC контейнеров: &lt;a href="http://www.castleproject.org/container/"&gt;Castle.Windsor&lt;/a&gt;, &lt;a href="http://unity.codeplex.com/"&gt;Unity&lt;/a&gt;, &lt;a href="http://ninject.org/"&gt;Ninject&lt;/a&gt;, &lt;a href="http://structuremap.net/structuremap/"&gt;StructureMap&lt;/a&gt;. Для этого вам необходимо установить соответствующую версию адаптера через nuget: &lt;a href="http://nuget.org/List/Packages/mvcextensions.windsor"&gt;Castle.Windsor&lt;/a&gt;, &lt;a href="http://nuget.org/List/Packages/mvcextensions.unity"&gt;Unity&lt;/a&gt;, &lt;a href="http://nuget.org/List/Packages/mvcextensions.ninject"&gt;Ninject&lt;/a&gt;, &lt;a href="http://nuget.org/List/Packages/mvcextensions.structuremap"&gt;StructureMap&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Затем вам необходимо унаследовать класс своего приложения от базового приложения для выбранного адаптера, например, MvcExtensions.WindsorMvcApplication:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:csharp"&gt;//Global.asax.cs
    public class MvcApplication : WindsorMvcApplication
    {
    	public MvcApplication()
    	{
 
    	}
    }

&lt;/pre&gt;&lt;br /&gt;
&lt;h1&gt;Инициализируем IoC контейнер&lt;/h1&gt;Для инициализации вашего любимого IoC контейнера вам необходимо просто реализовать соответствующий интерфейс или унаследоваться от базового класса: IWindsorInstaller, Ninject.Modules.NinjectModule, StructureMap.Configuration.DSL.Registry, Microsoft.Practices.Unity.IModule. Например для Windosor:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:csharp"&gt;public class AccountInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(Component.For&amp;lt;IAuthenticationService&gt;().ImplementedBy&amp;lt;FormsAuthenticationService&gt;().LifeStyle.Transient);
        }
    }
&lt;/pre&gt;&lt;br /&gt;
Полученные таким образом классы нужно положить в ваше MVC приложение в любую папку, я рекомендую в &amp;lt;YourMvcApplication&gt;/Infrastructure. &lt;i&gt;Необходимо запомнить, что контроллеры таким образом инициализировать не требуется!&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Регистрация контроллеров&lt;/h1&gt;&lt;br /&gt;
В MvcExtensions весь код, выполняющийся при старте приложения необходимо помещать в, так называемые, BootstrapperTask. Существуют некоторые стандартные таски, такие как например регистрация контроллеров. Для того, чтобы проинструктировать MvcExtensions, что вы хотите зарегистрировать свои контроллеры, необходимо всего-лишь включить задачу &lt;i&gt;RegisterControllers &lt;/i&gt;в последовательность задач, выполняемых при старте:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:csharp"&gt;//Global.asax.cs
    public class MvcApplication : WindsorMvcApplication
    {
        public MvcApplication()
    	{
       		Bootstrapper.BootstrapperTasks
    			.Include&amp;lt;RegisterControllers&gt;();
    	}
    }

&lt;/pre&gt;&lt;br /&gt;
После этого все контроллеры в вашем MVC приложении будут создаваться с помощью IoC контейнера.&lt;br /&gt;
&lt;br /&gt;
&lt;h1&gt;Метаданные&lt;/h1&gt;MvcExtensions предлагает пользователям замену стандартному способу конфигурирования метеданных через DataAnnotations, посредством fluent-интерфейса для конфигурирования. Этот подход обладает феноменальной гибкостью и неограниченными возможностями для расширения. &lt;br /&gt;
&lt;br /&gt;
Для того, чтобы использовать эту возможность в MvcExtensions необходимо также зарегистрировать ее:&lt;br /&gt;
&lt;pre class="brush:csharp"&gt;//Global.asax.cs
    public class MvcApplication : WindsorMvcApplication
    {
        public MvcApplication()
    	{
       		Bootstrapper.BootstrapperTasks
    			.Include&amp;lt;RegisterControllers&gt;()
     			.Include&amp;lt;RegisterModelMetadata&gt;();
    	}
    }

&lt;/pre&gt;&lt;br /&gt;
Затем необходимо написать конфигурацию для ваших форм и моделей отображения:&lt;br /&gt;
&lt;pre class="brush:csharp"&gt;public class SignUpMetadata : ModelMetadataConfiguration&amp;lt;SignUp&gt;
    {
        public SignUpMetadata()
        {
            Configure(x =&gt; x.Login)
                .DisplayName("Имя пользователя")
                .Required("Необходимо указать имя пользователя");

            Configure(x =&gt; x.Email)
                .DisplayName("Адрес электронной почты")
                .Required("Необходимоуказать адрес электронной почты")
                .AsEmail();

            Configure(x =&gt; x.Password)
                .DisplayName("Пароль")
                .Required("Необходимо указать пароль")
                .MinimumLength(6, " Длина пароля должна быть не меньше 6 символов")
                .AsPassword();

            Configure(x =&gt; x.ConfirmPassword)
                .DisplayName("Пароль еще раз")
                .Required("Необходимо указать подтверждение пароля")
                .AsPassword()
                .Compare("Password", "Пароль и подвтерждение пароля должны совпадать");
        }
    }
&lt;/pre&gt;&lt;br /&gt;
&lt;h1&gt;Заключение&lt;/h1&gt;На этом первый этап конфигурирования MvcExtensions завершен. Если что-то осталось непонятно - задавайте вопросы, буду стараться разъяснять.&lt;br /&gt;
&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/1624027437398699573-4034414293101430041?l=blog.hazzik.ru' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/JVZejCwEMwxBbYQUEW_k6eLZezs/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/JVZejCwEMwxBbYQUEW_k6eLZezs/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/JVZejCwEMwxBbYQUEW_k6eLZezs/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/JVZejCwEMwxBbYQUEW_k6eLZezs/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/hazzik/~4/18gIgA4XkYQ" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/hazzik/~3/18gIgA4XkYQ/mvcextensions.html</link><author>noreply@blogger.com (hazzik)</author><thr:total>9</thr:total><feedburner:origLink>http://blog.hazzik.ru/2011/08/mvcextensions.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1624027437398699573.post-6948422760771478543</guid><pubDate>Sun, 23 May 2010 13:29:00 +0000</pubDate><atom:updated>2010-05-23T19:29:30.141+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">DDD</category><category domain="http://www.blogger.com/atom/ns#">business rules</category><category domain="http://www.blogger.com/atom/ns#">questions</category><title>Бизнес-правила в DDD-2</title><description>Продолжу мучить сообщество своими вопросами по DDD дальше. &lt;br /&gt;
&lt;br /&gt;
Допустим, мы хотим добавить в систему идентификацию не только &lt;a href="http://blog.hazzik.ru/2010/05/ddd.html"&gt;по адресу электронной почты&lt;/a&gt;, но и по имени пользователя. В связи с этим у нас в системе возникает другое бизнес-правило:&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;Имя пользователя является уникальным&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
Это бизнес правило фигурирует в следующих историях использования:&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Пользователь регистрируется в системе, если уже есть другой пользователь с таким именем, то пользователю выводиться сообщение об ошибке.&lt;/li&gt;
&lt;li&gt;Пользователь может изменить свое имя, если уже есть другой пользователь с таким именем, то пользователю выводиться сообщение об ошибке.&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
Где должна находиться реализация данного бизнес-правила?&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/1624027437398699573-6948422760771478543?l=blog.hazzik.ru' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/DB3rz3LEmYQox3G9-YA4uOgJowA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/DB3rz3LEmYQox3G9-YA4uOgJowA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/DB3rz3LEmYQox3G9-YA4uOgJowA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/DB3rz3LEmYQox3G9-YA4uOgJowA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/hazzik/~4/D0izzR_gmtc" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/hazzik/~3/D0izzR_gmtc/ddd-2.html</link><author>noreply@blogger.com (hazzik)</author><thr:total>21</thr:total><feedburner:origLink>http://blog.hazzik.ru/2010/05/ddd-2.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1624027437398699573.post-1790873962076866235</guid><pubDate>Fri, 21 May 2010 20:40:00 +0000</pubDate><atom:updated>2010-09-03T22:19:16.219+06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">DDD</category><category domain="http://www.blogger.com/atom/ns#">business rules</category><category domain="http://www.blogger.com/atom/ns#">questions</category><title>Бизнес-правила в DDD</title><description>У нас в системе есть одно очень интересное и достаточно распространенное бизнес-правило:&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;Адрес электронной почты пользователя должен быть уникальным в пределах системы.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="brush:csharp"&gt;public class User : IEntity
{
  public int Id { get; set; }

  public string Email { get; set; }

  public string Name { get; set; }
}
&lt;/pre&gt;&lt;br /&gt;
Где должна находиться реализация данного бизнес-правила?&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/1624027437398699573-1790873962076866235?l=blog.hazzik.ru' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Ha5YE5YzwjtNLzqWQ7UYzh0xxPU/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Ha5YE5YzwjtNLzqWQ7UYzh0xxPU/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Ha5YE5YzwjtNLzqWQ7UYzh0xxPU/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Ha5YE5YzwjtNLzqWQ7UYzh0xxPU/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/hazzik/~4/GycTd4716xU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/hazzik/~3/GycTd4716xU/ddd.html</link><author>noreply@blogger.com (hazzik)</author><thr:total>14</thr:total><feedburner:origLink>http://blog.hazzik.ru/2010/05/ddd.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1624027437398699573.post-4831830297678179718</guid><pubDate>Sun, 24 Jan 2010 17:39:00 +0000</pubDate><atom:updated>2010-01-25T02:32:17.337+05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">silverlight</category><category domain="http://www.blogger.com/atom/ns#">phalanger</category><category domain="http://www.blogger.com/atom/ns#">php</category><title>PHP во вселенной .NET</title><description>Привет, сегодня я хочу рассказать о замечательном проекте &lt;a href="http://php-compiler.net/"&gt;Phalanger&lt;/a&gt;, который позволил прийти языку программирования &lt;a href="http://php.net/"&gt;PHP&lt;/a&gt; во вселенную &lt;a href="http://www.microsoft.com/NET/"&gt;.NET&lt;/a&gt; – и это не шутка! Phalanger – это компилятор PHP в IL-код. &lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;Проект активно развивается, и скоро разработчики обещают выпустить новый релиз (предыдущий был в декабре 2008).&lt;br /&gt;
&lt;br /&gt;
Проект &lt;strong&gt;Phalanger&lt;/strong&gt; это:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Компилятор phpc;&lt;/li&gt;
&lt;li&gt;Среда исполнения программ, написанных на PHP;&lt;/li&gt;
&lt;li&gt;Расширение языка PHP/CLI;&lt;/li&gt;
&lt;li&gt;Компоненты интеграции с Visual Studio.&lt;/li&gt;
&lt;/ul&gt;Компилятор phpc позволяет &lt;strong&gt;компилировать PHP код&lt;/strong&gt; в режиме совместимости (Legacy compilation mode), либо в режиме «чистый .NET» (pure .NET compilation mode). Режим совместимости позволяет любому вашему коду для родного интерпретатора компилироваться и работать без каких-либо проблем. Режим «чистый .NET» привносит более тесную &lt;strong&gt;интеграцию с .NET framework&lt;/strong&gt; – можно использовать код, написанный на PHP/CLI, в других приложениях .NET. &lt;br /&gt;
PHP/CLI дополняет PHP такими родными для мира .NET фичами, как генерики, атрибуты и партиал классы (pure mode). &lt;br /&gt;
Новый релиз сулит нам следующие вкусности:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Поддержка спецификации PHP версии 5.3.1;&lt;/li&gt;
&lt;li&gt;«Утиная» типизация для, еще более тесной, интеграцией с .NET;&lt;/li&gt;
&lt;li&gt;Улучшенная интеграция с VS, с поддержкой IntelliSense;&lt;/li&gt;
&lt;li&gt;Поддержку Silverlight 2.0;&lt;/li&gt;
&lt;li&gt;И, конечно же, исправление багов, найденных с предыдущего релиза.&lt;/li&gt;
&lt;/ul&gt;Также Phalanger уже поддерживает&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Разработку WinForms приложений;&lt;/li&gt;
&lt;li&gt;Разработку и отладку Silverlight 1.1 приложений;&lt;/li&gt;
&lt;li&gt;ASP.NET 2.&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
Phalanger совместим со множеством бесплатных CMS, движков блогов и  прочих PHP приложений. Данный проект, возможно, будет полезен тем, кто хочет постепенно изучить платформу .NET Framework и/или перейти на неё полностью c PHP.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1624027437398699573-4831830297678179718?l=blog.hazzik.ru' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/aLzWzP_RF-rJ723SBhYQDp3JLqM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/aLzWzP_RF-rJ723SBhYQDp3JLqM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/aLzWzP_RF-rJ723SBhYQDp3JLqM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/aLzWzP_RF-rJ723SBhYQDp3JLqM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/hazzik/~4/0xevhqeahfc" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/hazzik/~3/0xevhqeahfc/php-net.html</link><author>noreply@blogger.com (hazzik)</author><thr:total>5</thr:total><feedburner:origLink>http://blog.hazzik.ru/2010/01/php-net.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1624027437398699573.post-4883705696622664932</guid><pubDate>Thu, 21 Jan 2010 22:21:00 +0000</pubDate><atom:updated>2010-01-22T12:55:05.713+05:00</atom:updated><title>Subversion vs. Git</title><description>&lt;div&gt;Привет, сегодня я хочу поговорить о двух, по моему мнению, самых успешных системах контроля версий &lt;a href="http://tortoisesvn.tigris.org/"&gt;Subversion&lt;/a&gt; и &lt;a href="http://git-scm.com/"&gt;Git&lt;/a&gt;. В своей практике я сталкивался с множеством VCS, включая такие экзотические системы как &lt;a href="http://jedivcs.sourceforge.net/"&gt;Jedi VCS&lt;/a&gt;.  Мой личный путь миграции  можно кратко описать так: &lt;a href="http://www.nongnu.org/cvs/"&gt;CVS &lt;/a&gt;-&gt; SVN -&gt; Git.&lt;/div&gt; &lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;h3&gt;Коммиты&lt;/h3&gt;&lt;br /&gt;&lt;div&gt;Git благодаря наличию локального хранилища позволяет делать «отложенные» коммиты. «Отложенные» коммиты попадают в основное хранилище, только когда вы проталкиваете (push) их туда. Можно делать такие мелкие коммиты, какие вам захочется, не заботясь о том, красная полоска или зеленая – главное, чтобы при проталкивании изменений в основное хранилище полоска была зеленой (вы ведь &lt;a href="http://testdriven.net/"&gt;пишите тесты&lt;/a&gt;?;)) Таким образом, коммит становиться для разработчика просто &lt;strong&gt;точкой возврата&lt;/strong&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;В SVN же, коммит должен быть некой &lt;strong&gt;завершенной&lt;/strong&gt; логической единицей, т.к. сразу попадает в основное хранилище, и становиться доступен всей команде разработчиков.&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;Ветки&lt;/h3&gt;&lt;br /&gt;&lt;div&gt;Git обладает очень мощным механизмом ветвления, благодаря чему многие команды разработчиков каждую новую фичу разрабатывают в отдельной ветке, которая сливается с основной только после ее реализации. В основной ветке находиться версия в любой момент готовая к внедрению у заказчика. &lt;/div&gt;&lt;div&gt; В SVN ветки используются в основном для сохранения уже выпущенных версий продукта, а вся разработка ведется в основной ветке, со всеми вытекающими отсюда последствиями.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3&gt;Метаданные&lt;/h3&gt;&lt;div&gt;SVN хранит метаданные по каждой папке отдельно в подпапках .svn. Этих метаданных чрезвычайно много, и при интенсивной работе над проектом (у нас каждый делает по 10-15 коммитов в день) файловая система сильно засоряется и часто приходиться производить дефрагментацию. &lt;/div&gt;&lt;br /&gt;&lt;div&gt;Git хранит метаданные более компактно, в одном месте - в подпапке .git корневой директории проекта. Но за всё нужно платить – нельзя скачать часть репозитория как в SVN, только весь. &lt;/div&gt;&lt;h3&gt;Вместо заключения&lt;/h3&gt;&lt;div&gt; Git благодаря своей огромной гибкости подходит для больших и/или распределенных команд, но более сложен в освоении.  &lt;/div&gt;&lt;div&gt;SVN подходит для небольших команд из 2-3 человек, физически находящихся в одном помещении (хотя &lt;a href="http://www.mono-project.com/"&gt;есть исключения&lt;/a&gt;).  &lt;/div&gt;&lt;div&gt;Для операционных систем Windows для Subversion и Git есть очень удобные графические оболочки, встраивающиеся в проводник – &lt;a href="http://tortoisesvn.tigris.org/"&gt;TortoiseSVN&lt;/a&gt; и &lt;a href="http://code.google.com/p/tortoisegit/"&gt;TortoiseGit&lt;/a&gt; соответственно.&lt;/div&gt;&lt;div&gt;Для тех кто хочет мигрировать с SVN на Git в состав Git включена утилита &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-svn.html"&gt;git-svn&lt;/a&gt;, позволяющая делать двустороннюю синхронизацию между хранилищами. &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1624027437398699573-4883705696622664932?l=blog.hazzik.ru' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/IktnFJCVZwRbxBr20K14EQb2krI/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/IktnFJCVZwRbxBr20K14EQb2krI/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/IktnFJCVZwRbxBr20K14EQb2krI/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/IktnFJCVZwRbxBr20K14EQb2krI/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/hazzik/~4/81yDsoDE5zc" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/hazzik/~3/81yDsoDE5zc/subversion-vs-git.html</link><author>noreply@blogger.com (hazzik)</author><thr:total>12</thr:total><feedburner:origLink>http://blog.hazzik.ru/2010/01/subversion-vs-git.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1624027437398699573.post-6514169990731740574</guid><pubDate>Sun, 03 Jan 2010 20:09:00 +0000</pubDate><atom:updated>2010-01-09T00:27:33.957+05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">xUnit</category><category domain="http://www.blogger.com/atom/ns#">PHPUnit</category><category domain="http://www.blogger.com/atom/ns#">php</category><title>PHP: тестируем нетестируемое – echo</title><description>В данной заметке я хочу рассказать, как тестировать конструкции echo и ей подобные в &lt;a href="http://php.net/"&gt;PHP&lt;/a&gt;.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Допустим у нас есть такой код:&lt;br /&gt;&lt;pre class="brush:php"&gt;&lt;br /&gt;class World {&lt;br /&gt; public function printHello() {&lt;br /&gt;   echo 'hello world';&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public function printHello2() {&lt;br /&gt;?&amp;gt;&lt;br /&gt;   hello world&lt;br /&gt;&amp;lt;?php&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;//...&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;«Как же нам протестировать методы Hello и Hello2?» – вчера ночью этот вопрос загнал меня в тупик. Немного поразмыслив, я все-таки выкрутился из ситуации – для первого случая достаточно отрефакторить код таким образом, чтобы на вход конструкции &lt;i&gt;echo&lt;/i&gt; подавался результат функции, вычисляющий требуемое значение:&lt;br /&gt;&lt;pre class="brush:php"&gt;&lt;br /&gt;class World {&lt;br /&gt; /** @returns string */&lt;br /&gt; public function getGreeting(){&lt;br /&gt;   return 'hello world';&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public function printHello(){&lt;br /&gt;   echo $this-&gt;getGreeting();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;//...&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Такой код уже поддается тестированию:&lt;br /&gt;&lt;pre class="brush:php"&gt;&lt;br /&gt;/** @test */&lt;br /&gt;public function getGreetingReturnsHelloWorld () {&lt;br /&gt; $world = new World;&lt;br /&gt;&lt;br /&gt; self::assertEquals('hello world', $world-&gt;getGreeting());&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Но как же быть во втором случае? Во втором случае нам поможет &lt;a href="http://xunitpatterns.com/indirect%20output.html"&gt;тестирование непрямого вывода &lt;/a&gt;. Перед вызовом тестовой системы добавим вызов функции &lt;i&gt;ob_start()&lt;/i&gt; которая включает буферизированный вывод, а после вызова системы проверим содержимое буфера с помощью вызова функции &lt;i&gt;ob_get_flush()&lt;/i&gt;:&lt;br /&gt;&lt;pre class="brush:php"&gt;&lt;br /&gt;/** @test */&lt;br /&gt;public function hello2InderectTesting () {&lt;br /&gt; $world = new World;&lt;br /&gt;&lt;br /&gt; ob_start();&lt;br /&gt; $world-&gt; printHello2 ();&lt;br /&gt; $actual = ob_get_flush();&lt;br /&gt;&lt;br /&gt; self::assertEquals('hello world', $actual);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1624027437398699573-6514169990731740574?l=blog.hazzik.ru' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/AXoEqzwdI4Te8rkK09UmanUoS40/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/AXoEqzwdI4Te8rkK09UmanUoS40/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/AXoEqzwdI4Te8rkK09UmanUoS40/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/AXoEqzwdI4Te8rkK09UmanUoS40/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/hazzik/~4/su8Dbl5e3pc" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/hazzik/~3/su8Dbl5e3pc/php-echo.html</link><author>noreply@blogger.com (hazzik)</author><thr:total>5</thr:total><feedburner:origLink>http://blog.hazzik.ru/2010/01/php-echo.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1624027437398699573.post-4089624053628404827</guid><pubDate>Thu, 31 Dec 2009 16:34:00 +0000</pubDate><atom:updated>2010-01-09T00:31:23.402+05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">nhibernate</category><category domain="http://www.blogger.com/atom/ns#">llblgen</category><title>Причины перехода с LLBLGen Pro на NHibernate</title><description>&lt;div&gt;&lt;a href="http://www.byndyu.ru/"&gt;Мой коллега&lt;/a&gt; уже описал &lt;a href="http://blog.byndyu.ru/2009/12/llblgen-vs-nhibernate.html"&gt;пару причин&lt;/a&gt;, почему мы постепенно отказываемся от &lt;a href="http://www.llblgen.com/"&gt;LLBLGen Pro&lt;/a&gt; в пользу &lt;a href="http://nhforge.org/"&gt;NHibernate&lt;/a&gt;. Хочу добавить свои 50 центов.&lt;/div&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;Мы разрабатываем все проекты по методологии &lt;a href="http://xprogramming.com/"&gt;XP&lt;/a&gt;, активно используем принципы &lt;a href="http://en.wikipedia.org/wiki/KISS_principle"&gt;KISS &lt;/a&gt;и &lt;a href="http://en.wikipedia.org/wiki/YAGNI"&gt;YAGNI&lt;/a&gt;, поэтому каждый проект, его &lt;a href="http://domaindrivendesign.org/"&gt;доменная модель&lt;/a&gt; и, соответственно, база данных постоянно развиваются. С LLBLGen Pro нам приходилось генерировать модель несколько раз  в  день и, если одновременно нескольким разработчикам необходимо было что-то изменить – выстраивалась живая очередь.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;С генерируемым кодом очень сложно использовать &lt;a href="http://www.testdriven.com/"&gt;TDD &lt;/a&gt;– практически невозможно писать тест до кода. Чтобы написать тест необходимо проделать следующие шаги:  добавить таблицу/поле/ограничение в базу; сгенерировать модель и только затем написать тест – это уже не TDD.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;NHibernate, напротив, позволяет применять TDD в полной мере: благодаря тому, что информацию о способе отображения хранит не сама сущность, а некий внешний объект мы можем отдельно разрабатывать доменную модель и отдельно способ ее отображения. При этом сама собой возникает большая гибкость отображения.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Сгенерированный код очень трудно &lt;a href="http://www.refactoring.com/"&gt;рефакторить&lt;/a&gt;: нельзя просто переименовать сущность/поле/связь – необходимо сначала произвести переименование в проекте, потом в генераторе и затем заново сгенерировать модель.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;NHibernate, же, позволяет переименовывать сущности как вам угодно прямо в Visual Studio (при условии, что для конфигурирования отображения используется &lt;a href="http://fluentnhibernate.org/"&gt;fluent-nh&lt;/a&gt;) – необходимо будет только удостовериться, что рефакторинг произошел безболезненно – запустить тесты (а у вас ведь они есть ;-)).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;В LLBLGen Pro все таблицы базы данных отражаются в сущности доменной модели, практически один к одному. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Нет поддержки Value Object – т.е. невозможно из одной сущности выделить небольшие однообразные объекты, невозможно использовать связанную таблицу как Value Object. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Для отображения связи сущностей m:n используются также вспомогательные сущности, при этом сами коллекции m:n всегда прибывают в состоянии «только чтение»: чтобы добавить или удалить связанную сущность необходимо работать с коллекциями вспомогательных сущностей. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Нет поддержки каскадного удаления связанных сущностей – все необходимо делать руками.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;NHibernate лишен всех этих недостатков.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;h3&gt;Вместо заключения&lt;/h3&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Ваш выбор LLBLGen если: &lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;вам нужен очень быстрый старт и проект в дальнейшем не планирует своего развития либо поддержки;&lt;/li&gt;&lt;li&gt;вы разрабатываете проект с использованием тяжеловесных методик типа RUP и всяких там MSF, где вся система заранее детально проработана. &lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;Ваш выбор NHibernate если:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;ваша система находится в непрерывном развитии и вам необходима большая гибкость на уровне ORM&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;PS: всех с Новым годом ;-)&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1624027437398699573-4089624053628404827?l=blog.hazzik.ru' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/uqcF0LXIJkK8cQJMSRD2JO10o7Q/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/uqcF0LXIJkK8cQJMSRD2JO10o7Q/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/uqcF0LXIJkK8cQJMSRD2JO10o7Q/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/uqcF0LXIJkK8cQJMSRD2JO10o7Q/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/hazzik/~4/4C7DNUWOXew" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/hazzik/~3/4C7DNUWOXew/llblgen-pro-nhibernate.html</link><author>noreply@blogger.com (hazzik)</author><thr:total>0</thr:total><feedburner:origLink>http://blog.hazzik.ru/2009/12/llblgen-pro-nhibernate.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1624027437398699573.post-3455392215667477468</guid><pubDate>Thu, 12 Mar 2009 14:09:00 +0000</pubDate><atom:updated>2010-01-09T00:32:20.256+05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">Sockets</category><category domain="http://www.blogger.com/atom/ns#">network</category><category domain="http://www.blogger.com/atom/ns#">Асинхронный ввод-вывод</category><title>Асинхронный ввод-вывод. Проблемы</title><description>&lt;span&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;Проблема&lt;/span&gt;: допустим вы пишете простой -сервер, который получает какое-то сообщение от клиента, обрабатывает его и что-то ему отправляет. Протокол вы разрабатываете сами, на основе TCP/IP. Встает вопрос что выбрать: синхронные или асинхронные сокеты. Рассмотрим каждый вариант подробнее.&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Типичная диаграмма последовательности для решаемой задачи&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_VEYwyqMSqYM/SbkYU39RC8I/AAAAAAAAAEA/mOfKuhZLlAY/s1600-h/seq.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 161px; height: 320px;" src="http://4.bp.blogspot.com/_VEYwyqMSqYM/SbkYU39RC8I/AAAAAAAAAEA/mOfKuhZLlAY/s320/seq.png" alt="" id="BLOGGER_PHOTO_ID_5312303982357580738" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;h4&gt;Синхронные сокеты.&lt;/h4&gt;При работе с синхронными сокетами, при вызове какой-либо операции текущий поток выполнения блокируется, до завершения операции. Ну ничего, решаете вы и выделяете по потоку на каждое соединение (или по два, если нужна независимая отправка). Пока ваше приложение  маленькое, и пользуются им относительно небольшое количество одновременных пользователей, вы счастливы. Но как только количество пользователей переходит какой-то критический барьер - приложение начинает жутко тормозить. Давайте разберемся почему?&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;На каждый поток операционной системой выделяется определенный минимальный объем оперативной памяти (для стека и пр. - около 1 мб), при 500 пользователях это будет 500 МБ, без учета памяти потребляемой клиентом на выполнение своих операций.&lt;/li&gt;&lt;li&gt;Переключение между потоками выполнения занимает какое-то время.&lt;/li&gt;&lt;li&gt;Большинство потоков "висит" в ожидании завершения синхронной операции, при этом время на переключение между потоками продолжает тратиться.&lt;/li&gt;&lt;li&gt;Трудность отладки многопоточных приложений.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;Для решения этих и других проблем синхронного ввода-вывода и придумали асинхронные вызовы.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Асинхронные сокеты&lt;/h4&gt;Для решения проблем выше были придуманы асинхронные сокеты и операции. При вызове асинхронной операции - поток выполнения не блокируется, а продолжает свое выполнение. В большинстве случаев асинхронные вызовы работают через так называемые порты завершения (&lt;span style="font-style: italic;"&gt;IOCP&lt;/span&gt;). При вызове асинхронной операции сокет ассоциируется с каким-либо портом завершения, сокет будет оповещать этот поток о намерениях завершить операцию. При сигнале от сокета, порт берет из пула потоков (&lt;span style="font-style: italic;"&gt;thread pool&lt;/span&gt;), свободный поток и завершает операцию (в зависимости от реализации может потребоваться вызвать метод завершения операции).&lt;br /&gt;Что мы от этого выигрываем? Нет потоков ожидающих завершения операции, а следовательно потраченого впустую процессорного времени на переключение между ними; минимальное количество потоков; другие плюшки:)&lt;br /&gt;&lt;h5&gt;Проблемы&lt;/h5&gt;При любом чтении или записи из сокета возвращается МЕНЬШЕЕ или равное ожидаемому количество байт. И если при работе с синхронными сокетами мы могли быть уверены, что получим сообщение полностью, написав цикл, который будет вычитывать строго необходимое количество данных в буфер (также для отправки, включая асинхронную). При асинхронном получении данных такой уверенности нет (при условии, что длина не фиксирована) - приложение просто выплюнет нам какой-то обрывок сообщения, или два, или полтора - как получиться. Таким образом необходимо будет потратить какое-то время на разбор приходящих сообщений.&lt;br /&gt;Вторая проблема - т.к. асинхронные сокеты работают через нативный &lt;span style="font-style: italic;"&gt;IOCP&lt;/span&gt;, то буферы для приема и отправки будут в состоянии &lt;span style="font-style: italic;"&gt;pined&lt;/span&gt;. А множество пришпиленых объектов - зло, т.к. будет способствовать фрагментации кучи (подробнее &lt;a href="https://blogs.msdn.com/yunjin/archive/2004/01/27/63642.aspx"&gt;тут&lt;/a&gt;), поэтому обычно принимается решение выделить один большой буфер, и использовать его постоянно. В .NET 2.0 появилась возможность вместе с асинхронными сокетами использовать &lt;a href="http://msdn.microsoft.com/ru-ru/library/1hsbd92d.aspx"&gt;ArraySegment{T}&lt;/a&gt;, который может "разбить" наш большой буфер на несколько небольших сегментов, но не все так просто, как кажеться - всем этим хозяйством необходимо управлять. Итого нам необходим менеджер сегментов (можно почитать &lt;a href="http://codebetter.com/blogs/gregyoung/archive/2007/06/18/async-sockets-and-buffer-management.aspx"&gt;тут&lt;/a&gt;), и парсер сообщений.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1624027437398699573-3455392215667477468?l=blog.hazzik.ru' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/xWm2IQpOZppD4eHtdpwWZoYQa1Q/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/xWm2IQpOZppD4eHtdpwWZoYQa1Q/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/xWm2IQpOZppD4eHtdpwWZoYQa1Q/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/xWm2IQpOZppD4eHtdpwWZoYQa1Q/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/hazzik/~4/Z_KEkwrAImk" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/hazzik/~3/Z_KEkwrAImk/blog-post_12.html</link><author>noreply@blogger.com (hazzik)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_VEYwyqMSqYM/SbkYU39RC8I/AAAAAAAAAEA/mOfKuhZLlAY/s72-c/seq.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://blog.hazzik.ru/2009/03/blog-post_12.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1624027437398699573.post-8617669712095947154</guid><pubDate>Wed, 11 Mar 2009 06:12:00 +0000</pubDate><atom:updated>2010-01-09T00:33:43.498+05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">опрос</category><title>Асинхронный ввод-вывод</title><description>Привет. Кому-нибудь будет интересно почитать про асинхронный ввод-вывод, проблемы и их решения?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1624027437398699573-8617669712095947154?l=blog.hazzik.ru' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/QPkEG-eagRSpCL6ppSiuro_o9vM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/QPkEG-eagRSpCL6ppSiuro_o9vM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/QPkEG-eagRSpCL6ppSiuro_o9vM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/QPkEG-eagRSpCL6ppSiuro_o9vM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/hazzik/~4/NdQ_idZTFsU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/hazzik/~3/NdQ_idZTFsU/blog-post.html</link><author>noreply@blogger.com (hazzik)</author><thr:total>1</thr:total><feedburner:origLink>http://blog.hazzik.ru/2009/03/blog-post.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1624027437398699573.post-8132496381224981370</guid><pubDate>Thu, 05 Mar 2009 04:04:00 +0000</pubDate><atom:updated>2010-01-09T00:34:12.329+05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">mvc</category><category domain="http://www.blogger.com/atom/ns#">asp.net</category><category domain="http://www.blogger.com/atom/ns#">.NET 3.5</category><category domain="http://www.blogger.com/atom/ns#">asp.net mvc</category><title>ASP.NET MVC Controls</title><description>Привет. На днях вышел ASP.NET MVC RC2, который в связку MVC + WebForms View Engine привнес привычные для ASP.NET-программистов контролы. Контролы лежат во фьчерс паке(он же Microsoft.Web.Mvc).&lt;br /&gt;&lt;br /&gt;Давайте разберемся что нам это даст?&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;+ Design-time preview&lt;br /&gt;+ Исчезнут так называемые code nuggets, все будет выполнено в стиле ASP:)&lt;br /&gt;И самое главное - минусы:&lt;br /&gt;- Скромные возможности data-binding, если с помощью хелперов можно было как-то настроить отображение данных (например привести к верхнему регистру), то тут только as is&lt;br /&gt;- Работают только с вебформами (хотя это скорее фича).&lt;br /&gt;В конечном итоге что использовать: расширения HtmlHelper или MVC Controls решать вам:)&lt;br /&gt;&lt;br /&gt;Подробнее можно почитать у &lt;a href="http://weblogs.asp.net/leftslipper/archive/2009/03/03/asp-net-mvc-release-candidate-2-i-declare-myself-to-be-declarative.aspx"&gt;Eilon Lipton&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1624027437398699573-8132496381224981370?l=blog.hazzik.ru' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/RZXTR9ZIj7soCAm9AmFPkkl43XA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/RZXTR9ZIj7soCAm9AmFPkkl43XA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/RZXTR9ZIj7soCAm9AmFPkkl43XA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/RZXTR9ZIj7soCAm9AmFPkkl43XA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/hazzik/~4/YUqELjBxxF8" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/hazzik/~3/YUqELjBxxF8/mvc-controls.html</link><author>noreply@blogger.com (hazzik)</author><thr:total>3</thr:total><feedburner:origLink>http://blog.hazzik.ru/2009/03/mvc-controls.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1624027437398699573.post-7644707880491400425</guid><pubDate>Sat, 28 Feb 2009 13:43:00 +0000</pubDate><atom:updated>2010-01-09T00:35:06.159+05:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">sitemap.xml</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">asp.net</category><category domain="http://www.blogger.com/atom/ns#">SEO</category><category domain="http://www.blogger.com/atom/ns#">поисковая оптимизация</category><category domain="http://www.blogger.com/atom/ns#">.NET 3.5</category><category domain="http://www.blogger.com/atom/ns#">google sitemap</category><category domain="http://www.blogger.com/atom/ns#">asp.net mvc</category><title>Разработка динамического sitemap</title><description>Что такое sitemap, наверное знают все, кто не знает - может почитать &lt;a href="http://www.blogger.com/www.sitemaps.org/ru/"&gt;тут&lt;/a&gt; или &lt;a href="http://www.google.com/support/webmasters/bin/answer.py?answer=40318&amp;amp;topic=13450"&gt;тут&lt;/a&gt;. Итак начнем, для начала создадим пару тестов, для вспомогательных методов&lt;a name='more'&gt;&lt;/a&gt;:&lt;br /&gt;&lt;pre class="brush:csharp"&gt;[Fact]&lt;br /&gt;public void XDocumentEquals() {&lt;br /&gt; XDocument expected = XDocument.Parse(@"&amp;lt;someTag&amp;gt;A&amp;lt;/someTag&amp;gt;");&lt;br /&gt; XDocument actual = XDocument.Parse(@"&amp;lt;someTag&amp;gt;A&amp;lt;/someTag&amp;gt;");&lt;br /&gt; Equal(expected, actual);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:csharp"&gt;[Fact]&lt;br /&gt;public void XDocumentNotEquals() {&lt;br /&gt; XDocument expected = XDocument.Parse(@"&amp;lt;someTag&amp;gt;A&amp;lt;/someTag&amp;gt;");&lt;br /&gt; XDocument actual = XDocument.Parse(@"&amp;lt;someTag&amp;gt;B&amp;lt;/someTag&amp;gt;");&lt;br /&gt; Assert.Throws&amp;lt;EqualException&amp;gt;(() =&amp;gt; Equal(expected, actual));&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;И затем реализуем этот метод, он нам понадобиться для сравнения экземпляров класса XDocument.&lt;br /&gt;&lt;pre class="brush:csharp"&gt;public static void Equal(XObject expected, XObject actual) {&lt;br /&gt; Assert.Equal(expected.ToString(), actual.ToString());&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;cat&gt;&lt;br /&gt;При форматировании XDocument в строку, по умолчанию стоит кодировка "utf-16", а нам бы хотелось "utf-8", поэтому напишем тест для функции-расширения XDocument.ToXml():&lt;br /&gt;&lt;pre class="brush:csharp"&gt;public class XDocumentExtensionsFacts {&lt;br /&gt; [Fact]&lt;br /&gt; public void ToXml() {&lt;br /&gt;     var expected = @"&amp;lt;?xml version=""1.0"" encoding=""UTF-8""?&amp;gt;&lt;br /&gt; &amp;lt;root /&amp;gt;";&lt;br /&gt;     var xdoc = new XDocument(new XElement("root"));&lt;br /&gt;     var actual = xdoc.ToXml();&lt;br /&gt;     Assert.Equal(expected, actual, StringComparer.Create(CultureInfo.CurrentCulture, true));&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;И получим примерно такую реализацию:&lt;br /&gt;&lt;pre class="brush:csharp"&gt;public class EncodedStringWriter : StringWriter {&lt;br /&gt; private readonly Encoding _encoding;&lt;br /&gt;&lt;br /&gt; public EncodedStringWriter(Encoding encoding) {&lt;br /&gt;     _encoding = encoding;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public EncodedStringWriter() : this(Encoding.UTF8) {&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public override Encoding Encoding {&lt;br /&gt;     get { return _encoding; }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public static class XDocumentExtension {&lt;br /&gt; public static string ToXml(this XDocument doc) {&lt;br /&gt;     using(var writer = new EncodedStringWriter()) {&lt;br /&gt;         doc.Save(writer);&lt;br /&gt;         return writer.ToString();&lt;br /&gt;     }&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Создадим класс SiteUrl, который будет хранить информацию о url'ах сайта, и уметь сериализоваться в XElement, для этого напишем несколько тестов:&lt;br /&gt;&lt;pre class="brush:csharp"&gt;public class SiteUrlFacts {&lt;br /&gt; [Fact]&lt;br /&gt; public void Constructor() {&lt;br /&gt;     var expected = new Uri("http://somesite.com");&lt;br /&gt;     var target = new SiteUrl(expected);&lt;br /&gt;     Uri actual = target.Location;&lt;br /&gt;     Assert.Equal(expected, actual);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; [Fact]&lt;br /&gt; public void PriorityLessThenZeroThrowsException() {&lt;br /&gt;     var target = new SiteUrl("http://somesite.com");&lt;br /&gt;     int illegalPriority = new Random().Next(int.MinValue, 0);&lt;br /&gt;     Assert.True(illegalPriority &amp;lt; 0);&lt;br /&gt;     Assert.Throws&amp;lt;NotSupportedException&amp;gt;(() =&amp;gt; target.Priority = illegalPriority);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; [Fact]&lt;br /&gt; public void PriorityMoreThenOneThrowsException() {&lt;br /&gt;     var target = new SiteUrl("http://somesite.com");&lt;br /&gt;     int illegalPriority = new Random().Next(1, int.MaxValue);&lt;br /&gt;     Assert.True(illegalPriority &amp;gt; 1);&lt;br /&gt;     Assert.Throws&amp;lt;NotSupportedException&amp;gt;(() =&amp;gt; target.Priority = illegalPriority);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; [Fact]&lt;br /&gt; public void PrioritySetNormal() {&lt;br /&gt;     var target = new SiteUrl("http://somesite.com");&lt;br /&gt;     var normalPriority = (float)(new Random().NextDouble());&lt;br /&gt;     Assert.True(normalPriority &amp;gt;= 0);&lt;br /&gt;     Assert.True(normalPriority &amp;lt;= 1);&lt;br /&gt;     target.Priority = normalPriority;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; [Fact]&lt;br /&gt; public void SerializeHasAllArguments() {&lt;br /&gt;     XElement expected = XElement.Parse(&lt;br /&gt;         @"&amp;lt;url xmlns=""http://www.sitemaps.org/schemas/sitemap/0.9""&amp;gt;&lt;br /&gt; &amp;lt;loc&amp;gt;http://www.example.com/&amp;lt;/loc&amp;gt;&lt;br /&gt; &amp;lt;lastmod&amp;gt;2005-01-01T00:00:00 05:00&amp;lt;/lastmod&amp;gt;&lt;br /&gt; &amp;lt;changefreq&amp;gt;monthly&amp;lt;/changefreq&amp;gt;&lt;br /&gt; &amp;lt;priority&amp;gt;0.8&amp;lt;/priority&amp;gt;&lt;br /&gt;&amp;lt;/url&amp;gt;");&lt;br /&gt;     var siteUrl = new SiteUrl("http://www.example.com/") {&lt;br /&gt;         LastModified = new DateTimeOffset(new DateTime(2005, 01, 01)),&lt;br /&gt;         ChangeFrequency = ChangeFrequency.Monthly,&lt;br /&gt;         Priority = 0.8f,&lt;br /&gt;     };&lt;br /&gt;     XElement actual = siteUrl.Serialize();&lt;br /&gt;     XObjectAssert.Equal(expected, actual);&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Ну и соответственно реализация:&lt;br /&gt;&lt;pre class="brush:csharp"&gt;public enum ChangeFrequency {&lt;br /&gt; Always,&lt;br /&gt; Hourly,&lt;br /&gt; Daily,&lt;br /&gt; Weekly,&lt;br /&gt; Monthly,&lt;br /&gt; Yearly,&lt;br /&gt; Never,&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class SiteUrl {&lt;br /&gt; private float? _priority;&lt;br /&gt;&lt;br /&gt; public SiteUrl(string location)&lt;br /&gt;     : this(new Uri(location)) {&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public SiteUrl(Uri location) {&lt;br /&gt;     Location = location;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public Uri Location { get; private set; }&lt;br /&gt; public DateTimeOffset? LastModified { get; set; }&lt;br /&gt; public ChangeFrequency? ChangeFrequency { get; set; }&lt;br /&gt;&lt;br /&gt; public float? Priority {&lt;br /&gt;     get { return _priority; }&lt;br /&gt;     set {&lt;br /&gt;         if(value &amp;lt; 0 || value &amp;gt; 1) {&lt;br /&gt;             throw new NotSupportedException("Priority must be between 0 and 1");&lt;br /&gt;         }&lt;br /&gt;         _priority = value;&lt;br /&gt;     }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public virtual XElement Serialize() {&lt;br /&gt;     return new XElement(SiteMapBuilder._sitemap   "url",&lt;br /&gt;                         new XElement(SiteMapBuilder._sitemap   "loc", Location),&lt;br /&gt;                         LastModified.HasValue&lt;br /&gt;                             ? new XElement(SiteMapBuilder._sitemap   "lastmod",&lt;br /&gt;                                            LastModified.Value.ToString("yyyy-MM-ddTHH:mm:ss%K"))&lt;br /&gt;                             : null,&lt;br /&gt;                         ChangeFrequency.HasValue&lt;br /&gt;                             ? new XElement(SiteMapBuilder._sitemap   "changefreq",&lt;br /&gt;                                            ChangeFrequency.Value.ToString().ToLower())&lt;br /&gt;                             : null,&lt;br /&gt;                         Priority.HasValue&lt;br /&gt;                             ? new XElement(SiteMapBuilder._sitemap   "priority",&lt;br /&gt;                                            Priority.Value)&lt;br /&gt;                             : null);&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Дальше необходимо написать класс, который будет уметь сериализовать в XML коллекцию SiteMap'ов. напишем соответствующие тесты:&lt;br /&gt;&lt;pre class="brush:csharp"&gt;public class SiteMapBuilderFacts {&lt;br /&gt; [Fact]&lt;br /&gt; public void CreateEmptySitemap() {&lt;br /&gt;     XDocument expected = XDocument.Parse(&lt;br /&gt;         @"&amp;lt;?xml version=""1.0"" encoding=""UTF-8""?&amp;gt;&lt;br /&gt;&amp;lt;urlset xmlns=""http://www.sitemaps.org/schemas/sitemap/0.9"" /&amp;gt;");&lt;br /&gt;     XDocument actual = new SiteMapBuilder(new List&amp;lt;SiteUrl&amp;gt;()).Build();&lt;br /&gt;     XObjectAssert.Equal(expected, actual);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; [Fact]&lt;br /&gt; public void CreateSitemapWithOneElement() {&lt;br /&gt;     XDocument expected =&lt;br /&gt;         XDocument.Parse(&lt;br /&gt;             @"&amp;lt;?xml version=""1.0"" encoding=""UTF-8""?&amp;gt;&lt;br /&gt;&amp;lt;urlset xmlns=""http://www.sitemaps.org/schemas/sitemap/0.9""&amp;gt;&lt;br /&gt;&amp;lt;url&amp;gt;&lt;br /&gt;&amp;lt;loc&amp;gt;http://www.example.com/&amp;lt;/loc&amp;gt;&lt;br /&gt;&amp;lt;/url&amp;gt;&lt;br /&gt;&amp;lt;/urlset&amp;gt;");&lt;br /&gt;     var urlset = new List&amp;lt;SiteUrl&amp;gt; { new SiteUrl("http://www.example.com/") };&lt;br /&gt;     XDocument actual = new SiteMapBuilder(urlset).Build();&lt;br /&gt;     XObjectAssert.Equal(expected, actual);&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;И реализуем:&lt;br /&gt;&lt;pre class="brush:csharp"&gt;public class SiteMapBuilder {&lt;br /&gt; public static readonly XNamespace _sitemap = "http://www.sitemaps.org/schemas/sitemap/0.9";&lt;br /&gt; private readonly IEnumerable&amp;lt;SiteUrl&amp;gt; _urlset;&lt;br /&gt;&lt;br /&gt; public SiteMapBuilder(IEnumerable&amp;lt;SiteUrl&amp;gt; urlset) {&lt;br /&gt;     _urlset = urlset;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public XDocument Build() {&lt;br /&gt;     return new XDocument(new XElement(_sitemap + "urlset", _urlset.Select(x =&amp;gt; x.Serialize())));&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;И теперь осталось самое главное - как прикрутить это к ASP.NET? можно так:&lt;br /&gt;&lt;pre class="brush:csharp"&gt;&amp;lt;%@ WebHandler Language="C#" Class="SiteMapHandler" %&amp;gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Web;&lt;br /&gt;using Hazzik.SiteMap;&lt;br /&gt;&lt;br /&gt;public class SiteMapHandler : IHttpHandler {&lt;br /&gt; #region IHttpHandler Members&lt;br /&gt;&lt;br /&gt; public void ProcessRequest(HttpContext context) {&lt;br /&gt;     var urlset = new List&amp;lt;SiteUrl&amp;gt; { new SiteUrl("http://localhost/") };&lt;br /&gt;     var xml = new SiteMapBuilder(urlset).Build().ToXml();&lt;br /&gt;     HttpResponse response = context.Response;&lt;br /&gt;     response.Clear();&lt;br /&gt;     response.ContentType = "text/xml";&lt;br /&gt;     response.Write(xml);&lt;br /&gt;     response.End();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public bool IsReusable {&lt;br /&gt;     get { return true; }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; #endregion&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Для ASP.NET MVC можно написать примерно следующую реализацию ActionResult:&lt;br /&gt;&lt;pre class="brush:csharp"&gt;public class SiteMapResult : ActionResult {&lt;br /&gt; private readonly IEnumerable&amp;lt;SiteUrl&amp;gt; _map;&lt;br /&gt;&lt;br /&gt; public SiteMapResult(IEnumerable&amp;lt;SiteUrl&amp;gt; map) {&lt;br /&gt;     _map = map;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public IEnumerable&amp;lt;SiteUrl&amp;gt; Map {&lt;br /&gt;     get { return _map; }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public override void ExecuteResult(ControllerContext context) {&lt;br /&gt;     string xml = new SiteMapBuilder(Map).Build().ToXml();&lt;br /&gt;     HttpResponseBase response = context.HttpContext.Response;&lt;br /&gt;     response.ContentType = "text/xml";&lt;br /&gt;     response.Write(xml);&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ну а дальше я думаю разберетесь, удачи:)&lt;br /&gt;Строго не серчайте, это моя первая запись на техническую тему, да и русским литературным я не очень хорошо владею:) Замечания и комментарии приветствуются:)&lt;/cat&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1624027437398699573-7644707880491400425?l=blog.hazzik.ru' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/GSuAyxtkXho608f4Z-9uEcwve1A/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/GSuAyxtkXho608f4Z-9uEcwve1A/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/GSuAyxtkXho608f4Z-9uEcwve1A/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/GSuAyxtkXho608f4Z-9uEcwve1A/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/hazzik/~4/65GApzx2d_A" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/hazzik/~3/65GApzx2d_A/sitemap_28.html</link><author>noreply@blogger.com (hazzik)</author><thr:total>0</thr:total><feedburner:origLink>http://blog.hazzik.ru/2009/02/sitemap_28.html</feedburner:origLink></item></channel></rss>

