Standard MVC pattern for ControllerC/ActionA when controller code just states return View() is to look for ControllerC/ActionA.cshtml. I would like make it so if such view does not exist, it looks for some default view like Shared/Default.cshtml. How can I do this?
I don't know about MVC 5, but you could create custom class from RazorViewEngine.
public class MyFallbackLocationViewEngine : RazorViewEngine
{
public MyFallbackLocationViewEngine()
{
// Keep default locations and add our own fallback view
List<string> newLocations = new List<string>(ViewLocationFormats);
newLocations.Add("~/Views/Shared/Default.cshtml");
this.ViewLocationFormats = newLocations.ToArray();
}
}
And add it to your Application_Start:
// Clear default engines and add only yours
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new MyFallbackLocationViewEngine());
You could customize your ViewEngine as much as you lilke and override other methods
Related
I have created a custom mapper class as below but ABP does not automatically register and use it while mapping.
https://docs.abp.io/en/abp/4.4/Object-To-Object-Mapping#iobjectmapper-tsource-tdestination-interface
Sorry for less detail, i have added some below,
I have found that mycustommapperclass's interface different from my object mapper,
should I implement for all container types?
public class HierachyItemCustomMapper : IObjectMapper<HierachyItem, HierachyItemDto>, ITransientDependency
{
and my usage like
var nodeListDto = ObjectMapper.Map<IEnumerable<HierachyItem>, IEnumerable<HierachyItemDto>>(nodeList);
How can i handle this?
Obviously I am looking for a result instead of foreach iterator loop.
Edit:
it have found that it is known issue as below
https://github.com/abpframework/abp/issues/94
I've tried just before and it seems it works as expected.
This is my HierachyItemCustomMapper class which I've created in the Application layer. (It should be created in the Application layer.)
public class HierachyItemCustomMapper : IObjectMapper<HierachyItem, HierachyItemDto>, ITransientDependency
{
public HierachyItemDto Map(HierachyItem source)
{
return new HierachyItemDto
{
Name = source.Name
};
}
public HierachyItemDto Map(HierachyItem source, HierachyItemDto destination)
{
destination.Name = source.Name;
return destination;
}
}
I've just added a property named Name in my both classes (HierachyItem and HierachyItemDto) to test.
You probably didn't define it in the Application layer and that cause the problem. Can you check it?
It's simple , your defination is wrong
it should be like that
public class HierachyItemCustomMapper : IObjectMapper<IEnumerable<HierachyItem>,
IEnumerable<HierachyItemDto>>, ITransientDependency {}
as it searches for exact defination match , and if you want to add also capability of using ObjectMapper.Map<HierachyItem, HierachyItemDto>
you can make your custom mapper defination like that
public class HierachyItemCustomMapper : IObjectMapper<IEnumerable<HierachyItem>,
IEnumerable<HierachyItemDto>>, IObjectMapper<HierachyItem, HierachyItemDto> ,
ITransientDependency {}
and you will implement both
good luck
In some other places I saw people suggesting to set:
spring.data.web.pageable.one-indexed-parameters=true
However, this is not changing anything in the behavior of my application. I've put this on the properties file of my SpringBoot application.
Am I missing something ?
You can use the PageableDefault annotation, i.e. #PageableDefault(page = 1) on your Controller method, e.g.:
#RestController
public class Controller {
public Page< DataEntity > getEntities(#PageableDefault(page = 1)Pageable pageable){
//repository call here...
}
}
I have a custom RequestFilterAttribute that I am applying to my ServiceStack services:
[MyCustomAttribute]
public class MyService : ServiceStack.Service {...
I have recently begun using the AutoQuery feature (which is awesome) but I'm wondering how to apply MyCustomAttribute to the auto-generated services that you "get for free" when your request DTO inherits from QueryBase.
I could certainly add methods to my service with the "magic" AutoQuery code:
SqlExpression<DTO> sqlExpression = AutoQuery.CreateQuery(request, Request.GetRequestParams());
QueryResponse<DTO> response = AutoQuery.Execute(request, sqlExpression);
but I'm hoping there's a better way?
If you wanted to customize the AutoQuery behavior you should first take a look at the extensibility options using Query Filters provides.
Otherwise you should be able to add the RequestFilter Attribute to the Request DTO itself, i.e:
[MyCustomAttribute]
public class MyQuery : QueryBase<Poco> {}
Alternatively you can get a reference to the auto-generated Service using:
var autoQueryService = appHost.Metadata.GetServiceTypeByRequest(typeof(MyQuery));
And then use the dynamic API to add custom attributes to it, e.g:
autoQueryService
.AddAttributes(new MyCustomAttribute { ... });
Since the Services are only generated and registered once the AutoQueryFeature Plugin is executed you'll only be able to access the service after all plugins are loaded which you can do:
1) In your own plugin by implementing the IPostInitPlugin Interface
2) By registering a AfterInitCallbacks handler:
this.AfterInitCallbacks.Add(appHost => { ... });
3) By overriding OnAfterInit() virtual method in your AppHost, e.g:
public override void OnAfterInit()
{
...
base.OnAfterInit();
}
...guess I'm the first to ask about this one?
Say you have the following routes, each declared on a different controller:
[HttpGet, Route("sign-up/register", Order = 1)]
[HttpGet, Route("sign-up/{ticket}", Order = 2)]
... you could do this in MVC 5.0 with the same code except for the Order parameter. But after upgrading to MVC 5.1, you get the exception message in the question title:
Multiple controller types were found that match the URL. This can
happen if attribute routes on multiple controllers match the requested
URL.
So the new RouteAttribute.Order property is only controller-level? I know in AttributeRouting.NET you can do SitePrecedence too. Is the only way to have routes like the above when all actions are in the same controller?
Update
Sorry, I should have mentioned these routes are on MVC controllers, not WebAPI. I am not sure how this affects ApiControllers.
If you know that ticket will be an int you can specify that type in the route to help resolve the route:
[HttpGet, Route("sign-up/register")]
[HttpGet, Route("sign-up/{ticket:int}")]
This approach worked for me, per user1145404's comment that includes a link to Multiple Controller Types with same Route prefix ASP.NET Web Api
In case of Attribute routing, Web API tries to find all the controllers which match a request. If it sees that multiple controllers are able to handle this, then it throws an exception as it considers this to be possibly an user error. This route probing is different from regular routing where the first match wins.
As a workaround, if you have these two actions within the same controller, then Web API honors the route precedence and you should see your scenario working.
There are two ways to fix this:
A regex constraint, like here: MVC Route Attribute error on two different routes
Or a custom route constraint, like here: https://blogs.msdn.microsoft.com/webdev/2013/10/17/attribute-routing-in-asp-net-mvc-5/
You can create custom route constraints by implementing the IRouteConstraint interface. For example, the following constraint restricts a parameter to set of valid values:
public class ValuesConstraint : IRouteConstraint
{
private readonly string[] validOptions;
public ValuesConstraint(string options)
{
validOptions = options.Split('|');
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
object value;
if (values.TryGetValue(parameterName, out value) && value != null)
{
return validOptions.Contains(value.ToString(), StringComparer.OrdinalIgnoreCase);
}
return false;
}
}
The following code shows how to register the constraint:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
var constraintsResolver = new DefaultInlineConstraintResolver();
constraintsResolver.ConstraintMap.Add("values", typeof(ValuesConstraint));
routes.MapMvcAttributeRoutes(constraintsResolver);
}
}
Now you can apply the constraint in your routes:
public class TemperatureController : Controller
{
// eg: temp/celsius and /temp/fahrenheit but not /temp/kelvin
[Route("temp/{scale:values(celsius|fahrenheit)}")]
public ActionResult Show(string scale)
{
return Content("scale is " + scale);
}
}
In my opinion, this isn't great design. There are no judgments about what URL you intended and no specificity rules when matching unless you explicitly set them yourself. But at least you can get your URLs looking the way you want. Hopefully your constraint list isn't too long. If it is, or you don't want to hard-code the route string parameter and its constraints, you could build it programmatically outside the action method and feed it to the Route attribute as a variable.
I have an Orchard cms module with some additional Content types set up and have added an AutoRoute component via code.
Everything works perfectly, however I am not happy with the default permalink pattern.
What I am trying to do is add a custom pattern and use one of the public properties in my content type. In my case the custom type has a public property called ClubName and I would like that to be used (It makes more sense from a routing perspective).
The Orchard part class name is called TrackPart.
I have tried {Content.TrackPart.ClubName}, {Content.Track.ClubName}, {ContentItem.TrackPart.ClubName},{Content.TrackPart.ClubName} and various other variations but nothing seems to be working.
I am really new to Orchard so there is a high chance I am missing something simple.
Any help would be greatly appreciated.
In response to feedback from #Bertrand-le-roy I created my own token by copying an example token. I can now get see the token in the drop down menu and select it. However the route pattern is still not working.
I can only assume that I have misunderstood the Evaluate() function's context.For usage. It looks like I am not getting the data I need
Here is what I have so far.
public class TrackPartTokens : ITokenProvider {
private readonly IContentManager _contentManager;
public TrackPartTokens(IContentManager contentManager) {
_contentManager = contentManager;
}
public Localizer T { get; set; }
public void Describe(dynamic context) {
context.For("Track", T("Track"), T("Tokens for Track"))
.Token("ClubName", T("ClubName"), T("The name of the club."))
;
}
public void Evaluate(dynamic context) {
context.For<TrackPart>("Track")
.Token("ClubName", (Func<TrackPart, object>)(field => field.ClubName))
.Chain("ClubName", "ClubName", (Func<TrackPart, object>)(field =>field.ClubName))
;
}</code>
The above code was based on the DateTimeField token inside the Orchard.Fields module.
context.For("DateTimeField")
.Token("Date", (Func)(field => field.DateTime))
.Chain("Date", "Date", (Func)(field => field.DateTime));
I had the same issue.
After some troubleshooting I managed to get the autoroute working by changing my implementaion to the following (adapted to your example, note that your setup might require some changes to the linq-function):
In your tokens-class:
First add a using System.Linq statement.
Then change your Evaluate implementation to the following:
context.For<IContent>("Content")
.Token("ClubName", (Func<IContent>, object>)(content =>
content.ContentItem.Parts.OfType<TrackPart>().First().ClubName));
Make sure your AutoroutePart settings in Migrations.cs uses the Content-prefix. Like:
.WithPart("AutoroutePart", partBuilder =>
partBuilder
.WithSetting("AutorouteSettings.AllowCustomPattern", "true")
.WithSetting("AutorouteSettings.AutomaticAdjustmentOnEdit", "false")
.WithSetting("AutorouteSettings.PatternDefinitions",
#"[{Name:'Track', Pattern:'{Content.ClubName}',
Description:'Your description'}]")
.WithSetting("AutorouteSettings.DefaultPatternIndex", "0"))
There seems to be some problems with the TokenManager-class in Orchard source that only allows the target-parameter to equal "Content" in order for the call: _data.TryGetValue(target, out value) to work (TokenManager.cs, line 67). I have tried a number of different setups but the _data-dictionary always only contain the "Content" key.
You'll have to make your own token. It's really easy. Copy a working example.