Сменная архитектура для ASP.NET MVC

70
задан ThinkingStiff 27 July 2012 в 07:47
поделиться

6 ответов

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

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

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

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

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

51
ответ дан Community 24 November 2019 в 13:30
поделиться

Таким образом, у меня было немного игры вокруг с примером от Wynia Дж выше. Большое спасибо за это btw.

я изменил вещи так, чтобы расширение VirtualPathProvider использовало статического конструктора для создания списка всех имеющихся ресурсов, заканчивающихся .aspx в различном dll's в системе. Это является трудоемким, но только мы только делаем его однажды.

Это - вероятно, общее злоупотребление способом, которым VirtualFiles, как предполагается, используются также;-)

Вы заканчиваете с a:

частный статический 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);
    }
4
ответ дан Community 24 November 2019 в 13:30
поделиться

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

Это - моя идея: Вам нужен ViewEngine, который назвал бы плагин (вероятно, через интерфейс) и запросил бы представление (IView). Плагин тогда инстанцировал бы представления не через его URL (как обычный ViewEngine делает - /Views/Shared/View.asp), но через его название представления), например, через отражение или контейнер DI/МОК).

возврат представления в плагине мог бы меня даже hardcoded (простой пример следует):

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

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

3
ответ дан gius 24 November 2019 в 13:30
поделиться

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

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

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

Плагин является просто asselmbly, который содержит Контроллеры или Репозитории или Объекты области, который уважает определенное соглашение о присвоении имен. Соглашение о присвоении имен просто, каждый класс должен быть снабжен префиксом customerID, например: AdventureworksHomeController.

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

Когда Вы хотите расширить некоторые существующие функции, Вы создаете плагин блок, который содержит подклассы базового приложения. Когда необходимо создать полностью новые технические возможности, Вы добавляете новые контроллеры в плагине. Эти контроллеры будут загружены платформой MVC, когда соответствующий URL будут требовать. Если Вы хотите расширить UI, можно создать новое представление в дополнительной папке и сослаться на представление новым или разделенным на подклассы контроллером.To, изменяют существующее поведение, можно создать новые репозитории или объекты области или sub, классифицирующий выходящие. Ответственность за платформу состоит в том, чтобы определить, какой контроллер / репозиторий / объект области должен быть загружен для определенного клиента.
Я советую, чтобы взглянуть на structuremap (http://structuremap.sourceforge.net/Default.htm) и особенно на функции Registry 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, наследовавшийся Системе. Сеть. MVC. DefaultControllerFactory. Эта фабрика ответственна для загрузки дополнительных объектов (контроллеры/реестры или объекты области). Вы можете плагин Ваши собственные фабрики путем регистрации их при запуске в файле Global.asax:

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

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

14
ответ дан Greg Ogle 24 November 2019 в 13:30
поделиться

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

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

0
ответ дан 24 November 2019 в 13:30
поделиться

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

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

3
ответ дан 24 November 2019 в 13:30
поделиться
Другие вопросы по тегам:

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