How to Link to OData Collection in Razor using ASP.NET MVC Web API OData - asp.net

I have an ASP.NET MVC 4 app that i'm incorporating an OData API into. This is running the 2012.2 stuff with the larger OData support.
I did not use a separate area for this...that might have been a mistake but my app is small and area seemed overkill.
I've got my controllers setup correctly and an example path to my Segments collection (segments is a type in my domain) is "/odata/Segments". This loads as expected and is working.
On my homepage i'm trying to add a link to this resource using Razor's Html.ActionLink (or RouteLink) but it seems the OData controllers layout doesn't quite work with those methods because the controllers are prefixed with "odata" when registered in WebAPIConfig:
config.Routes.MapODataRoute("OData Route", "odata", model );
I can trick the method to construct the correct url by pretending there's an odata controller when there certainly isn't one (as far as i know) with something like this:
#Html.RouteLink("Segments", "Segments", "odata")
but that seems like a hack.
I don't quite understand the ASP.NET routing plumbing well enough to understand how that prefix passed to MapODataRoute is being incorporated into the MVC chain so that i can use the "right" razor method the "right" way.
just for kicks, here's my SegmentsController:
public class SegmentsController : EntitySetController<Segment, long>
{
private MarketerDB db = new MarketerDB();
// GET api/segments
override public IQueryable<Segment> Get()
{
return db.Segments.AsQueryable();
}
protected override Segment GetEntityByKey(long key)
{
return db.Segments.Find(key);
}
public IQueryable<Affiliate> GetAffiliates([FromODataUri] long key)
{
return this.GetEntityByKey(key).Affiliates.AsQueryable();
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}

We have an ODataLink method on System.Web.Http.UrlHelper but we forgot to add one to the MVC System.Web.Mvc.UrlHelper. Till we add it, you can use this extension method,
namespace System.Web.Mvc
{
public static class UrlHelperExtensions
{
private static IODataPathHandler _pathHandler = new DefaultODataPathHandler();
public static string ODataUrl(this UrlHelper urlHelper, string routeName, params ODataPathSegment[] segments)
{
string odataPath = _pathHandler.Link(new ODataPath(segments));
return urlHelper.HttpRouteUrl(
routeName,
new RouteValueDictionary() { { ODataRouteConstants.ODataPath, odataPath } });
}
}
}
and call it from your razor views by doing something like (assuming there is an entityset customers and you want to put the navigation link to orders on customers(42)),
#Url.ODataUrl("odata", new EntitySetPathSegment("customers"), new KeyValuePathSegment("42"), new NavigationPathSegment("orders"))
Make sure you have an #using System.Web.Http.OData.Routing directive in your razor view.

Related

GetControllerName not being called in derived DefaultHttpControllerSelector implementation

I'm attempting to do versioning with ASP.NET Web API. I have created a simple controllerselector that derives from DefaultHttpControllerSelector.
public class MyHttpControllerSelector : DefaultHttpControllerSelector
{
public MyHttpControllerSelector(HttpConfiguration config) : base(config) { }
public override string GetControllerName(HttpRequestMessage request)
{
return "SomeControllerName";
}
}
In my WebApiConfig.Register:
config.MapHttpAttributeRoutes();
config.Services.Replace(typeof(IHttpControllerSelector), new MyHttpControllerSelector(config));
My problem is GetControllerName is never called. I may be misunderstanding the intent of this function, but I have seen plenty of examples, some right from Microsoft that use this method.
What am I missing?
In my case it was because I was not calling config.Routes.MapHttpRoute(). I was just using Attribute Routing. Once I used MapHttpRoute() it seemed to start calling GetControllerName.

Infinite URL Parameters for ASP.NET MVC Route

I need an implementation where I can get infinite parameters on my ASP.NET Controller. It will be better if I give you an example :
Let's assume that I will have following urls :
example.com/tag/poo/bar/poobar
example.com/tag/poo/bar/poobar/poo2/poo4
example.com/tag/poo/bar/poobar/poo89
As you can see, it will get infinite number of tags after example.com/tag/ and slash will be a delimiter here.
On the controller I would like to do this :
foreach(string item in paramaters) {
//this is one of the url paramaters
string poo = item;
}
Is there any known way to achieve this? How can I get reach the values from controller? With Dictionary<string, string> or List<string>?
NOTE :
The question is not well explained IMO but I tried my best to fit it.
in. Feel free to tweak it
Like this:
routes.MapRoute("Name", "tag/{*tags}", new { controller = ..., action = ... });
ActionResult MyAction(string tags) {
foreach(string tag in tags.Split("/")) {
...
}
}
The catch all will give you the raw string. If you want a more elegant way to handle the data, you could always use a custom route handler.
public class AllPathRouteHandler : MvcRouteHandler
{
private readonly string key;
public AllPathRouteHandler(string key)
{
this.key = key;
}
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
var allPaths = requestContext.RouteData.Values[key] as string;
if (!string.IsNullOrEmpty(allPaths))
{
requestContext.RouteData.Values[key] = allPaths.Split('/');
}
return base.GetHttpHandler(requestContext);
}
}
Register the route handler.
routes.Add(new Route("tag/{*tags}",
new RouteValueDictionary(
new
{
controller = "Tag",
action = "Index",
}),
new AllPathRouteHandler("tags")));
Get the tags as a array in the controller.
public ActionResult Index(string[] tags)
{
// do something with tags
return View();
}
That's called catch-all:
tag/{*tags}
Just in case anyone is coming to this with MVC in .NET 4.0, you need to be careful where you define your routes. I was happily going to global.asax and adding routes as suggested in these answers (and in other tutorials) and getting nowhere. My routes all just defaulted to {controller}/{action}/{id}. Adding further segments to the URL gave me a 404 error. Then I discovered the RouteConfig.cs file in the App_Start folder. It turns out this file is called by global.asax in the Application_Start() method. So, in .NET 4.0, make sure you add your custom routes there. This article covers it beautifully.
in asp .net core you can use * in routing
for example
[HTTPGet({*id})]
this code can multi parameter or when using send string with slash use them to get all parameters

ASP.NET MVC 2.0 JsonRequestBehavior Global Setting

ASP.NET MVC 2.0 will now, by default, throw an exception when an action attempts to return JSON in response to a GET request. I know this can be overridden on a method by method basis by using JsonRequestBehavior.AllowGet, but is it possible to set on a controller or higher basis (possibly the web.config)?
Update: Per Levi's comment, this is what I ended up using-
protected override JsonResult Json(object data, string contentType, System.Text.Encoding contentEncoding)
{
return Json(data, contentType, JsonRequestBehavior.AllowGet);
}
This, like other MVC-specific settings, is not settable via Web.config. But you have two options:
Override the Controller.Json(object, string, Encoding) overload to call Json(object, string, Encoding, JsonRequestBehavior), passing JsonRequestBehavior.AllowGet as the last argument. If you want this to apply to all controllers, then do this inside an abstract base controller class, then have all your controllers subclass that abstract class.
Make an extension method MyJson(this Controller, ...) which creates a JsonResult and sets the appropriate properties, then call it from your controller via this.MyJson(...).
There's another option. Use Action Filters.
Create a new ActionFilterAttribute, apply it to your controller or a specific action (depending on your needs). This should suffice:
public class JsonRequestBehaviorAttribute : ActionFilterAttribute
{
private JsonRequestBehavior Behavior { get; set; }
public JsonRequestBehaviorAttribute()
{
Behavior = JsonRequestBehavior.AllowGet;
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
var result = filterContext.Result as JsonResult;
if (result != null)
{
result.JsonRequestBehavior = Behavior;
}
}
}
Then apply it like this:
[JsonRequestBehavior]
public class Upload2Controller : Controller
MVC 2 block Json for GET requests for security reasons. If you want to override that behavior, check out the overload for Json that accepts a JsonRequestBehavior parameter.
public ActionResult Index()
{
return Json(data, JsonRequestBehavior.AllowGet)
}
I also got this error when I first use MVC 2.0 using my old code in MVC 1.0. I use fiddler to identify the cause of the error. See the steps on how to troubleshoot it using Fidder -
http://www.rodcerrada.com/post/2011/07/11/jQuery-getJSON()-does-not-tirgger-the-callback-in-ASPNET-MVC-2.aspx
Is this is the security issue MVC2 was trying to address?
http://haacked.com/archive/2009/06/25/json-hijacking.aspx
If so, it seems like the vulnerability is only an issue if you are trying to do a json call to an outside website. If your MVC2 app is only making json calls to your own website (to fill jqgrids for example), shouldn't you be able to safely override the Json call in your base controller to always allow get?
Just change JSON code from :
$.getJson("methodname/" + ID, null, function (data, textStatus)
to:
$.post("methodname/" + ID, null, function (data, textStatus)

Injecting Lower Layer Dependency in Presenter in an ASP.NET MVP Application

I recently read Phil Haack's post where he gives an example of implementing Model View Presenter for ASP.NET. One of the code snippets shows how the code for the view class.
public partial class _Default : System.Web.UI.Page, IPostEditView
{
PostEditController controller;
public _Default()
{
this.controller = new PostEditController(this, new BlogDataService());
}
}
However, here the view constructs the instance of the BlogDataService and passes it along to the presenter. Ideally the view should not know about BlogDataService or any of the presenter's lower layer dependencies. But i also prefer to keep the BlogDataService as a constructor injected dependency of the presenter as it makes the dependencies of the presenter explicit.
This same question has been asked on stackoverflow here.
One of the answers suggests using a service locator to get the instance of the BlogDataService and passing it along to the presenter's constructor.This solution however does not solve the problem of the view knowing about the BlogDataService and needing to explicitly get a reference to it.
Is there a way to automatically construct the presenter object using an IoC or DI container tool such that the view does not have to deal with explicitly creating the BlogDataService object and also injecting the view and service instances into the presenter's constructor. I prefer to use the constructor injection pattern as far as possible.
Or is there a better design available to solve the problem?. Can there be a better way to implement this If i am building a WinForms application instead of a ASP.NET WebForms application?
Thanks for any feedback.
Yes there is. For example using StructureMap in a webform constructor:
public partial class AttributeDetails : EntityDetailView<AttributeDetailPresenter>, IAttributeDetailView
{
public AttributeDetails()
{
_presenter = ObjectFactory.With<IAttributeDetailView>(this).GetInstance<AttributeDetailPresenter>();
}
....
}
and as you can see here presenter needs view and service injected
public AttributeDetailPresenter(IAttributeDetailView view, IAttributeService attributeService)
{
MyForm = view;
AppService = attributeService;
}
You can also use StructureMap BuildUp feature for webforms so that you can avoid using ObjectFactory directly in your view.
I did exactly this. The solution is based on Autofac, but can be implemented on top of any container.
First, define an interface representing the authority for presenting views in a request to the MVP system:
public interface IMvpRequest
{
void Present(object view);
}
Next, create a base page which has a property of that type:
public abstract class PageView : Page
{
public IMvpRequest MvpRequest { get; set; }
}
At this point, set up dependency injection for pages. Most containers have ASP.NET integration, usually in the form of HTTP modules. Because we don't create the page instance, we can't use constructor injection, and have to use property injection here only.
After that is set up, create event arguments representing a view which is ready to be presented:
public class PresentableEventArgs : EventArgs
{}
Now, catch the events in PageView and pass them to the request (present the page as well):
protected override bool OnBubbleEvent(object source, EventArgs args)
{
var cancel = false;
if(args is PresentableEventArgs)
{
cancel = true;
Present(source);
}
else
{
cancel = base.OnBubbleEvent(source, args);
}
return cancel;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Present(this);
}
private void Present(object view)
{
if(MvpRequest != null && view != null)
{
MvpRequest.Present(view);
}
}
Finally, create base classes for each type of control you'd like to serve as a view (master pages, composite controls, etc.):
public abstract class UserControlView : UserControl
{
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
EnsureChildControls();
RaiseBubbleEvent(this, new PresentableEventArgs());
}
}
This connects the control tree to the MVP system via IMvpRequest, which you'll now have to implement and register in the application-level container. The ASP.NET integration should take care of injecting the implementation into the page. This decouples the page entirely from presenter creation, relying on IMvpRequest to do the mapping.
The implementation of IMvpRequest will be container-specific. Presenters will be registered in the container like other types, meaning their constructors will automatically be resolved.
You will have some sort of map from view types to presenter types:
public interface IPresenterMap
{
Type GetPresenterType(Type viewType);
}
These are the types you will resolve from the container.
(The one gotcha here is that the view already exists, meaning the container doesn't create the instance or ever know about it. You will have to pass it in as a resolution parameter, another concept supported by most containers.)
A decent default mapping might look like this:
[Presenter(typeof(LogOnPresenter))]
public class LogOnPage : PageView, ILogOnView
{
// ...
}

How to use Dependency Injection with ASP.NET Web Forms

I am trying to work out a way to use dependency injection with ASP.NET Web Forms controls.
I have got lots of controls that create repositories directly, and use those to access and bind to data etc.
I am looking for a pattern where I can pass repositories to the controls externally (IoC), so my controls remain unaware of how repositories are constructed and where they come from etc.
I would prefer not to have a dependency on the IoC container from my controls, therefore I just want to be able to construct the controls with constructor or property injection.
(And just to complicate things, these controls are being constructed and placed on the page by a CMS at runtime!)
Any thoughts?
UPDATE 2019:
With the introduction of Web Forms 4.7.2, there is now better support for DI. This invalidates the below. See: Wiring up Simple Injector in WebForms in .NET 4.7.2
You can use automatic constructor injection by replacing the default PageHandlerFactory with a custom one. This way you can use an overloaded constructor to load the dependencies. Your page might look like this:
public partial class HomePage : System.Web.UI.Page
{
private readonly IDependency dependency;
public HomePage(IDependency dependency)
{
this.dependency = dependency;
}
// Do note this protected ctor. You need it for this to work.
protected HomePage () { }
}
Configuring that custom PageHandlerFactory can be done in the web.config as follows:
<?xml version="1.0"?>
<configuration>
<system.web>
<httpHandlers>
<add verb="*" path="*.aspx"
type="YourApp.CustomPageHandlerFactory, YourApp"/>
</httpHandlers>
</system.web>
</configuration>
Your CustomPageHandlerFactory can look like this:
public class CustomPageHandlerFactory : PageHandlerFactory
{
private static object GetInstance(Type type)
{
// TODO: Get instance using your favorite DI library.
// for instance using the Common Service Locator:
return Microsoft.Practices.ServiceLocation
.ServiceLocator.Current.GetInstance(type);
}
public override IHttpHandler GetHandler(HttpContext cxt,
string type, string vPath, string path)
{
var page = base.GetHandler(cxt, type, vPath, path);
if (page != null)
{
// Magic happens here ;-)
InjectDependencies(page);
}
return page;
}
private static void InjectDependencies(object page)
{
Type pageType = page.GetType().BaseType;
var ctor = GetInjectableCtor(pageType);
if (ctor != null)
{
object[] arguments = (
from parameter in ctor.GetParameters()
select GetInstance(parameter.ParameterType)
.ToArray();
ctor.Invoke(page, arguments);
}
}
private static ConstructorInfo GetInjectableCtor(
Type type)
{
var overloadedPublicConstructors = (
from constructor in type.GetConstructors()
where constructor.GetParameters().Length > 0
select constructor).ToArray();
if (overloadedPublicConstructors.Length == 0)
{
return null;
}
if (overloadedPublicConstructors.Length == 1)
{
return overloadedPublicConstructors[0];
}
throw new Exception(string.Format(
"The type {0} has multiple public " +
"ctors and can't be initialized.", type));
}
}
Downside is that this only works when running your side in Full Trust. You can read more about it here. But do note that developing ASP.NET applications in partial trust seems a lost cause.
Starting from .NET 4.7.2 (what's new), it is now easy for developers to use Dependency Injection in WebForms applications. With the UnityAdapter, you can add it to your existing WebForms application in 4 simple steps. See this blog.
Autofac supports fairly unobtrusive dependency injection in ASP.NET WebForms. My understanding is it just hooks into the ASP.NET page lifecycle using an http module and does property injection. The only catch is that for controls I don't think this happens until after the Init event.
The best way is to have a base class for the controls like:
public class PartialView : UserControl
{
protected override void OnInit(System.EventArgs e)
{
ObjectFactory.BuildUp(this);
base.OnInit(e);
}
}
That will inject any control that inherits from that base class (uses structuremap). Combining that with a property based config, you will be able to have controls like:
public partial class AdminHeader : PartialView
{
IMyRepository Repository{get;set;}
}
Update 1: If you can't have the controls inherit, perhaps the CMS has a hook right after creating the controls, in there you can call the BuildUp. Also if the CMS allows you to hook something to fetch the instance you could use constructor based injection, but I prefer BuildUp on this specific scenario as asp.net doesn't have a hook for this.
You could also create some singleton instances in the Application_Start global.asax event and have them available as public static readonly properties.
This is a solution I recently used to avoid hooking into the pipeline (I find that confuses everyone that looks at my code in the future, but yes, I see its benefits as well):
public static class TemplateControlExtensions
{
static readonly PerRequestObjectManager perRequestObjectManager = new PerRequestObjectManager();
private static WIIIPDataContext GetDataContext(this TemplateControl templateControl)
{
var dataContext = (WIIIPDataContext) perRequestObjectManager.GetValue("DataContext");
if (dataContext == null)
{
dataContext = new WIIIPDataContext();
perRequestObjectManager.SetValue("DataContext", dataContext);
}
return dataContext;
}
public static IMailer GetMailer(this TemplateControl templateControl)
{
return (IMailer)IoC.Container.Resolve(typeof(IMailer));
}
public static T Query<T>(this TemplateControl templateControl, Query<T> query)
{
query.DataContext = GetDataContext(templateControl);
return query.GetQuery();
}
public static void ExecuteCommand(this TemplateControl templateControl, Command command)
{
command.DataContext = GetDataContext(templateControl);
command.Execute();
}
private class PerRequestObjectManager
{
public object GetValue(string key)
{
if (HttpContext.Current != null && HttpContext.Current.Items.Contains(key))
return HttpContext.Current.Items[key];
else
return null;
}
public void SetValue(string key, object newValue)
{
if (HttpContext.Current != null)
HttpContext.Current.Items[key] = newValue;
}
}
}
This shows how you can create your own life time manager pretty easily as well as hook into an IoC container if you so desire. Oh, and I am also using a query/command structure which is sort of unrelated, but more on the reasoning behind that can be found here:
Limit your abstractions: Refactoring toward reduced abstractions

Resources