Мой предыдущий пост касался того, как использовать API Magento с WCF. Еще один аспект Magento, с которым я столкнулся, - это чрезвычайно гибкая поддержка тематического.
Вы можете разработать новую тему, которая выглядит радикально отличной от по умолчанию. Мало того, что вы можете изменить изображения и цвета в каскадных листах в стиле, вы также можете переопределить регионы (заголовок, контент, нижний колонтитул ...), которые составляют страницу. В соответствии с регионом вы можете указать, какой HTML входит в него. Это дает вам максимальную мощность настройки для темации вашего сайта.
Тема в ASP.NET поддерживается вне коробки. Используя папку APP_THEMES, вы можете настроить внешний вид своего сайта, как бы ни была обременительная эта система.
Magento построен с использованием шаблона MVC, как и Framework ASP.NET MVC. И это в центре внимания этой статьи. Как мы можем реализовать тематические тематические данные в ASP.NET MVC?
Начнем ...
Прежде чем мы начнем кодирование, давайте обобжим целей, которые мы хотим достичь. Давайте предположим, что у нас есть компания, которая импортирует мебель, сделанную в Китае, Вьетнаме, Южно-Кореи ... и т. Д. Мы продаем не непосредственно пользователям, а посредникам.
Мы хотим разработать веб-приложение ASP.NET MVC, которое все наши рееллеры можно использовать в качестве сайта электронной коммерции. У каждого посредника есть свое доменное имя и хочет продать свои товары онлайн. Каждый из этих доменов связан с нашим единственным веб -приложением.
Функциональность, предлагаемая веб -приложением, одинакова для каждого реселлера, но каждый реселлер хочет настроить свой интернет -магазин, применив пользовательскую тему. Мы будем поддерживать следующие ситуации:
Поэтому мы должны выяснить, как мы можем динамически заменить лист стилей (CSS), главную страницу, представления и частичные представления.
Прежде чем мы сможем начать, нам нужно заложить основу. Давайте начнем с построения оснований для простого демонстрационного применения.
Примечание : В этом разделе я даю быстрый обзор настройки демонстрационного приложения, чтобы можно было продемонстрировать функцию темации. Это просто быстрый и легкий подход. Основное внимание в этой статье не в том, как разработать модель домена, разработка вашего бизнес -логического уровня ... и т. Д. Поэтому я держу это как можно более коротким. Не стесняйтесь улучшить это.
База данных
Создайте новую базу данных с помощью SQL Server Express (2005 или 2008). Вдохновленный северной базой данных, я назвал свою базу данных [WindDirection].
Эта база данных содержит ровно одну таблицу под названием [Reseller]. Создайте таблицу, как показано на следующем рисунке.
Рисунок 1 - [Список] Таблица
![Таблица посредников [Reseller] Table Design](https://images.downcodes.com/uploads/20250616/img_68500176a3ae030.png)
Как видно, столбец ID является первичной ключом, и он использует спецификацию идентификации (= Autoincrement). Также добавьте уникальное ограничение в столбце домена, так как у каждого посредника есть свой уникальный домен.
Примечание : исходный код, сопровождающий эту статью, содержит сценарий (ddl.sql), который позволяет быстро генерировать эту таблицу, если вы не хотите разрабатывать его вручную.
Последняя часть настройки нашей базы данных - вводить некоторые фиктивные записи для таблицы [посредников]. Пожалуйста, введите следующие записи:
Рисунок 2 - Пятники

У нас четыре посредника. Первый реселлер не имеет пользовательской темы и возвращается к по умолчанию. У всех остальных определены собственная индивидуальная тема.
Начните Visual Studio 2008 и создайте новое пустое решение под названием «MVCapplication». Добавьте новую библиотеку кода и назовите ее "cgeers.winddirection.database". Удалить автоматически сгенерированный файл class1.css.
Затем добавьте новый элемент LINQ в SQL Classe и назовите его «DataClasses». Переименовать новый DataContext в «WindDirectionDataContext». Теперь перетащите таблицу [Reseller] с вкладки Server Explorer на поверхность Designer Linq в SQL.
Рисунок 3 - сущность посредника

Установите свойство подключения DataContext на «Нет» и удалите настройку приложения строки подключения и файл конфигурации приложения (app.config). Я не люблю тот факт, что Visual Studio вводит для меня строку подключения. Мне нравится делать это сам.
Вот почему я добавил следующий частичный класс в эту сборку, которая обрабатывает инициализацию DataContext с помощью строки соединения. Единственная часть, с которой мы должны договориться, - это то, что строка соединения называется «WindDirection».
Листинг 1 - WindDirectionDataContext Class
public partial class WindDirectionDataContext
{
private static readonly string ConnectionString ;
static WindDirectionDataContext ( )
{
ConnectionStringSettings settings = ConfigurationManager . ConnectionStrings [ "WindDirection" ] ;
ConnectionString = settings != null ? settings . ConnectionString : String . Empty ;
}
public WindDirectionDataContext ( ) : base ( ConnectionString ) { }
}Не забудьте добавить ссылку на сборку System.configuration. Пока вы включите строку подключения, называемую «Wind Direction» в приложениях, которые ссылаются на эту сборку, она будет работать просто отлично.
Мы почти там. Просто держись, мы сделаем это. Теперь добавьте новую библиотеку кода в решение под названием «cgeers.winddirection.managers». Удалите автоматически сгенерированный файл class1.cs и добавьте ссылку на сборку System.data.linq.
Добавьте новый класс с именем Manager и добавьте в него следующий код:
Листинг 2 - Аннотация Менеджер
public abstract class Manager
{
protected Manager ( )
{
Context = new WindDirectionDataContext ( ) ;
}
public WindDirectionDataContext Context { get ; set ; }
}Этот очень простой класс создает новый обратный данных, на котором мы можем развязать наши запросы LINQ позже.
Затем добавьте класс под названием «ResellerManager» в проект и добавьте код, показанный в листинге 3.
Список 3 ResellerManager
public class ResellerManager : Manager
{
public string GetThemeForDomain ( string domain )
{
var q = from r in Context . Resellers
where r . Domain == domain
select r . Theme ;
string theme = q . SingleOrDefault ( ) ;
return ! String . IsNullOrEmpty ( theme ) ? theme : "Default" ;
}
}Этот класс менеджера выходит из нашего класса Abstract Manager и добавляет один метод под названием GetThemefordomain (...). Этот метод ищет тему посредника на основе данного доменного имени. Поскольку каждый домен уникально связан с одним посредником, это не представляет проблем.
Вуаля, это весь доступ к данным, который необходим для нашего демонстрационного приложения. Нам нужно выяснить тему посредника на основе домена входящего запроса, а затем мы должны применить ее.
Примечание : Остерегайтесь использования контекста LINQ для SQL в приложении ASP.NET. Хотя это и не продемонстрировано в этой статье, потому что она будет слишком отвлечься от нашего основного усилия, желательно создать только один контекст по запросу. Храните контекст в httpcontext запроса, чтобы вы могли получить к нему доступ всегда во время запроса.
Некоторое время назад я написал статью специально об этом, ознакомьтесь с статьей Entity Framework ObjectContext здесь. Хотя он имеет дело с структурой сущности вместо LINQ для SQL, это все еще применимо.
Последний шаг в завершении нашего основного демонстрационного приложения - добавление нового проекта веб -сайта в решение. Добавьте новый проект в решение, основанное на шаблоне проекта веб -приложения ASP.NET MVC и назвайте его «MVCapplication». Вас спросят, хотите ли вы также создать модульный тестовый проект для этого приложения. Выберите «Нет», чтобы пропустить это, поскольку нам это не нужно для этой статьи.
Visual Studio генерирует приложение «Привет, мир!»-введите приложение ASP.NET MVC, которое содержит ряд страниц по умолчанию (дома, о том, что войти ... и т. Д.). Добавьте свою строку подключения в файл web.config и добавьте ссылки на cgeers.winddirection.database и cgeers.winddirection.managers.
Примечание : Web.Config содержит ряд настроек конфигурации, относящиеся к членству ASP.NET, профиль, роли ... поставщики. Вы можете пойти дальше и удалить их, так как они нам не нужны.
Ваше решение для исследования должно напоминать рисунок 4.
Рисунок 4 - Решение исследователя

Замечание : На момент написания этой статьи я использую ASP.NET MVC версию 1.0. Однако версия 2.0 будет выпущена в ближайшем будущем.
При запуске веб -приложения первое, что ему нужно выяснить, - это тема, которую она должна применить. Это должно быть сделано для каждого отдельного запроса. Таким образом, подключение настраиваемого HTTP-модуля в пипетке запроса кажется подходящим.
Добавьте новый класс в проект MVCapplication и назовите его Themehttpmodule. Пусть класс реализует интерфейс ihttpmodule. Весь код для этого класса показан в списке 4.
Эта статья не является праймером для написания HTTP -модулей, поэтому, если вам требуется дополнительная информация, пожалуйста, ознакомьтесь с статьей «Прохождение: создание и регистрация пользовательского HTTP -модуля» на MSDN.
Листинг 4 - Themehttpmodule
public class ThemeHttpModule : IHttpModule
{
public void Init ( HttpApplication application )
{
application . BeginRequest += application_BeginRequest ;
}
private void application_BeginRequest ( object sender , EventArgs e )
{
HttpApplication application = ( HttpApplication ) sender ;
HttpContext context = application . Context ;
if ( context . Cache == null )
{
return ;
}
string domain = context . Request . Url . GetDomain ( ) ;
string cacheKey = String . Format ( CultureInfo . InvariantCulture , "theme_for_{0}" , domain ) ;
if ( context . Cache [ cacheKey ] == null )
{
ResellerManager manager = new ResellerManager ( ) ;
string theme = manager . GetThemeForDomain ( domain ) ;
context . Cache [ cacheKey ] = theme ;
}
}
public void Dispose ( ) { }
}Этот модуль HTTP добавляет обработчик событий для события BeginRequest. Это событие происходит как первое событие в цепочке выполнения HTTP, когда ASP.NET отвечает на запрос.
Здесь мы извлекаем доменное имя из входящего запроса. Затем мы получаем тему для этого домена, используя метод GetThemefordomain (...) ResellerManager. Результат затем кэшируется. В следующий раз, когда запускается запрос на этот домен, тема будет извлечена из кэша, и запрос базы данных не будет запущен.
Метод getDomain () является методом расширения для класса URI. Проверьте исходный код этой статьи, чтобы увидеть, как она работает. Аналогичным образом вы можете извлечь субдомен (например, www, admin ... и т. Д.) Из запроса. Затем вы можете расширить свой тематический двигатель, чтобы применить разные темы для каждого поддомена домена.
И последнее, но не менее важное, зарегистрируйте Themehttpmodule, создав запись в файле web.config. Это необходимо для того, чтобы подписаться на модуль HTTP на уведомления о запросах.
Листинг 5 - Зарегистрируйте тему httpmodule
< httpModules >
< add name = " ThemeHttpModule " type = " MvcApplication.ThemeHttpModule " />
<!-- ... -->
</ httpModules >Когда вы запустите веб -приложение, вы получите по умолчанию «Взгляд и ощущение», как показано на рисунке 5. Visual Studio будет генерировать некоторые страницы по умолчанию (дома, о входе ... и т. Д.), Включая главную страницу и лист стиля. Мы будем использовать эти файлы, чтобы составить нашу тему по умолчанию.
Рисунок 5 - ASP.NET MVC Приложение по умолчанию тема

По умолчанию все файлы сохраняются в папках содержимого и представления. Нам нужно реализовать нашу собственную структуру каталогов, чтобы мы могли логически группировать наши темы. Поэтому создайте новую папку, называемую темами. Создайте подпалку для каталога тем и назовите его по умолчанию. Переместите каталог контента и представлений в рамках этого каталога по умолчанию.
После перемещения папок содержимого и представлений вам необходимо регулировать свойство MasterPageFile директивы Page для каждого из представлений! Старое значение ссылается на место, которое больше не существует. Изменить MasterPageFile = "~/views/shared/site.master" на MasterPageFile = "~/Themes/Default/Views/shared/site.master" !
Рисунок 6 - Тема по умолчанию

Вуаля, наша тема по умолчанию была настроена. Если вы хотите создать новую тему, вам нужно только создать новую папку и поместить ее в папку Themes. Как вы можете видеть на предыдущем скриншоте, мы будем создавать некоторые другие темы (зеленый, оранжевый, красный) позже.
Мы только что перенесли наши главные страницы, простыни в стиле, просмотры ... и т. Д. в другой каталог. Если мы начнем наше веб -приложение сейчас, мы получим следующее исключение:
Рисунок 7 - InvalidoperationException

Представление «индекс» или его мастер не может быть найден. Были искали следующие места:
MVC пытается найти представление для вашей страницы запуска по умолчанию, но не может найти его в местах по умолчанию, которую он ищет, таким образом, вы получаете исключение. Мы переместили эти файлы в нашу папку темы по умолчанию, и скоро мы будем создавать другие темы. Нам нужен способ информировать MVC о местах, где искать его представления, главную страницу, частичные представления ... и т. Д. Эти местоположения различаются на теме посредника.
Так что в основном все, что нам нужно сделать, чтобы поддержать тематинг в ASP.NET MVC, это:
Для этого нам нужно написать наш собственный двигатель просмотра. MVC использует двигатель View для рендеринга страниц для ответа. Этот двигатель просмотра отвечает за поиск главной страницы, просмотров и частичных представлений. По умолчанию используется WebFormViewEngine.
Нам нужно заменить этот двигатель по умолчанию самостоятельно. Чтобы сделать это, добавьте новый класс под названием TheDViewEngine в проект MVCapplication и пусть он спустится с класса WebFormViewEngine.
Листинг 6 - темадвивенгин
public class ThemedViewEngine : WebFormViewEngine
{
#region Constructor(s)
// Replace the default search paths by our own.
public ThemedViewEngine ( )
{
// Search paths for the master pages
base . MasterLocationFormats = new [ ]
{
"~/Themes/{2}/Views/{1}/{0}.master" ,
"~/Themes/{2}/Views/Shared/{0}.master"
} ;
// Search paths for the views
base . ViewLocationFormats = new [ ]
{
"~/Themes/{2}/Views/{1}/{0}.aspx" ,
"~/Themes/{2}/Views/{1}/{0}.ascx" ,
"~/Themes/{2}/Views/Shared/{0}.aspx" ,
"~/Themes/{2}/Views/Shared/{0}.ascx" ,
} ;
// Search parts for the partial views
// The search parts for the partial views are the same as the regular views
base . PartialViewLocationFormats = base . ViewLocationFormats ;
}
#endregion
}В конструкторе этого нового двигателя просмотра мы устанавливаем MasterLocationFormats, ViewLocationFormats и PartialViewLocationFormats в новые местоположения, например: ~/Themes/{2}/Views/{1}/{0} .aspx.
Каждый путь содержит 3 части, которые определяются динамически.
Чтобы использовать новый двигатель View, вам нужно зарегистрировать его. Сделайте это, добавив следующий код в обработчик событий Application_start, расположенный в файле Global.asax.cs.
Листинг 7 - Зарегистрируйте тематическую
protected void Application_Start ( )
{
ViewEngines . Engines . Clear ( ) ;
ViewEngines . Engines . Add ( new ThemedViewEngine ( ) ) ;
RegisterRoutes ( RouteTable . Routes ) ;
}Здесь вы очищаете любые двигатели, которые могли быть загружены ранее, и вводить ваши собственные. Теперь все, что остается, это инструктировать двигатель просмотра, как форматировать новые пути поиска, чтобы он правильно находил запрошенные файлы. Для этого вам нужно переопределить следующие два метода:
Листинг 8 - FindPartialView (...) и FindView (...) Методы
public override ViewEngineResult FindPartialView ( ControllerContext controllerContext , string partialViewName , bool useCache )
public override ViewEngineResult FindView ( ControllerContext controllerContext , string viewName , string masterName , bool useCache )Я не собираюсь включать код для этих двух функций здесь, потому что он довольно длинный и имеет несколько ссылок на частные вспомогательные методы. В основном эти два метода следуют одной и той же схеме:
Таким образом, наш новый двигатель просмотра в основном ищет нашу тематическую папку и, если он не может найти запрошенную главную страницу, представление или частичное представление, он использует тему по умолчанию. Конечно, тема по умолчанию должна быть завершена и не может иметь никаких недостающих файлов.
Это позволяет вам создавать темы, которые содержат только главную страницу, которая, в свою очередь, ссылается на другой лист стиля или темы, которые содержат представления и / или частичные представления только для тех разделов, которые вы хотите стилизовать по -разному.
Следуя этому шаблону, вы можете создавать темы, которые переопределяют только определенные представления и отступают на представления темы по умолчанию, если не предоставлено пользовательское представление.
Я основал свой просмотр двигателя на работе превосходной статьи Криса Питшмана о тематических условиях в ASP.NET MVC. Я предлагаю вам проверить его статью, поскольку она содержит больше информации о том, как работает двигатель просмотра внутри.
Благодаря новому двигателю просмотра мы можем снова запустить веб -приложение без каких -либо исключений, поскольку теперь он может разрешить запросы на главную страницу, представления и частичные представления.
Примечание : я немного изменил код, чтобы, когда двигатель просмотра не может разрешить запрос на определенную главную страницу, просмотр или частичное представление, он возвращается на те, которые находятся в теме по умолчанию. Так что обязательно ознакомьтесь с исходным кодом этой статьи.
Давайте быстро создадим новую тему. Добавьте новую папку, называемую «красный», под папкой тем. Скопируйте сайт.master и site.css из темы по умолчанию, как показано на следующем рисунке.
Рисунок 8 - Красная тема

Откройте лист стиля красной темы и измените свой свойство фонового цвета элемента тела. Установите его на красный. Теперь откройте таблицу [Reseller] и установите поле темы «красным» для посредника, чей домен установлен на Localhost. Перезапустите веб -приложение, и теперь оно должно использовать главную страницу и лист стиля красной темы.
Рисунок 9 - Красная тема в действии

Точно так же вы можете создать апельсиновую тему, которая содержит не только главную страницу, но и другой вид для домашней страницы.
Рисунок 10 - Оранжевая тема

Оранжевая тема будет отображать новый вид для домашней страницы вместо представления по умолчанию. Если вы хотите заменить частичный вид, вы можете сделать это так же. Просто скопируйте частичное представление по умолчанию в то же место под новой папкой темы и при необходимости настройте его.
Для каждой темы вы теперь можете служить разным мастер -страницам, взглядам и частичным представлениям. Есть один оставшийся сценарий, который я хочу поддержать. Попроблетели, которые удовлетворены представлением по умолчанию, но хотят отрегулировать логотип, некоторые цвета ... и т. Д. Можно легко довольствоваться, применив другой лист стиля к теме по умолчанию.
Добавьте новую папку темы в каталог темы и назовите ее зеленым. Скопируйте лист стилей темы по умолчанию в зеленую тему, как показано на следующем рисунке.
Рисунок 11 - Зеленая тема

Откройте лист стиля зеленой темы и отрегулируйте свойство фонового цвета элемента кузова, чтобы получить зеленый. Если вы установите тему для реселлера с доменом Localhost для зеленого и запустите приложение, вы заметите, что она все еще использует лист стиля темы по умолчанию.
Это вызвано тем фактом, что зеленая тема не имеет собственной главной страницы. Он использует главную страницу темы по умолчанию, и эта главная страница ссылается на свой собственный лист стилей.
Откройте главную страницу темы по умолчанию и замените строку:
< link href =" ../../Content/Site.css " rel =" stylesheet " type =" text/css " />с
< link href =" <% " ="Html.GetThemedStyleSheet()" % /> rel="stylesheet"
type="text/css" / >Метод getThemedStylesHeeet () является методом расширения для класса HTML. Добавьте в проект новый класс под названием Htmlhelperextensions и добавьте в него следующий код.
Листинг 9 - htmlhelperextensions
public static class HtmlHelperExtensions
{
public static string GetThemedStyleSheet ( this HtmlHelper html )
{
HttpContext context = HttpContext . Current ;
if ( context == null )
{
throw new InvalidOperationException ( "Http Context cannot be null." ) ;
}
string defaultStyleSheet = context . Server . MapPath ( "~/Themes/Default/Content/Site.css" ) ;
string domain = context . Request . Url . GetDomain ( ) ;
string cacheKey = String . Format ( CultureInfo . InvariantCulture , "theme_for_{0}" , domain ) ;
string theme = ( string ) context . Cache [ cacheKey ] ;
if ( String . IsNullOrEmpty ( theme ) || theme == "Default" )
{
return defaultStyleSheet ;
}
string styleSheet = context . Server . MapPath ( String . Format ( CultureInfo . InvariantCulture ,
"~/Themes/{0}/Content/Site.css" , theme ) ) ;
if ( ! File . Exists ( styleSheet ) )
{
styleSheet = defaultStyleSheet ;
}
return String . Format ( CultureInfo . InvariantCulture , "'{0}'" , styleSheet ) ;
}
}Метод getThemedStylesHeet () загружает тему из кеша httpapplication и проверяет, имеет ли эта тема свой лист стиля. Если нет, это возвращается на лист стиля темы по умолчанию. Код содержит некоторые твердые строки, хотя и не оптимальные, он делает свое дело. Не стесняйтесь улучшить этот метод.
Если вы запустите веб -приложение сейчас, вы получите хороший зеленый фон.
В этой статье показано, как вы можете включить тематические условия в ASP.NET MVC. Для этого вам нужно только внедрить две вещи, а именно:
Система тематической, которую мы реализовали, использует тему по умолчанию и проверяет, нужно ли она заменить части этой темы по умолчанию на пользовательскую тему. Вы можете легко поддерживать один из следующих сценариев или объединить их: