Url.RouteUrl returns null - asp.net-mvc

I'm buiding a UrlHelper for a route
as in best practices
the problem is that the returned value is always null
when debugging in found that
Url.RouteUrl("x") return null
Url.RouteCollection["X"] return Route
i'm trying to do :
public static string Category(this UrlHelper helper, int Id, string category)
{
return helper.RouteUrl("X", new {id = Id, category= category});
}
I can't see where I'm doing something wrong

It appears that this is being caused because you did not specify a default value for {id} and {category} when registering your routes.
Url.RouteUrl("x") will return null because there's no value for id and category provided, and your route definition does not have a default.
I think that you will find if you update your route entry to specify a default value for id and category this will solve your problem. Alternatively, if you are sure to always provide a value for id and category, you can do without it.
As far as your actual Url helper method Category(), that should be working just fine as-is if you are providing a non-null or empty value for id and category. I literally copied the code and it works for me.

For some reason i was still running the mvc release candidate
I installed the mvc 1.0 and now it works fine

Related

Why is MVC attribute routing not matching this GUID?

I'm trying to use attribute routing. For various reasons, one of my Guid argments required for a password reset must not be in the queryString so I've had to put it in the path. That itself isn't a problem, except for the fact that I can't get the resetToken to populate in my controller - even when it matches the controller method based on on the Route attribute I have defined.
So given this URL:
http://example.com/Account/ResetPasswordBySMS/96b7ba88-65e0-4dbc-a012-e69545a29a55/?userid=9d394579-afbb-49c4-ba21-877f4dad91fa
...would not populate "resetToken" here? (it's always null)
Change your action to this:
[Route("~/Account/ResetPasswordBySMS/{resetToken?}")]
public ActionResult ResetPasswordBySMS(string resetToken, [FromQuery] string userId, [FromQuery] string code)
{
var guidResetToken= new Guid(resetToken);
}
I tested url http://localhost:55480/Account/ResetPasswordBySMS/96b7ba88-65e0-4dbc-a012-e69545a29a55?userid=9d394579-afbb-49c4-ba21-877f4dad91fa&code=fghjk in Postman. It is working.
And it is not a good idea to mix query string with routing.I would add userId and code to routing too:
[Route("~/Account/ResetPasswordBySMS/{resetToken?}/{userId?}/{code?}
public ActionResult ResetPasswordBySMS(string resetToken, string userId, string code)
I can see the url error - slash after reset token and before ? mark. This slash should be gone.

OData Web API routing

I have a web API exposing ODATA from a SQL stored proc. I want to use a url like /odata/firmhierarchy(225) to pass 225 into a param for the stored proc. It just tells me that it can't find a matching resource. It hits the controller, just skips the method. Thoughts?
In webapiconfig
private static IEdmModel GenerateEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Employee>("Employees");
builder.EntitySet<Employee>("FirmHierarchy");
return builder.GetEdmModel();
}
Context:
public virtual ObjectResult<Employee> sp_EmployeeHierarchy(Nullable<int> managerEmpID)
{
var managerEmpIDParameter = managerEmpID.HasValue ?
new SqlParameter("ManagerEmpID", managerEmpID) :
new SqlParameter("ManagerEmpID", 0);
return ((IObjectContextAdapter)this).ObjectContext.ExecuteStoreQuery<Employee>("sp_EmployeeHierarchy #ManagerEmpID", managerEmpIDParameter);
}
Only method in controller:
[Queryable]
public IQueryable<Employee> GetFirmHierarchy()
{
return db.sp_EmployeeHierarchy(225).AsQueryable();
//return SingleResult.Create(db.Employees.Where(employee => employee.EmpId == key));
}
This should work:
1.Write another method in your controller:
[EnableQuery]
public IQueryable<Employee> Get([FromODataUri] int key)
{
return db.sp_EmployeeHierarchy(key).AsQueryable();
}
Please note that [EnableQuery] is an attribute introduced in Web API for OData V4. If you are still using Web API for OData V1-3, use [Queryable] still.
2.Then you can send the request
GET /odata/firmhierarchy(225)
and get the employees.
I was able to make ODATA work for a table, when auto-generated from entity framework. However, that generation process didn't want to work for a complex type returned by a Table Valued Function (similar scenario to a SP), because it didn't seem to understand where the key was.
What I found was that I could however make it work. First, I check out this article. He sets things up a bit more manually, where his Get on a companyProcessingController ends up routing for id 3 as "http://localhost:10020/odata/companyProcessing(3)" .
This surprised me. My other generated classes set up the pattern that SomeEntity became SomeEntityController, with methods like GetSomeEntities, and a routing that seemed to me to match the method but dropping the word get. Therefore, dropping the entity name from the Get method name seemed different, but it worked. Proving that the path is actually matching the controller name, not the method name.
In this Case you configure the routing using the data type you're querying for, and the beginning of the controller name. Then the actual path utilizes the beginning of the controller name as well.
And then all of this just brings us essentially to the other posted solution, assuming your controller name is firmhierarchyController
So, now, making sense of this... Try going to http://localhost:55063/odata/$metadata , where your port may differ. You'll notice that ODATA exposes a DataType, which is accessed via a DataSet. When a client tries to query into ODATA, they are trying to query against the DataSet, getting items of the DataType.
The DataSet matching the controller name (less Controller), and the Get methods can indeed just be Get without further extension of the name - and otherwise in this scenario was giving me problems.

How to get primary key field name for null EntityReference?

I've made the following function to assign value to a foreign key reference (I know, it's dirty and does not work for compound keys):
void setFk(dynamic tbl,object id){
var path=db.DefaultContainerName+"."+tbl.TargetRoleName;
var ret=new System.Data.EntityKey(path, tbl.RelationshipSet.ElementType.RelationshipEndMembers[0].GetEntityType().KeyMembers[0].Name, id);
tbl.EntityKey=ret;
}
which I want to invoke by this: setFk(newRec.WorkItemsReference,src.WorkItemsId)
The problem is that it does not work for newly created object newRec because tbl.EntityKey and tbl.RelationshipSet are both null.
One route that I see is to get tbl.GetType() and somehow locate Id column from there. If this is the right route then how ?
Any other route ?
My goal was not to pass more arguments into setFk function because it's supposed to be sufficient with 2 arguments and would lead to duplication of entity name.

MVC optional parameter for method

I want to create an optional parameter for an ActionResult method.
I have the following:
public ActionResult ViewReq (int id, string Req = null)
When I tried to do the following:
http://localhost/RepMedia/Controller1/ViewReq?id=34343?Req="34233"
I tried the following but got an error:
An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter. Parameter name: parameters
I am not sure what I am doing wrong.
The problem is with 'id'. The id must be part of the base URI:
http://localhost/RepMedia/Controller1/ViewReq/34343?Req=34233
http://localhost/RepMedia/Controller1/ViewReq?id=34343&Req=34233
Use a question sign before the first parameter - all others should be split by ampersand.
public ActionResult ViewReq (int? id, string Req)
http://localhost/RepMedia/Controller1/ViewReq?id=34343&Req=34233
You don't need make a string parameter optional, as they are reference types whose values will be null anyway if they aren't passed in by MVC. That URL would end up with a non-null "Id", but null "Req".

RavenDB Ids and ASP.NET MVC3 Routes

Just building a quick, simple site with MVC 3 RC2 and RavenDB to test some things out.
I've been able to make a bunch of projects, but I'm curious as to how Html.ActionLink() handles a raven DB ID.
My example: I have a Document called "reasons" (a reason for something, just text mostly), which has reason text and a list of links. I can add, remove, and do everything else fine via my repository.
Below is the part of my razor view that lists each reason in a bulleted list, with an Edit link as the first text:
#foreach(var Reason in ViewBag.ReasonsList)
{
<li>#Html.ActionLink("Edit", "Reasons", "Edit", new { id = Reason.Id }, null) #Reason.ReasonText</li>
<ul>
#foreach (var reasonlink in Reason.ReasonLinks)
{
<li>#reasonlink.URL</li>
}
</ul>
}
The Problem
This works fine, except for the edit link. While the values and code here appear to work directly (i.e the link is firing directly), RavenDB saves my document's ID as "reasons/1".
So, when the URL happens and it passes the ID, the resulting route is "http://localhost:4976/Reasons/Edit/reasons/2". So, the ID is appended correctly, but MVC is interpreting it as its own route.
Any suggestions on how I might be able to get around this? Do I need to create a special route to handle it or is there something else I can do?
I just downloaded the latest version of RavenDB and tried this out.
public class Entity {
public int Id { get;set; }
public string Text { get;set; }
}
When I saved it to RavenDB, the id in Raven was "entities/1", but the mapping in RavenDB client was able to successfully interpret the Id from what was in the database to the integer that I wanted.
var entity = session.Load<Entity>(1);
Assert.IsTrue(entity.Id == 1);
You do not need a extension method, and this way you would not need to alter any routes as mentioned above, because you will be dealing with good ol' integers. The string Ids were almost a deal breaker, but amazingly this works so RavenDB is back in the hunt.
Note: I figured this out from watching a Rob Ashton talk and realizing that all his document classes had integers as Ids.
I was having a similar issue and came up with my own solution before I found the link to Shiju Varghese blog post referenced by #jfar.
It might not be as clean and simple as the solutions provided in the blog post, but I do believe it can compete as a solution none the less. So here goes:
In a standard model class, when using RavenDB, we normally have an id property like so:
public string Id { get; set; }
What I did, was to add another id property like so:
public int? IdInt
{
get { return int.Parse(Id.Substring(Id.IndexOf("/") + 1)); }
}
This will give us the number part of the given RavenDB id.
I then had a class that looked something like this:
[Bind(Exclude = "IdInt")]
public class Item
{
public string Id { get; set; }
public int? IdInt
{
get { return int.Parse(Id.Substring(Id.IndexOf("/") + 1)); }
}
...
}
Note that I have excluded the IdInt property, as I don't want the model binder to handle it.
Furthermore, note, that the IdInt property is nullable. This is to avoid problems with the model binder later on, when we are creating new items and the Id property is null.
Then in the routes I declared a rule similar to this:
routes.MapRoute(
"WithParam", // Route name
"{controller}/{action}/{id}" // URL with parameters
);
Later on using an ActionLink or something similar we can do the following:
#Html.ActionLink("Details", "Details", new { id = item.IdInt })
And lastly when we press the rendered link, we are sent to the appropriate action which could look something like this:
public ActionResult Details(int id)
{
var item = _session.Load<Item>(id);
return View(item);
}
The reason why this will work is because of the Load method, which takes a ValueType as parameter and then automatically resolves the number to the correct RavenDB id.
What we have achieved with this approach is a url that looks something like this:
/items/details/1
As I said in the beginning, this solution is less elegant than the previous suggested solutions, however, this approach will give you a cleaner url as you can work with the id's as you normally would.
FYI, the link posted by #jfar was exactly the article. The following is the solution I used from the article:
Solution 2 - Modify ASP.NET MVC Route
Modify the ASP.NET MVC routes in the
Global.asax.cs file, as shown in the
following code:
routes.MapRoute(
"WithParam", // Route name
"{controller}/{action}/{*id}" // URL with parameters
);
We just put "*" in front of the id
variable that will be working with the
default Id separator of RavenDB

Resources