Архитектура плагинов для ASP.NET MVC

Я провел некоторое время, просматривая статью Фила Хаака о Группировка контроллеров очень интересная штука.

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

Итак, мой вопрос: возможно ли разделить статью Области в Филе на несколько проектов?

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

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

У меня есть некоторый опыт работы с архитектурой плагинов, но не с массивами, поэтому любое руководство в этой области будет полезно.

70 голосов | спросил Simon Farrow 4 ThuEurope/Moscow2008-12-04T13:40:22+03:00Europe/Moscow12bEurope/MoscowThu, 04 Dec 2008 13:40:22 +0300 2008, 13:40:22

6 ответов


0

Несколько недель назад я проверил концепцию концепции, где я поместил полный набор компонентов: класс модели, класс контроллера и связанные с ними представления в DLL, добавил /отрегулировал один из примеров классов VirtualPathProvider, которые извлекают представления, чтобы они обращались к тем в DLL соответственно.

В конце я просто поместил DLL в правильно настроенное приложение MVC, и оно работало так же, как если бы оно было частью приложения MVC с самого начала. Я продвинул его немного дальше, и он отлично работал с 5 из этих маленьких плагинов mini-MVC. Очевидно, вы должны следить за ссылками и конфиг-зависимостями, когда все перемешиваете, но это сработало.

Это упражнение было направлено на функциональность плагина для платформы на основе MVC, которую я создаю для клиента. Существует основной набор контроллеров и представлений, которые дополняются более дополнительными в каждом экземпляре сайта. Мы собираемся сделать эти дополнительные биты в эти модульные плагины DLL. Пока все хорошо.

Я написал обзор своего прототипа и пример решения для плагинов ASP.NET MVC на моем сайте.

РЕДАКТИРОВАТЬ: 4 года спустя я делал довольно много приложений ASP.NET MVC с плагинами и больше не использую метод, который я описал выше. На этом этапе я запускаю все свои плагины через MEF и вообще не помещаю контроллеры в плагины. Скорее, я создаю общие контроллеры, которые используют информацию о маршрутизации для выбора плагинов MEF и перекладывают работу на плагин и т. Д. Просто подумал, что я бы добавил, так как этот ответ поразил всех.

ответил J Wynia 4 ThuEurope/Moscow2008-12-04T20:07:09+03:00Europe/Moscow12bEurope/MoscowThu, 04 Dec 2008 20:07:09 +0300 2008, 20:07:09
0

На самом деле я работаю над платформой расширяемости для использования поверх ASP.NET MVC. Моя структура расширяемости основана на известном контейнере Ioc: Structuremap.

Вариант использования, который я пытаюсь выполнить, прост: создайте приложение, которое должно иметь некоторые базовые функциональные возможности, которые могут быть расширены для каждого клиента (= мультитенантность). Должен быть только один экземпляр размещенного приложения, но этот экземпляр можно адаптировать для каждого клиента без внесения каких-либо изменений в основной веб-сайт.

Меня вдохновила статья о многопользовательском режиме, написанная Айенде Рахиен: http://ayende.com/Blog/archive/2008/08/16/Multi-Tenancy--Approaches-and-Applicability.aspx Еще одним источником вдохновения стала книга Эрика Эванса о доменно-управляемом дизайне. Моя структура расширяемости основана на шаблоне хранилища и концепции корневых агрегатов. Чтобы иметь возможность использовать фреймворк, хост-приложение должно быть построено вокруг репозиториев и доменных объектов. Контроллеры, репозитории или доменные объекты связываются во время выполнения с помощью ExtensionFactory.

Плагин - это просто сборка, которая содержит контроллеры, репозитории или доменные объекты, которые соответствуют определенному соглашению об именах. Соглашение об именах простое, каждый класс должен начинаться с префикса customerID, например: AdventureworksHomeController.

Чтобы расширить приложение, скопируйте сборку плагина в папку расширения приложения. Когда пользователь запрашивает страницу в корневой папке клиента, например:   http://multitenant-site.com/[customerID]/[контроллер] /[действие] фреймворк проверяет, есть ли плагин для этого конкретного клиента, и создает экземпляры пользовательских классов плагинов, в противном случае он загружает значение по умолчанию один раз. Пользовательские классы могут быть Контроллеры - Хранилища или Объекты Домена. Такой подход позволяет расширить приложение на всех уровнях, от базы данных до пользовательского интерфейса, через модель домена, репозитории.

Когда вы хотите расширить некоторые существующие функции, вы создаете подключаемый модуль, содержащий подклассы основного приложения. Когда вы создаете совершенно новые функциональные возможности, вы добавляете новые контроллеры внутри плагина. Эти контроллеры будут загружены платформой MVC при запросе соответствующего URL. Если вы хотите расширить пользовательский интерфейс, вы можете создать новое представление внутри папки расширений и ссылаться на представление новым или подклассовым контроллером. Чтобы изменить существующее поведение, вы можете создавать новые репозитории или доменные объекты или подклассифицировать выходящие из них. Основой ответственности является определение того, какой контроллер /хранилище /объект домена должен быть загружен для конкретного клиента.
Я советую взглянуть на карту структуры ( http://structuremap.sourceforge.net/Default.htm). >) и особенно в функциях реестра DSL http://structuremap.sourceforge.net/RegistryDSL.htm

Это код, который я использую при запуске приложения для регистрации всех подключаемых контроллеров /репозиториев или объектов домена:

protected void ScanControllersAndRepositoriesFromPath(string path)
        {
            this.Scan(o =>
            {
                o.AssembliesFromPath(path);
                o.AddAllTypesOf<SaasController>().NameBy(type => type.Name.Replace("Controller", ""));
                o.AddAllTypesOf<IRepository>().NameBy(type => type.Name.Replace("Repository", ""));
                o.AddAllTypesOf<IDomainFactory>().NameBy(type => type.Name.Replace("DomainFactory", ""));
            });
        }

Я также использую ExtensionFactory, унаследованный от System.Web.MVC. DefaultControllerFactory. Эта фабрика отвечает за загрузку объектов расширения (контроллеров /реестров или объектов домена). Вы можете подключить свои собственные фабрики, зарегистрировав их при запуске в файле Global.asax:

protected void Application_Start()
        {
            ControllerBuilder.Current.SetControllerFactory(
                new ExtensionControllerFactory()
                );
        }

Этот фреймворк как полнофункциональный пример сайта можно найти по адресу: http: //code .google.com /р /multimvc /

ответил Geo 24 MarpmTue, 24 Mar 2009 17:35:55 +03002009-03-24T17:35:55+03:0005 2009, 17:35:55
0

Итак, я немного поигрался с примером из J Wynia выше. Большое спасибо за это.

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

Вероятно, это полное злоупотребление способом использования VirtualFiles; -)

вы получите:

частный статический IDictionary resourceVirtualFile;

со строкой, являющейся виртуальными путями.

код ниже делает некоторые предположения о пространстве имен файлов .aspx, но он будет работать в простых случаях. Это хорошо, что вам не нужно создавать сложные пути просмотра, они создаются из имени ресурса.

class ResourceVirtualFile : VirtualFile
{
    string path;
    string assemblyName;
    string resourceName;

    public ResourceVirtualFile(
        string virtualPath,
        string AssemblyName,
        string ResourceName)
        : base(virtualPath)
    {
        path = VirtualPathUtility.ToAppRelative(virtualPath);
        assemblyName = AssemblyName;
        resourceName = ResourceName;
    }

    public override Stream Open()
    {
        assemblyName = Path.Combine(HttpRuntime.BinDirectory, assemblyName + ".dll");

        Assembly assembly = Assembly.ReflectionOnlyLoadFrom(assemblyName);
        if (assembly != null)
        {
            Stream resourceStream = assembly.GetManifestResourceStream(resourceName);
            if (resourceStream == null)
                throw new ArgumentException("Cannot find resource: " + resourceName);
            return resourceStream;
        }
        throw new ArgumentException("Cannot find assembly: " + assemblyName);
    }

    //todo: Neaten this up
    private static string CreateVirtualPath(string AssemblyName, string ResourceName)
    {
        string path = ResourceName.Substring(AssemblyName.Length);
        path = path.Replace(".aspx", "").Replace(".", "/");
        return string.Format("~{0}.aspx", path);
    }

    public static IDictionary<string, VirtualFile> FindAllResources()
    {
        Dictionary<string, VirtualFile> files = new Dictionary<string, VirtualFile>();

        //list all of the bin files
        string[] assemblyFilePaths = Directory.GetFiles(HttpRuntime.BinDirectory, "*.dll");
        foreach (string assemblyFilePath in assemblyFilePaths)
        {
            string assemblyName = Path.GetFileNameWithoutExtension(assemblyFilePath);
            Assembly assembly = Assembly.ReflectionOnlyLoadFrom(assemblyFilePath);  

            //go through each one and get all of the resources that end in aspx
            string[] resourceNames = assembly.GetManifestResourceNames();

            foreach (string resourceName in resourceNames)
            {
                if (resourceName.EndsWith(".aspx"))
                {
                    string virtualPath = CreateVirtualPath(assemblyName, resourceName);
                    files.Add(virtualPath, new ResourceVirtualFile(virtualPath, assemblyName, resourceName));
                }
            }
        }

        return files;
    }
}

Затем вы можете сделать что-то подобное в расширенном VirtualPathProvider:

    private bool IsExtended(string virtualPath)
    {
        String checkPath = VirtualPathUtility.ToAppRelative(virtualPath);
        return resourceVirtualFile.ContainsKey(checkPath);
    }

    public override bool FileExists(string virtualPath)
    {
        return (IsExtended(virtualPath) || base.FileExists(virtualPath));
    }

    public override VirtualFile GetFile(string virtualPath)
    {
        string withTilda = string.Format("~{0}", virtualPath);

        if (resourceVirtualFile.ContainsKey(withTilda))
            return resourceVirtualFile[withTilda];

        return base.GetFile(virtualPath);
    }
ответил Simon Farrow 9 TueEurope/Moscow2008-12-09T19:38:19+03:00Europe/Moscow12bEurope/MoscowTue, 09 Dec 2008 19:38:19 +0300 2008, 19:38:19
0

Я полагаю, что можно оставить свои представления в проектах плагинов.

Это моя идея: вам нужен ViewEngine, который будет вызывать плагин (вероятно, через интерфейс) и запрашивать представление (IView). Затем плагин будет создавать экземпляр представления не через его URL (как это делает обычный ViewEngine - /Views/Shared/View.asp), а через имя представления (например, через отражение или контейнер DI /IoC).

Возвращение представления в плагине может быть даже жестко задано (приведен простой пример):

public IView GetView(string viewName)
{
    switch (viewName)
    {
        case "Namespace.View1":
            return new View1();
        case "Namespace.View2":
            return new View2();
        ...
    }
}

... это была просто идея, но я надеюсь, что это сработает или просто послужит хорошим вдохновением.

ответил gius 4 ThuEurope/Moscow2008-12-04T14:48:34+03:00Europe/Moscow12bEurope/MoscowThu, 04 Dec 2008 14:48:34 +0300 2008, 14:48:34
0

Этот пост может быть немного запоздалым, но я играю с ASP.NET MVC2 и придумала прототип с использованием функции "Области".

Вот ссылка для всех, кому это интересно: http://www.veebsbraindump.com/2010/06/asp-net-mvc2-plugins-using-areas/

ответил Veebs 15 J0000006Europe/Moscow 2010, 09:23:11
0

[публикуется как ответ, потому что я не могу комментировать]

Отличное решение - я использовал подход J Wynia и получил его для визуализации вида из отдельной сборки. Однако этот подход, по-видимому, только отображает представление. Контроллеры в плагине не поддерживаются, верно? Например, если представление из плагина отправило сообщение назад, контроллер этого представления внутри плагина будет не называться . Вместо этого он будет направлен на контроллер в корневом приложении MVC . Я правильно понимаю, или есть обходной путь для этой проблемы?

ответил tbehunin 24 FebruaryEurope/MoscowbWed, 24 Feb 2010 00:15:44 +0300000000amWed, 24 Feb 2010 00:15:44 +030010 2010, 00:15:44

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132