I'm building one ASP.NET Core Web API and I've recently found one issue regarding the binding of DateTime values.
In truth I have one minimumDate and one maximumDate properties for filtering in a certain resource. These are part of one Filtering object which just gets populated on the controller by model binding.
The issue is that the request is sent like this:
minimumDate=2014-01-20T00:00:00.000Z&maximumDate=2014-03-21T00:00:00.000Z
and on the controller one gets when debuging:
MinimumDate = 19/01/2014 22:00:00
MaximumDate = 20/03/2014 21:00:00
This is clearly wrong. The expected was:
MinimumDate = 20/01/2014 00:00:00
MaximumDate = 21/03/2014 00:00:00
It is reducing one day in both the minimum and maximum dates and furthermore it is messing the time part.
I thought at first it had to do with culture and globalization, but this is already set in the Startup configure method as:
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("pt-BR");
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("pt-BR");
so I doubt this to be the reason.
What am I doing wrong? How to get dates properly being sent to the API with model binding?
EDIT I managed to solve the issue by manualy parsing the datetime objects using:
filtering.MinimumDate = DateTime.Parse(this.Request.Query["minimumDate"], null, System.Globalization.DateTimeStyles.RoundtripKind);
filtering.MaximumDate = DateTime.Parse(this.Request.Query["maximumDate"], null, System.Globalization.DateTimeStyles.RoundtripKind);
In other words, bypassing the model binder. Still, I want to know: why model binding is presenting this strange behavior here?
To me it looks like the model binder which uses Json.net behind the scenes is converting your UTC time to local time for BRT (UTC-3) which is why you see the date and time change. You should be able to update your JsonSerializerSettings property as:
new JsonSerializerSettings
{
.....
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
.....
}
That should take care of proper model binding in your case.
Related
I've got a WebAPI OData controller which is using the Delta to do partial updates of my entity.
In my entity framework model I've got a Version field. This is a rowversion in the SQL Server database and is mapped to a byte array in Entity Framework with its concurrency mode set to Fixed (it's using database first).
I'm using fiddler to send back a partial update using a stale value for the Version field. I load the current record from my context and then I patch my changed fields over the top which changes the values in the Version column without throwing an error and then when I save changes on my context everything is saved without error. Obviously this is expected, the entity which is being saved has not been detacched from the context so how can I implement optimistic concurrency with a Delta.
I'm using the very latest versions of everything (or was just before christmas) so Entity Framework 6.0.1 and OData 5.6.0
public IHttpActionResult Put([FromODataUri]int key, [FromBody]Delta<Job> delta)
{
using (var tran = new TransactionScope())
{
Job j = this._context.Jobs.SingleOrDefault(x => x.JobId == key);
delta.Patch(j);
this._context.SaveChanges();
tran.Complete();
return Ok(j);
}
}
Thanks
I've just come across this too using Entity Framework 6 and Web API 2 OData controllers.
The EF DbContext seems to use the original value of the timestamp obtained when the entity was loaded at the start of the PUT/PATCH methods for the concurrency check when the subsequent update takes place.
Updating the current value of the timestamp to a value different to that in the database before saving changes does not result in a concurrency error.
I've found you can "fix" this behaviour by forcing the original value of the timestamp to be that of the current in the context.
For example, you can do this by overriding SaveChanges on the context, e.g.:
public partial class DataContext
{
public override int SaveChanges()
{
foreach (DbEntityEntry<Job> entry in ChangeTracker.Entries<Job>().Where(u => u.State == EntityState.Modified))
entry.Property("Timestamp").OriginalValue = entry.Property("Timestamp").CurrentValue;
return base.SaveChanges();
}
}
(Assuming the concurrency column is named "Timestamp" and the concurrency mode for this column is set to "Fixed" in the EDMX)
A further improvement to this would be to write and apply a custom interface to all your models requiring this fix and just replace "Job" with the interface in the code above.
Feedback from Rowan in the Entity Framework Team (4th August 2015):
This is by design. In some cases it is perfectly valid to update a
concurrency token, in which case we need the current value to hold the
value it should be set to and the original value to contain the value
we should check against. For example, you could configure
Person.LastName as a concurrency token. This is one of the downsides
of the "query and update" pattern being used in this action.
The logic
you added to set the correct original value is the right approach to
use in this scenario.
When you're posting the data to server, you need to send RowVersion field as well. If you're testing it with fiddler, get the latest RowVersion value from your database and add the value to your Request Body.
Should be something like;
RowVersion: "AAAAAAAAB9E="
If it's a web page, while you're loading the data from the server, again get RowVersion field from server, keep it in a hidden field and send it back to server along with the other changes.
Basically, when you call PATCH method, RowField needs to be in your patch object.
Then update your code like this;
Job j = this._context.Jobs.SingleOrDefault(x => x.JobId == key);
// Concurrency check
if (!j.RowVersion.SequenceEqual(patch.GetEntity().RowVersion))
{
return Conflict();
}
this._context.Entry(entity).State = EntityState.Modified; // Probably you need this line as well?
this._context.SaveChanges();
Simple, the way you always do it with Entity Framework: you add a Timestamp field and put that field's Concurrency Mode to Fixed. That makes sure EF knows this timestamp field is not part of any queries but is used to determine versioning.
See also http://blogs.msdn.com/b/alexj/archive/2009/05/20/tip-19-how-to-use-optimistic-concurrency-in-the-entity-framework.aspx
I want to store a simple variable in the session, to be able to get its value from different controllers.
In my controller I've tried to save variable this way:
HttpContext.Session.Add("CurrentPage",index.ToString())
And this way:
Session["CurrentPage"] = index.ToString();
But when I'm trying to read from session:
var currPage = Session["CurrentPage"] I am getting nothing. Variable is not initialized and isn't displayed in Locals debug window.
What am I doing wrong?
For now I've decided to use cookies for that task, but anyway I am interested in possible solutions. May be someone else will have same problem and will search the solution.
If u want to pass a value from one method to another in a controller let try this
TempData["Data"] = index.ToString();
and to retrieve the data back
use
var message = TempData["Data"];
I hope the question is easy, but I am searching around for more than an hour and I just do not find the answer. So I have a asp.net mvc 2 application and one of the forms contains a date textbox with a jquery datepicker. I have set in the datepicker the date format to be dd.mm.yyyy. My problem is that on the model binding this date is interpreted as mm.dd.yyyy, so by the time the model reaches my action method the DateTime attribute of the model is not correct (day and month is reversed, if it is not possible to reverse it client side validation does not let me save the item). I do not want to change culture, but I would like to specify somehow to the model binder how to interpret the date. Is it possible somehow?
Thanks a lot
This blog post explains how you can specify a particular date format to be used by the MVC model binder.
You will need to create a custom model binder implementation that is aware of the date format, then associate that with the DateTime type in your Global.asax.cs:
protected void Application_Start()
{
//...
var binder = new DateTimeModelBinder(GetCustomDateFormat());
ModelBinders.Binders.Add(typeof(DateTime), binder);
}
You might find the answer to your question here
Custom DateTime model binder in Asp.net MVC
Also Scott Hanselman has talked about a DateTime custom model binder here
http://www.hanselman.com/blog/SplittingDateTimeUnitTestingASPNETMVCCustomModelBinders.aspx
Although he's using the binder to split the date & time you might his post useful when creating your custom model binder.
I am using xVal with NHibernate Validator and I have a hard time to validate the dates.
First, NHibernate does not have validation for Date/DateTime formatting (except Past and Future). Second, I tried xVal itself (not using NHibernate Validator) but still no chance.
I need to validate the date values (let's say in a text box), to make sure it's a valid date. For instance, 13/01/2010 or 11/31/2010 are not valid dates.
I have tried creating new rules for NHibernate Validator by extending a new class, but it needs to be declared in the xVal client side too. I don't like to overwrite the existing scripts, if possible. I also used xval's [DataType(DataType.Date)] but it doesn't check if the date is valid!
Any suggestions?
After spending some time on this issue, here is the answer to my question: Custom Validation in xVal
I've got an MVC app that I've set the globalization in the web.config. All is well in the web app. But in my tests project I'm getting an issue in my service layer. I'm asking for date of birth in the following format dd/MM/yyyy. I'm passing this as a string to my service layer. I've got a RegEx to check that it is formatted correctly but when it is and I try to convert it to a date I'm getting an error. This is because the CultureInfo is set to en.US, I want it to be en.GB. I've tried in one of my initialise test methods to do the following, to no avail:
string sCulture = ConfigurationSettings.AppSettings["CultureToUse"]; //returns "en.GB"
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(sCulture);
CultureInfo.CreateSpecificCulture(sCulture);
Any ideas how to set CultureInfo in my tests project?
Since you enforce the format that the data is in and it only is numeric, you shouldn't rely on a user defined setting and are better off using DateTime.ParseExact(dateString, "dd/MM/yyyy", CultureInfo.InvariantCulture).
The CurrentUICulture property controls the resources that get loaded for the app. CurrentCulture is what you want to set/get to control parsing/formatting.
ConfigurationSettings.AppSettings["CultureToUse"]
Must return "en-GB" not "en.GB", hope this helps!