SaveChanges only works in debug - c#

I have a MVC controller method that looks like this:
[HttpPost]
public async Task<JsonResult> Edit(Foo input)
{
if (ModelState.IsValid)
{
Foo current = await db.foos.FindAsync(input.Id);
current.SomeProp = input.SomeProp; // etc.
db.Entry(current).State = EntityState.Modified;
await db.SaveChangesAsync();
return Json(true, JsonRequestBehavior.AllowGet);
}
return Json(false, JsonRequestBehavior.AllowGet);
}
This works when I'm debugging in Visual Studio. When I'm not debugging, I get a 500. What difference would debugging make here?
Note: I'm not using db.Entry(current).CurrentValues.SetValues(input); because there are some properties of Foo I don't want to change here.
Edit: I can't figure out how to get the exception details when not in debug mode. I tried writing it to a file. This works in debug mode (with throw new Exception("test"); taking the place of the statement that only works in debug mode) but I get a 401 when not debugging. The IIS user does have write permission on the directory.

The exception was a DbEntityValidationException indicating that a required field was missing. The missing field is a relation of Foo.
Based on Entity Framework Loading Related Entities, I believe my code loads Foo's relations lazily. I didn't even consider this possibility because I misunderstood comments like "EF doesn't support asynchronous lazy loading." Inspecting the object in debug mode seemed to confirm that its relations were being loaded eagerly. But now I realize that inspecting the object may have triggered lazy loading!

Related

WebApi Internal Server Error

I have 2 projects, a Front-End (AngularJS) and a Back-End (C# Web-Api). The thing is, when the front-end query the api (e.g GET localhost/api/Especialistas?rol=XXXX) I get a 500 error. Here is the code of the API:
public IHttpActionResult GetEspecialista(string rol)
{
Especialista especialista = db.Especialistas.First( e=> e.Rol == rol);
if (especialista == null)
{
return NotFound();
}
return Ok(especialista);
}
The API is working, since I reach the return Ok(especialista).
The Front-end is using this Restangular to query the API
Restangular.one("Especialistas?rol=XXXX").get()
The Network console shows a request 200 OK OPTION, but a 500 Internal Server Error GET.
I tried a message handler in the WebApiConfig.cs file to check if the GET request was reaching the Api, and is indeed reaching it, so I don't know what happened, since I didn't change any configuration file.
Any clue on how to fix this problem will be appreciated, thanks.
If your action is called successfully, but still receive a 500 error, I think the error is created by the serializing of especialista object when converted to a HTTP response.
Most probably, serialization fails because of some navigation properties which creat cycles in your object graph. It is recommended to return simple objects, not entity framework models.
Try the following:
var retObj = new { Prop1 = especialista.Prop1, Prop2 = especialista.Prop2 };
return Ok(retObj);
If above code works, I suggest creating service models "mirror" objects that should be populated based on your data models. These objects should be returned instead of data models.
A good helper to avoid the boilerplate code of property value assignments is Automapper.

How to use TryUpdateModel

I am readying this tutorial. I see from this tutorial that for update the author is using the following code:
....
var studentToUpdate = db.Students.Find(id);
if (TryUpdateModel(studentToUpdate, "",
new string[] { "LastName", "FirstMidName", "EnrollmentDate" }))
{
try
{
db.Entry(studentToUpdate).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
...
}
But I don't understand why the following line is needed:
db.Entry(studentToUpdate).State = EntityState.Modified;
When I remove this line, the code still works well and update is done perfectly.
Can someone help me with whether that line is needed? If so, why when I remove it, the update works well.
It works well because you find the studentToUpdate from your context, that's way the entity is attached and the changes that are made by the TryUpdateModel method are saved when you call the SaveChanges method.
If you were working with a detached entity, for example doing this:
var studentToUpdate=new Student(){Id=id};
if (TryUpdateModel(studentToUpdate, "",
new string[] { "LastName", "FirstMidName", "EnrollmentDate" }))
{
try
{
db.Entry(studentToUpdate).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
...
}
In this case you have to call the Entry method to attach the entity to your context and change its state.
That line is explicitly telling the EF context that the entity has been modified and needs to be updated the next time SaveChanges() is called. The reason everything still works when you remove that line is that the context usually tracks those changes automatically for you. I have yet to come across a situation where I have needed to fiddle with EF's automatic change tracking in production, it seems to work well.
See How change tracking works in Entity Framework for a bit more info.

Ensure that HttpConfiguration.EnsureInitialized()

I've installed Visual Studio 2013 and when I run my app I get the error below.
I've got no idea as to where I'm to initialized this object.
What to do?
Server Error in '/' Application.
The object has not yet been initialized. Ensure that HttpConfiguration.EnsureInitialized() is called in the application's startup code after all other initialization code.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: The object has not yet been initialized. Ensure that HttpConfiguration.EnsureInitialized() is called in the application's startup code after all other initialization code.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[InvalidOperationException: The object has not yet been initialized. Ensure that HttpConfiguration.EnsureInitialized() is called in the application's startup code after all other initialization code.]
System.Web.Http.Routing.RouteCollectionRoute.get_SubRoutes() +101
System.Web.Http.Routing.RouteCollectionRoute.GetRouteData(String virtualPathRoot, HttpRequestMessage request) +63
System.Web.Http.WebHost.Routing.HttpWebRoute.GetRouteData(HttpContextBase httpContext) +107
System.Web.Routing.RouteCollection.GetRouteData(HttpContextBase httpContext) +233
System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(HttpContextBase context) +60
System.Web.Routing.UrlRoutingModule.OnApplicationPostResolveRequestCache(Object sender, EventArgs e) +82
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +136
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +69
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.18408
This is for AlumCloud
If you do it at the end of Application_Start it will be too late, as WebApiConfig.Register has been called.
The best way to resolve this is to use new initialization method by replacing in Global.asax :
WebApiConfig.Register(GlobalConfiguration.Configuration);
by
GlobalConfiguration.Configure(WebApiConfig.Register);
See #gentiane's answer below for the correct way to handle this now.
At the end of the Application_Start method in Global.Asax.cs try adding:-
GlobalConfiguration.Configuration.EnsureInitialized();
I actually got this error when I was using Attribute Routing within my WebApi.
I had
[Route("webapi/siteTypes/{siteTypeId"]
instead of
[Route("webapi/siteTypes/{siteTypeId}"]
for my route and got this error. I had simply missed out the closing curly bracket. Once I added it back in, this error didn't occur again.
This is old, but is the first result on google when searching for this error. After quite a bit of digging I was able to figure out what was going on.
tldr:
All GlobalConfiguration.Configure does is invoke your action and call EnsureInitialized(). config.MapAttributeRoutes() must be called before EnsureInitialized() since EnsureInitialized only runs once.
Meaning: if you're coming from an existing Mvc project, all you have to do is:
Add
GlobalConfiguration.Configuration.EnsureInitialized();
to the bottom of your Application_Start method.
OR
Move your entire configuration into a single call to GlobalConfiguration.Configure:
GlobalConfiguration.Configure(config =>
{
WebApiConfig.Register(config);
config.MapAttributeRoutes();
...
});
Digging Deeper
HttpConfiguration.Configuration has an "Initializer" property defined like this:
public Action<HttpConfiguration> Initializer;
HttpConfiguration.EnsureInitialized() runs this action and sets _initialized to true
public void EnsureInitialized()
{
if (_initialized)
{
return;
}
_initialized = true;
Initializer(this);
}
HttpConfiguration.MapAttributeRoutes calls internal method AttributeRoutingMapper.MapAttributeRoutes which sets HttpConfiguration.Initializer
public static void MapAttributeRoutes(...)
{
RouteCollectionRoute aggregateRoute = new RouteCollectionRoute();
configuration.Routes.Add(AttributeRouteName, aggregateRoute);
...
Action<HttpConfiguration> previousInitializer = configuration.Initializer;
configuration.Initializer = config =>
{
previousInitializer(config);
...
};
}
GlobalConfiguration.Configure runs EnsureInitialized immediately after invoking your action:
public static void Configure(Action<HttpConfiguration> configurationCallback)
{
if (configurationCallback == null)
{
throw new ArgumentNullException("configurationCallback");
}
configurationCallback.Invoke(Configuration);
Configuration.EnsureInitialized();
}
Don't forget, if you run in to a wall, the source for asp.net is available at http://aspnetwebstack.codeplex.com/SourceControl/latest
I've had a related issue. Sometimes calling GlobalConfiguration.Configure multiple times triggers this error. As a workaround, I've put all configuration initialization logic in one place.
IF THIS ERROR SEEMS TO HAVE COME "OUT OF NOWHERE", i.e. your app was working perfectly fine for a while, ask yourself: Did I add an action to a controller or change any routes prior to seeing this error?
If the answer is yes (and it probably is), you likely made a mistake in the process. Incorrect formatting, copy/pasting an action and forgetting to make sure the endpoint names are unique, etc. will all end you up here. The suggestion that this error makes on how to resolve it can send you barking up the wrong tree.
For me, the problem was that I was trying to use named parameters for query string fields in my routes:
[Route("my-route?field={field}")]
public void MyRoute([FromUri] string field)
{
}
Query string fields are automatically mapped to parameters and aren't actually part of the route definition. This works:
[Route("my-route")]
public void MyRoute([FromUri] string field)
{
}
Although the above answer works if incase that is not set, In my case this stuff was set already. What was different was that, for one of the APIs I had written, I had prefixed the route with a / . Example
[Route("/api/abc/{client}")]
.Changing this to
[Route("api/abc/{client}")]
fixed it for me
Call
GlobalConfiguration.Configuration.MapHttpAttributeRoutes();
before
GlobalConfiguration.Configure(c => ...);
completes its execution.
I got this error when the version of Newtonsoft.Json was different in my main project compared to the helper project
One typically gets this exception when route templates in "Attribute Routing" are not proper.
For example, i got this when i wrote the following code:
[Route("{dirName:string}/contents")] //incorrect
public HttpResponseMessage GetDirContents(string dirName) { ... }
In route constraints syntax {parameter:constraint}, constraint by default is of type string. No need to mention it explicitly.
[Route("{dirName}/contents")] //correct
public HttpResponseMessage GetDirContents(string dirName) { ... }
I began getting this error one day. After I'd altered our app to call EnsureInitialized() I was able to see the root cause.
I had a custom attribute, a filter, on an action. That attribute class had had a breaking change made in the NuGet package in which it lives.
Even though I'd updated the the code and it all compiled, the local IIS worker was loading an old DLL and not finding a class member during initialization, reading attributes on actions etc.
For some reason (possibly due to order/when our logging is initialized), this error was not discoverable, possibly leaving the WebAPI in a strange state, until I'd added EnsureInitialized() which caught the exception and surfaced it.
Performing a proper bin and obj clean via a handy script resolved it.
In my case I created the webservice in project A and started it from Project B and I got exactly this error. The problem was that some .dll-files which are required by A where missing in the build-output-folder of B. Ensure that these .dll-files are available fixed it.
In my case , i used an Entity as parameter of my action that its 'Schema' is missing.
Wrong attribute:
[Table("Table name", Schema = "")]
Correct :
[Table("Table name", Schema = "schema name")]
In my case I fixed replacing:
<Route("{id:string}")>
with
<Route("{id}")>
Strange that I was sure that the original code was working before suddenly stopping, go figure....
I experienced this similar error.
<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>The object has not yet been initialized. Ensure that HttpConfiguration.EnsureInitialized() is called in the application's startup code after all other initialization code.</ExceptionMessage>
<ExceptionType>System.InvalidOperationException</ExceptionType>
<StackTrace> at System.Web.Http.Routing.RouteCollectionRoute.get_SubRoutes() at System.Web.Http.Routing.RouteCollectionRoute.GetRouteData(String virtualPathRoot, HttpRequestMessage request) at System.Web.Http.WebHost.Routing.HttpWebRoute.GetRouteData(HttpContextBase httpContext)</StackTrace>
</Error>
After fiddling with it for a while, I realized two problems: directory name collides with routing prefix and routing prefix must not end with '/'. When starting up the service for debugging I got this browser error after adding the SampleController.cs.
The folder structure
/api/Controllers/SampleController.cs
/api/Model/...
SampleController.cs
[RoutePrefix("api/sub/{parameterId}/more/")]
public class SampleController : ApiController
{
[HttpGet]
[Route("")]
public IHttpActionResult Get(int parameterId)
{
return Ok("Knock Knock...");
}
}
Changing the routing prefix to not end in '/' and not colliding with the directory name solved the problem.

Why is my ModelState null when running a unit test?

I have already read this and tried the solution and it did not work for me. I am trying to understand this and perhaps I am not testing this properly. I am getting a NullReferenceException on the line in the controller where I am testing ModelState. I am using MVC 3 with NUnit as my test suite. ShouldBeViewNamed("Index") is an extension method I wrote to test the Name of the view I am getting back, but I don't even get that far when executing the code. Here is my controller:
public class TestController : Controller
{
[HttpPost]
public ViewResult Index(TestModel model)
{
if(ModelState.IsValid) // results in a null reference exception.
return View();
return new ViewResult();
}
}
and the test class:
[TestFixture]
public class TestControllerTest
{
[Test]
public void TestingModelState()
{
// Arrange
var model = new TestModel();
var controller = new TestController();
// Arbitrary values to indicate an error in the model
controller.ModelState.AddModelError("tester1", "error happened");
// Act
var result = controller.Index(model);
// Assert
result.ShouldBeViewNamed("Index");
}
}
According to most blogs that I've read, this should "just work". But no matter what I do, the ModelState is always null... If this works for someone else, I'll assume it has to do with my local setup or perhaps a framework we use. I have had one other developer test this at my shop and he also gets the same exception.
EDIT: I have narrowed down the issue to being specific to this particular project. By adding a new project to the solution and testing with the same tests, I cannot duplicate this issue. Needless to say this is an inherited project that was started in 2009 likely as an MVC 1 project and has been upgraded to MVC 3. I will look at project level settings next.
EDIT: See the answer below, turns out a consultant we had hired a while back attempted to upgrade the project to MVC 4, ran into some issues and never reverted the files. Most of the unit tests still passed and the projects ran fine, but the NUnit project had a bad reference to MVC 4. I had to manually delete the reference from the .csproj file and readd it in VS and everything works.
Make sure your NUnit project is referencing the same version of System.Web.Mvc.dll as your MVC project.

ASP.NET MVC: Using GET and POST in the same method

Here's the scenario: when a new user registers to our web site, we want to send an email to verify that the user owns the email address. In the email there's a link to a page that will do the verification, something like this:
http://www.mysite.com/account/verify/token
The verify method looks like this:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Verify(Nullable<Guid> id)
{
// tries to get the user based on the verification code
if (ValidId(id))
{
// if the id is correct, update user data in the model and redirect
return RedirectToAction("Index", "Dashboard");
}
else
{
// redirects the user to the verify view
return View("Verify");
}
}
The "Verify" view is simply a textbox with a button, so the user can enter the verification code manually (the user can get to this page from the site, and might prefer just to copy-paste the code). When the user clicks on the button, I want to do the same thing that my GET method does; so I ended up with something like this:
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult Verify(Nullable<Guid> id) { ... }
I have a couple of issues with this code (which works, but...):
Is it OK to have a GET and POST method? Or is there a better way to handle this scenario?
I'm modifying data in a GET method (if the id is correct, I update the user data to reflect that it's verified) and that's a big NO NO ... still, I want the user to just be able to click on the link and verify the token. Is there a better way to achieve this?
Thanks
I personally wouldn't bother with the AcceptVerbs attribute. (** See note below) You could then combine this into one action, which could respond as needed. (Showing some untested code below.) The reason I'm adding an answer instead of just a comment is that I wanted to recommend you add one more branch to your logic, to handle a failed code (i.e., to present an error message).
public ActionResult Verify(Nullable<Guid> id)
{
if (!id.HasValue)
{
// nothing was submitted
ViewData["message"] = "Please enter your ID and press Submit";
return View("Verify");
}
if (!ValidId(id))
{
// something was submitted, but wasn't valid
ViewData["message"] = "ID is invalid or incomplete. Pleaes check your speeling";
return View("Verify");
}
// must be valid
return RedirectToAction("Index", "Dashboard");
}
You then of course could display <%=ViewData["message"]%> in your Verify view. This is of course just a simple example.
** OK, here is my note RE: not bothering with the AcceptVerbs attribute:
In your scenario you could also just choose to make your form's method GET instead of POST. Because you're already "taking action" and modifying state on the handy link your users click on, I wouldn't see any difference. I'm just mentioning this to be thorough even though I'd personally opt for my previous recommendation.
Good luck!
I'm modifying data in a GET method ... and that's a big NO NO
I wouldn't say it's always a big no no. HTTP says that the GET method "SHOULD" be "safe", that is it SHOULD have no effect other than information retrieval. In this case, I think it's reasonable to stretch the definition of "safe" to mean that it doesn't have any harmful side effects, and indeed the only possible side effect your verification link can have is a desirable one.
The other property that the GET method is supposed to have is idempotence: if the user clicks the verification link multiple times, it's the same as if they clicked it just once. Hopefully you have this property, since a verification link is generated with a single-use code.
I have to say it is rare to find somebody so concerned with using the proper HTTP verb. I don't believe the original intention of the HTTP spec was to confine all data editing submissions to POST and all retrievals to GET. I think what you're doing is just fine.
if you are worried about it then what is wrong with this?
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Verify()
{
return View("Verify");
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Verify(Guid? id)
{
if (ValidId(id))
{
return RedirectToAction("Index", "Dashboard");
}
return View("Verify");
}

Categories

Resources