url.Действие с MvcContrib генерирует недопустимые ссылки

В нашем приложении мы используем MvcContrib для создания ссылок, за исключением перекрестных ссылок, где Contrib, похоже, не работает должным образом (или мы делаем что-то не так). В сервисах у нас есть функция, которая генерирует List ‹ZakladkaModel>, который содержит URL-адрес и другие свойства, используемые при генерации tabstrib с помощью настраиваемого помощника html. Эта функция принимает в качестве аргумента идентификатор объекта базы данных и UrlHelper для помощи в создании ссылки.

m_service.GenerowanieZakladkiDlaKontrolera_ARCH_Akt(idAktu, new UrlHelper(this.ControllerContext.RequestContext));

Тогда в GenerowanieZakladkiDlaKontrolera_ARCH_Akt у нас есть что-то вроде этого:

model.Add(new ZakladkaModel { Aktywnosc = true, NazwaZakladki = "Akt", Url = "" });
model.Add(new ZakladkaModel { Aktywnosc = true, NazwaZakladki = "Wzmianki", Url = url.Action<Usc.Presentation.Areas.FU_RAU.Controllers.ARCH.ARCH_WzmiankiController>(c => c.Index(idAktu)) });
if (tekstJednolity.StanTekstuJednolitego == "RB" || tekstJednolity.StanTekstuJednolitego == "SW")
{
model.Add(new ZakladkaModel { Aktywnosc = true, NazwaZakladki = "t.j. aktu", Url = url.Action<Usc.Presentation.Areas.FU_RAU.Controllers.ARCH.ARCH_TekstJednolityController>(c => c.Edytuj(tekstJednolity.Id)) });
}
else
{
model.Add(new ZakladkaModel { Aktywnosc = true, NazwaZakladki = "t.j. aktu", Url = url.Action<Usc.Presentation.Areas.FU_RAU.Controllers.ARCH.ARCH_TekstJednolityController>(c => c.Raport(tekstJednolity.Id)) });
}
model.Add(new ZakladkaModel { Aktywnosc = true, NazwaZakladki = "Przypisek 1", Url = url.Action<Usc.Presentation.Areas.FU_RAU.Controllers.ARCH.Przypisek1Controller>(c => c.Index(idAktu)) });
model.Add(new ZakladkaModel { Aktywnosc = true, NazwaZakladki = "Przypisek 2", Url = url.Action<Usc.Presentation.Areas.FU_RAU.Controllers.ARCH.Przypisek2Controller>(c => c.Index(idAktu)) });
model.Add(new ZakladkaModel { Aktywnosc = true, NazwaZakladki = "Przypisek 3", Url = url.Action<Usc.Presentation.Areas.FU_RAU.Controllers.ARCH.Przypisek3Controller>(c => c.Edytuj(idAktu)) });
model.Add(new ZakladkaModel { Aktywnosc = true, NazwaZakladki = "Przypisek 4", Url = url.Action<Usc.Presentation.Areas.FU_RAU.Controllers.ARCH.Przypisek4Controller>(c => c.Edytuj(idAktu)) });

Теперь проблема в том, что на некоторых компьютерах коллег он правильно генерирует ссылки на действия, а на некоторых похоже, что он берет область выкупа из нашего приложения и пытается создать недопустимую ссылку. Мы могли бы использовать простой url.Action («действие», «контроллер»), который отлично работает со всеми, но мы бы предпочли MvcContrib :). Кто-нибудь знает, почему это происходит? Или может поделюсь альтернативой?


person AdrianCogiel    schedule 08.01.2013    source источник


Ответы (1)


Кажется, что LinkBuilder, который используется под, вообще не использует GetVirtualPatchForArea, что, как я читал, является ошибкой MVC. Поэтому я решил создать свой собственный HtmlHelper, который использует этот метод:

 public static string ActionArea<TController>(this HtmlHelper urlHelper, Expression<Action<TController>> expression) where TController : Controller
{
     RouteValueDictionary routeValues = GetRouteValuesFromExpression(expression);
     VirtualPathData vpd = new UrlHelper(urlHelper.ViewContext.RequestContext).RouteCollection.GetVirtualPathForArea(urlHelper.ViewContext.RequestContext, routeValues);
   return (vpd == null) ? null : vpd.VirtualPath;
}

public static string ActionArea<TController>(this UrlHelper urlHelper, Expression<Action<TController>> expression) where TController : Controller
        {
            RouteValueDictionary routeValues = GetRouteValuesFromExpression(expression);
            VirtualPathData vpd = urlHelper.RouteCollection.GetVirtualPathForArea(urlHelper.RequestContext, routeValues);
            return (vpd == null) ? null : vpd.VirtualPath;
        }

public static RouteValueDictionary GetRouteValuesFromExpression<TController>(Expression<Action<TController>> action) where TController : Controller
        {
            if (action == null)
            {
                throw new ArgumentNullException("action");
            }

            MethodCallExpression call = action.Body as MethodCallExpression;
            if (call == null)
            {
                throw new ArgumentException("Akcja nie może być pusta.", "action");
            }

            string controllerName = typeof(TController).Name;
            if (!controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase))
            {
                throw new ArgumentException("Docelowa klasa nie jest kontrolerem.(Nie kończy się na 'Controller')", "action");
            }
            controllerName = controllerName.Substring(0, controllerName.Length - "Controller".Length);
            if (controllerName.Length == 0)
            {
                throw new ArgumentException("Nie można przejść do kontrolera.", "action");
            }

            // TODO: How do we know that this method is even web callable?
            //      For now, we just let the call itself throw an exception.

            string actionName = GetTargetActionName(call.Method);

            var rvd = new RouteValueDictionary();
            rvd.Add("Controller", controllerName);
            rvd.Add("Action", actionName);

            var namespaceNazwa = typeof(TController).Namespace;

            if(namespaceNazwa.Contains("Areas."))
            {
                int index = namespaceNazwa.IndexOf('.',namespaceNazwa.IndexOf("Areas."));
                string nazwaArea = namespaceNazwa.Substring(namespaceNazwa.IndexOf("Areas.") + 6, index - namespaceNazwa.IndexOf("Areas.") + 1);
                if (!String.IsNullOrEmpty(nazwaArea))
                {
                    rvd.Add("Area", nazwaArea);
                }
            }

            //var typ = typeof(TController).GetCustomAttributes(typeof(ActionLinkAreaAttribute), true /* inherit */).FirstOrDefault();
            /*ActionLinkAreaAttribute areaAttr = typ as ActionLinkAreaAttribute;
            if (areaAttr != null)
            {
                string areaName = areaAttr.Area;
                rvd.Add("Area", areaName);
            }*/

            AddParameterValuesFromExpressionToDictionary(rvd, call);
            return rvd;
        }


private static string GetTargetActionName(MethodInfo methodInfo)
        {
            string methodName = methodInfo.Name;

            // do we know this not to be an action?
            if (methodInfo.IsDefined(typeof(NonActionAttribute), true /* inherit */))
            {
                throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,
                    "Nie można wywoływać metod innych niż akcje.", methodName));
            }

            // has this been renamed?
            ActionNameAttribute nameAttr = methodInfo.GetCustomAttributes(typeof(ActionNameAttribute), true /* inherit */).OfType<ActionNameAttribute>().FirstOrDefault();
            if (nameAttr != null)
            {
                return nameAttr.Name;
            }

            // targeting an async action?
            if (methodInfo.DeclaringType.IsSubclassOf(typeof(AsyncController)))
            {
                if (methodName.EndsWith("Async", StringComparison.OrdinalIgnoreCase))
                {
                    return methodName.Substring(0, methodName.Length - "Async".Length);
                }
                if (methodName.EndsWith("Completed", StringComparison.OrdinalIgnoreCase))
                {
                    throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,
                       "Nie można wywoływać kompletnych metod.", methodName));
                }
            }

            // fallback
            return methodName;
        }

static void AddParameterValuesFromExpressionToDictionary(RouteValueDictionary rvd, MethodCallExpression call)
        {
            ParameterInfo[] parameters = call.Method.GetParameters();

            if (parameters.Length > 0)
            {
                for (int i = 0; i < parameters.Length; i++)
                {
                    Expression arg = call.Arguments[i];
                    object value = null;
                    ConstantExpression ce = arg as ConstantExpression;
                    if (ce != null)
                    {
                        // If argument is a constant expression, just get the value
                        value = ce.Value;
                    }
                    else
                    {
                        value = CachedExpressionCompiler.Evaluate(arg);
                    }
                    rvd.Add(parameters[i].Name, value);
                }
            }
        }

Надеюсь, это поможет людям с похожими проблемами. Часть приведенного выше кода я получил из mvc2-rtm-sources, модифицированного в соответствии с моими потребностями http://aspnet.codeplex.com/releases/view/41742

person AdrianCogiel    schedule 31.01.2013