MVC 2.0, StructureMap, области и дубликаты имен контроллеров - PullRequest
0 голосов
/ 15 октября 2010

У меня есть небольшая проблема. У меня есть область под названием Framed. В этой области есть домашний контроллер. По умолчанию для сайта также есть домашний контроллер.

То, что я пытаюсь сделать с этим, это иметь версию каждого контроллера / действия, которая подходит для IFrame, и версию, которая является обычным сайтом. Я делаю это через мастер-страницы, а на главной странице сайта много заполнителей, отличных от рамочной версии. По этой причине я не могу просто поменять местами главную страницу. Например, http://example.com/Framed/Account/Index покажет очень простую версию с информацией о вашей учетной записи для использования на внешнем сайте. http://example.com/Account/Index покажет те же данные, но внутри сайта по умолчанию.

Мой контейнер IoC - это Structuremap. Итак, я нашел http://odetocode.com/Blogs/scott/archive/2009/10/19/mvc-2-areas-and-containers.aspx и http://odetocode.com/Blogs/scott/archive/2009/10/13/asp-net-mvc2-preview-2-areas-and-routes.aspx. Вот мои текущие настройки.

Структура карты Init

ObjectFactory.Initialize(x =>
            {
                x.AddRegistry(new ApplicationRegistry());
                x.Scan(s =>
                {
                    s.AssembliesFromPath(HttpRuntime.BinDirectory);
                    s.AddAllTypesOf<IController>()
                        .NameBy(type => type.Namespace + "." + type.Name.Replace("Controller", ""));
                });
            });

Проблема, которую я обнаружил при отладке, заключается в том, что поскольку контроллеры имеют одно и то же имя (HomeController), он регистрирует только первое, которое является домашним контроллером по умолчанию. Я проявил творческий подход и добавил пространство имен, чтобы оно регистрировало все мои контроллеры.

Маршрут по умолчанию

routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { area = "", controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
                new[] { "MySite.Controllers" }
                );

Маршрут области

context.MapRoute(
                "Framed_default",
                "Framed/{controller}/{action}/{id}",
                new { area = "Framed", controller = "Home", action = "Index", id = UrlParameter.Optional },
                new string[] { "MySite.Areas.Framed.Controllers" }
            );

Как рекомендует Фил Хаак , я использую пространства имен в качестве 4-го параметра

запуск приложения, просто чтобы доказать порядок инициализации

protected void Application_Start()
        {
            InitializeControllerFactory();

            AreaRegistration.RegisterAllAreas();

            RouteConfiguration.RegisterRoutes();
        }

Контроллер Фабрика

protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            IController result = null;
            if (controllerType != null)
            {
                result = ObjectFactory.GetInstance(controllerType)
                    as IController;
            }
            return result;
        }

Итак, когда я нажимаю / Home / Index, он переходит к правильному типу контроллера. Когда я нажимаю / Framed / Home / Index, controllerType имеет значение null, что приводит к ошибкам, поскольку контроллер не возвращается.

Как будто MVC полностью игнорирует мою область. Что тут происходит? Что я делаю не так?

Ответы [ 2 ]

0 голосов
/ 27 октября 2010

У меня была похожая проблема с использованием Structuremap с областями. У меня была область с именем Admin, и всякий раз, когда вы пытались перейти к / admin, она получала на фабрике контроллеров StructureMap с типом контроллера null.

Я исправил это, следуя этому сообщению в блоге: http://stephenwalther.com/blog/archive/2008/08/07/asp-net-mvc-tip-30-create-custom-route-constraints.aspx

Пришлось добавить ограничение на маршрут по умолчанию, чтобы оно не совпадало, если контроллер был администратором.

Вот мое определение маршрута по умолчанию:

routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "MyController", action = "AnAction", id = UrlParameter.Optional },
    new { controller = new NotEqualConstraint("Admin")},
    new string[] {"DailyDealsHQ.WebUI.Controllers"} 
);

и вот реализация NotEqualConstraint:

public class NotEqualConstraint : IRouteConstraint
{
    private string match = String.Empty;

    public NotEqualConstraint(string match)
    {
        this.match = match;
    }

    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        return String.Compare(values[parameterName].ToString(), match, true) != 0;
    }
}

Возможно, есть другие способы решения этой проблемы, но это исправило это для меня:)

0 голосов
/ 16 октября 2010

В случае, если кто-то пытается сделать что-то подобное, я использовал идею из этого поста: Категории контроллеров в MVC Routing? (Дублирующие имена контроллеров в отдельных пространствах имен) Мне пришлось вообще создавать дамп, используя области, и сам что-то реализовывать.

У меня есть Controllers / HomeController.cs и Controllers / Framed / HomeController.cs

У меня есть класс ControllerBase, от которого наследуются все контроллеры в / Controllers. У меня есть AreaController, который наследует от ControllerBase, от которого расширяются все контроллеры в / Controllers / Framed.

Вот мой класс контроллера области

public class AreaController : ControllerBase
    {
        private string Area
        {
            get
            {
                return this.GetType().Namespace.Replace("MySite.Controllers.", "");
            }
        }
        protected override ViewResult View(string viewName, string masterName, object model)
        {
            string controller = this.ControllerContext.RequestContext.RouteData.Values["controller"].ToString();

            if (String.IsNullOrEmpty(viewName))
                viewName = this.ControllerContext.RequestContext.RouteData.Values["action"].ToString();

            return base.View(String.Format("~/Views/{0}/{1}/{2}.aspx", Area, controller, viewName), masterName, model);
        }

        protected override PartialViewResult PartialView(string viewName, object model)
        {
            string controller = this.ControllerContext.RequestContext.RouteData.Values["controller"].ToString();

            if (String.IsNullOrEmpty(viewName))
                viewName = this.ControllerContext.RequestContext.RouteData.Values["action"].ToString();

            PartialViewResult result = null;

            result = base.PartialView(String.Format("~/Views/{0}/{1}/{2}.aspx", Area, controller, viewName), model);

            if (result != null)
                return result;

            result = base.PartialView(String.Format("~/Views/{0}/{1}/{2}.ascx", Area, controller, viewName), model);

            if (result != null)
                return result;

            result = base.PartialView(viewName, model);

            return result;
        }
    }

Мне пришлось переопределить методы представления и частичного просмотра. Таким образом, контроллеры в моей «области» могут использовать методы по умолчанию для представлений и частичных данных и поддерживать добавленные структуры папок.

Что касается просмотров, у меня есть Views / Home / Index.aspx и Views / Framed / Home / Index.aspx. Я использую маршрутизацию, как показано в посте, но вот как моя выглядит для справки:

var testNamespace = new RouteValueDictionary();
            testNamespace.Add("namespaces", new HashSet<string>(new string[] 
            { 
                "MySite.Controllers.Framed"
            }));

            //for some reason we need to delare the empty version to support /framed when it does not have a controller or action
            routes.Add("FramedEmpty", new Route("Framed", new MvcRouteHandler())
            {
                Defaults = new RouteValueDictionary(new
                {
                    controller = "Home",
                    action = "Index",
                    id = UrlParameter.Optional
                }),
                DataTokens = testNamespace
            });

            routes.Add("FramedDefault", new Route("Framed/{controller}/{action}/{id}", new MvcRouteHandler())
            {
                Defaults = new RouteValueDictionary(new
                {
                    //controller = "Home",
                    action = "Index",
                    id = UrlParameter.Optional
                }),
                DataTokens = testNamespace
            });

var defaultNamespace = new RouteValueDictionary();
            defaultNamespace.Add("namespaces", new HashSet<string>(new string[] 
            { 
                "MySite.Controllers"
            }));

routes.Add("Default", new Route("{controller}/{action}/{id}", new MvcRouteHandler())
                {
                    Defaults = new RouteValueDictionary(new
                    {
                        controller = "Home",
                        action = "Index",
                        id = UrlParameter.Optional
                    }),
                    DataTokens = defaultNamespace
                });

Теперь я могу перейти на / Home / Index или / Framed / Home / Index на одном сайте и получить два разных представления с общим контролем. В идеале мне бы хотелось, чтобы один контроллер возвращал одно из двух представлений, но я понятия не имею, как заставить это работать без двух контроллеров.

...