I'm using the setup described in this SO answer: Unobtrusive AOP with Spring.Net and source based on q9114762_aop_on_mvc_controllers on GitHub to get AOP logging for my controllers.
All is great except when a controller method with optional parameters is invoked without the optional parameter.
My test methods:
using System.Web.Mvc;
public class OrderController : Controller
{
[SetMethodInfoAsMessage]
virtual public ActionResult Test1()
{
return null;
}
[SetMethodInfoAsMessage]
virtual public ActionResult Test2(int someParam = 0)
{
return null;
}
[SetMethodInfoAsMessage]
virtual public ActionResult Test3(int? someParam = 0)
{
return null;
}
[SetMethodInfoAsMessage]
virtual public ActionResult Test4(int someParam)
{
return null;
}
}
and here's the corresponding behaviour when GETting these methods from a browser:
http://localhost:62376/Order/Test1 - 200 OK
http://localhost:62376/Order/Test2 - 500 Internal Server Error. Server Error in '/' Application. The parameters dictionary contains an invalid entry for parameter 'someParam' for method 'System.Web.Mvc.ActionResult Test2(Int32)' in 'InheritanceAopProxy_7b93ae81d25d46529bebc7ed00ebc409'. The dictionary contains a value of type 'System.Reflection.Missing', but the parameter requires a value of type 'System.Int32'. Parameter name: parameters
http://localhost:62376/Order/Test2?someParam=15 - 200 OK
http://localhost:62376/Order/Test3 - 500 Internal Server Error. Server Error in '/' Application. The parameters dictionary contains an invalid entry for parameter 'someParam' for method 'System.Web.Mvc.ActionResult Test3(System.Nullable1[System.Int32])' in 'InheritanceAopProxy_7b93ae81d25d46529bebc7ed00ebc409'. The dictionary contains a value of type 'System.Reflection.Missing', but the parameter requires a value of type 'System.Nullable1[System.Int32]'. Parameter name: parameters
http://localhost:62376/Order/Test3?someParam=15 - 200 OK
http://localhost:62376/Order/Test4 - 500 Internal Server Error. Server Error in '/' Application. The parameters dictionary contains a null entry for parameter 'someParam' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Test4(Int32)' in 'InheritanceAopProxy_7b93ae81d25d46529bebc7ed00ebc409'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter. Parameter name: parameters
The last test (6.) makes sense since someParam is mandatory. But we can see from 2. and 4. that not supplying an optional parameter results in a 500. Another thing to note is the error text from 4. - An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter. So according to this, 4. should work, right? (since Test3's optional parameter is nullable).
Has anyone else experienced this? Anyone have any suggestions as to a workaround (other than manually adding logging statements to the methods) ?
This is using Spring.Aop 1.3.2, Spring.Core 1.3.2, Spring.Web. 1.3.2 and Spring.Web.Mvc3 1.3.2.
EDIT: as requested, here's the advice. It simply logs out args except for passwords (we don't want those logged):
public class SetMethodInfoAsMessageAdvice : IMethodBeforeAdvice //, IThrowsAdvice
{
private static readonly PELogger log = Log4NetHelper.GetLogger(typeof(SetMethodInfoAsMessageAdvice));
public void Before(MethodInfo m, object[] args, object target)
{
ILog logger = LogManager.GetLogger(m.DeclaringType);
string dump = args.ToJson();
if (dump.Contains("password", StringComparison.OrdinalIgnoreCase))
dump = "<password hidden>";
logger.Info(m.Name + "(" + dump + ")");
}
}
Related
I need to select data from database depending on Session value , but I'm getting this error always
'operator '==' cannot be applied to operands of type 'int?' and 'object' MVC '
I tried the following code in orders controller and orders view:
public ActionResult ordersCash()
{
return View(db.Lab_orders_Cash.Where(x => x.patient_no == Session["UserpatientNo"]).ToList());
}
I tried the solutions in this site by using (int) but its not working :
public ActionResult ordersCash()
{
return View(db.Lab_orders_Cash.Where(x => x.patient_no == (int)Session["UserpatientNo"]).ToList());
}
When i used (int)Session["UserpatientNo"]) i got this error :
LINQ to Entities does not recognize the method 'System.Object get_Item(System.String)' method, and this method cannot be translated into a store expression.
Without where clause i can get all data but i need to filter and search by session value.
How can i solve this error?
Pull the session variable into a variable:
public ActionResult ordersCash()
{
int userPatientNum = (int)Session["UserpatientNo"];
return View(db.Lab_orders_Cash.Where(x => x.patient_no == userPatientNum).ToList());
}
I have an action method already written in my web api 2.0 project. I would like to add a new parameter without disturbing the existing contract. What is the best way to do that? Appreciate any best practice hints on this :)
Here's the code sample of what I intend to do:
Existing code:
[Route("{myId}",Name="MyId")]
Public IHttpActionResult Get(String myId)
{
//Some more code here
}
Url: http://localhost:8888/webapi/1111
Expecting to do something like the below:
//I want to keep the route name same for backwards compatibility.
[Route("{myId}/{myName}",Name="MyId")]
Public IHttpActionResult Get(String myId,string? myName)
{
//Some more code here
}
Url: http://localhost:8888/webapi/1111/John
The Url mentioned above hits the method rightly, but I never get the second parameter (myName) populated with John.
Thanks everyone for any help towards this.
Sree.
In your example you have myName as string? which is not allowed as:
The type 'string' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable'
A test controller was created to implement you action
[RoutePrefix("webapi")]
public class TestsController : ApiController {
[HttpGet]
[Route("{myId}/{myName}", Name = "MyId")]
public IHttpActionResult Get(string myId, string myName) {
//Some code to show the values of the parameters
return Ok(new { myId = myId, myName = myName });
}
}
When tested with webapi/1111/John the following response is returned
{"myId":"1111","myName":"John"}
which does include the value for MyName as John
If backwards uri webapi/1111 is tried, a NotFound response is returned as the template does not match the new action.
To fix this you need to make the myName parameter optional. To learn more about that check
Optional URI Parameters and Default Values
The new route will look like
//NOTICE THE `?` ON THE {myName} TEMPLATE
[Route("{myId}/{myName?}", Name = "MyId")]
public IHttpActionResult Get(string myId, string myName = null) {...}
You will notice that myName was made optional in the route {myId}/{myName?} and in the action parameter (string myId, string myName = null)
Now when tested with webapi/1111 the following response is returned
{"myId":"1111","myName":null}
Which would match your expected result for backwards compatibility.
String is a reference type so you don't need to make it nullable, it already is. Remove the '?' and remove the Name from the attribute. What happens then?
This is my Action method:
public ActionResult Index(int SelectedID, int mode)
{
ViewModel viewModel = new ViewModel();
viewModel.SelectedID = SelectedID;
viewModel.Mode = mode;
return View(viewModel);
}
This is how I call it:
localservername/DManager/DManager/Index?SelectedID=9306270318&Mode=DataManager
And I am getting the following error:
The parameters dictionary contains a null entry for parameter 'SelectedID' of non-nullable type 'System.Int32' for method
'System.Web.Mvc.ActionResult Index(Int32, Int32)' in
'MscanES.Web.Areas.DManager.Controllers.DManagerController'. An
optional parameter must be a reference type, a nullable type, or be
declared as an optional parameter.
Parameter name: parameters
Clueless..
C# int (Int32) range is -2,147,483,648 to 2,147,483,647. Your number 9,306,270,318 is obviously over the range, use Int64 instead.
I am able to successfully call Backbone's HTTP POST and PUT methods and have them link up to my server using Asp.Net MVC.
The problem is that when I call the HTTP DELETE using model.destroy() I get this error...
The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Delete(Int32)' in 'GSASF.Controllers.AdminController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
Right before I call model.destroy() I logged the id to the console and it was correct. This is my code. *Note that my model doesn't have a field called id but instead a field called HoverId. The database table already existed so I have to make due.
Delete: function(id) {
if (id) {
for (var i = this.collection.length - 1; i >= 0; i--) {
var item = this.collection.at(i);
if (item.get("HoverId") == id)
alert("Item to be destroyed ID: " + item.get("id"));
item.destroy();
};
}
[ActionName("SpaceToolTipEdit")]
[HttpDelete]
public ActionResult Delete(int id)
{
var imageHover = sr.GetImageHoverById(id);
if (imageHover != null)
{
sr.DeleteImageHover(imageHover);
return new HttpStatusCodeResult(200);
}
return new EmptyResult();
}
Ok, I feel dumb now.
The solution was all to simple. I had originally set the url attribute to my /{Controller}/{SpaceToolTipEdit} url. I was supposed to set this to the urlRoot attribute instead.
I'm trying to bind a service and specifying a constructor argument using Ninject in our application. The constructor argument is a value that can be pulled from the query string or a cookie. The code we currently have is something like this
kernel.Bind<SomeService>()
.ToSelf()
.InRequestScope()
.WithConstructorArgument("someID", ctx =>
// Try to get it from the posted form values
System.Web.HttpContext.Current.Request.Form["someID"] != null ?
long.Parse(System.Web.HttpContext.Current.Request.Form["someID"]) :
// Try to get it from the query string
System.Web.HttpContext.Current.Request.QueryString["someID"] != null ?
long.Parse(System.Web.HttpContext.Current.Request.QueryString["someID"])
: 0);
This works but is pretty ugly. I realize there are other ways of accomplishing this such as passing in the Form value or the QueryString value as a parameter, but we like having it defined in the Binding. What we would ideally like to do is something like this:
kernel.Bind<SomeService>()
.ToSelf()
.InRequestScope()
.WithConstructorArgument("someID", ctx => GetSomeID());
From what I can tell, this is not possible. Is there another way to break out the constructor argument injection logic into another method so we don't have to nested one line if statements?
I'd suggest binding the dependency on the Query String / HTTP form via an interface. This approach seems more in line with the dependency injection pattern (de-coupling code from specific implementations and classes).
public interface IParameters
{
string SomeID { get; }
}
public class ParametersFromHttpContext
{
IQueryString _queryString;
IRequestForm _requestForm;
public ParametersFromHttpContext(IQueryString queryString, IRequestForm requestForm)
{
_queryString = queryString;
_requestForm = requestForm;
}
public string SomeID
{
get
{
return
// Try to get it from the posted form values
_requestForm["someID"] != null ?
long.Parse(_requestForm["someID"]) :
// Try to get it from the query string
_queryString["someID"] != null ?
long.Parse(_queryString["someID"])
: 0;
}
}
}
Now logic you want can be contained in the binding, without the need to reference HttpContext in the kernel.
kernel.Bind<IParameters>().To<ParametersFromHttpContext>();