How do you use optimistic concurrency with WebAPI OData controller - asp.net

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

Related

How can I hide System.Exception errors on .NET Core?

I try to improve myself with .NET Web API now and I am trying to return a custom error in Swagger. But when returning this custom error, I can see the error is on which line. How can I do to prevent this?
public async Task<BookCreateDTO> CreateBook(BookCreateDTO bookCreateDto)
{
if (await _context.Books.AnyAsync(x => x.Name == bookCreateDto.Name))
{
throw new BookExistException("Book already exist");
}
var book= _mapper.Map<Book>(bookCreateDto);
_context.Books.Add(book);
await _context.SaveChangesAsync();
return book;
}
What should I do to see only this exception message in the Swagger response?
Thank you for your help.
Exceptions should be exceptional: Don't throw exceptions for non-exceptional errors.
I don't recommend specifying your web-service's response DTO type in the C# action method return type because it limits your expressiveness (as you're discovering).
Instead use IActionResult or ActionResult<T> to document the default (i.e. HTTP 2xx) response type and then list error DTO types in [ProducesResponseType] attributes with their corresponding HTTP status codes.
This also means that each response status code should only be associated with a single DTO type.
While Swagger is not expressive enough to allow you to say "if the response status is HTTP 200 then the response body/DTO is one-of DtoFoo, DtoBar, DtoQux", in-practice a well-designed web-service API should not exhibit that kind of response DTO polymorphism.
And if it didn't, how else is a client supposed to know what the type is just from the HTTP headers? (Well, you could put the full DTO type-name in a custom HTTP response header, but that introduces other problems...)
For error conditions, add the errors to ModelState (with the Key, if possible) and let ASP.NET Core handle the rest for you with ProblemDetails.
If you do throw an exception, then ASP.NET Core can be configured to automatically render it as a ProblemDetails - or it can show the DeveloperExceptionPage - or something else entirely.
I note that a good reason to not throw an exception inside a Controller for non-exceptional exceptions is because your logging framework may choose to log more details about unhandled exceptions in ASP.NET Core's pipeline, which would result in useless extraneous entries in your logs that make it harder to find "real" exceptions that you need to fix.
Document the DTOs used, and their corresponding HTTP status codes, with [ProducesResponseType]: this is very useful when using Swagger/NSwag to generate online documentation and client libraries.
Also: do not use EF entity types as DTOs or ViewModels.
Reason 1: When the response (with EF entity objects) is serialized, entities with lazy-loaded properties will cause your entire database object-graph to be serialized (because the JSON serializer will traverse every property of every object).
Reason 2: Security! If you directly accept an EF entity as an input request body DTO or HTML form model then users/visitors can set properties arbitrarily, e.g. POST /users with { accessLevel: 'superAdmin' }, for example. While you can exclude or restrict which properties of an object can be set by a request it just adds to your project's maintenance workload (as it's another non-local, manually-written, list or definition in your program you need to ensure is kept in-sync with everything else.
Reason 3: Self-documenting intent: an entity-type is for in-proc state, not as a communications contract.
Reason 4: the members of an entity-type are never exactly what you'll want to expose in a DTO.
For example, your User entity will have a Byte[] PasswordHash and Byte[] PasswordSalt properties (I hope...), and obviously those two properties must never be exposed; but in a User DTO for editing a user you might want different members, like NewPassword and ConfirmPassword - which don't map to DB columns at all.
Reason 5: On a related note to Reason 4, using Entity classes as DTOs automatically binds the exact design of your web-service API to your database model.
Supposing that one day you absolutely need to make changes to your database design: perhaps someone told you the business requirements changed; that's normal and happens all the time.
Supposing the DB design change was from allowing only 1 address per customer (because the street addresses were being stored in the same table as customers) to allowing customers to have many addresses (i.e. the street-address columns are moved to a different table)...
...so you make the DB changes, run the migration script, and deploy to production - but suddenly all of your web-service clients stop working because they all assumed your Customer object had inline Street address fields but now they're missing (because your Customer EF entity types' don't have street-address columns anymore, that's over in the CustomerAddress entity class).
If you had been using a dedicated DTO type specifically for Customer objects then during the process of updating the design of the application you would have noticed builds breaking sooner (rather than inevitably later!) due to C# compile-time type-checking in your DTO-to-Entity (and Entity-to-DTO) mapping code - that's a benefit right there.
But the main benefit is that it allows you to completely abstract-away your underlying database design - and so, in our example, if you have remote clients that depend on Customer address information being inline then your Customer DTO can still emulate the older design by inlining the first Customer Address into the original Customer DTO when it renders its JSON/XML/Protobuf response to the remote client. That saves time, trouble, effort, money, stress, complaints, firings, unnecessary beatings, grievous bodily harm and a scheduled dental hygienist's appointment.
Anyway, I've modified your posted code to follow the guidance above:
I added [ProducesResponseType] attributes.
I appreciate it is redundant to specify the default response type BookCreateDTO twice (in [ProducesResponseType] as well as ActionResult<BookCreateDTO> - you should be able to remove either one of those without affecting Swagger output.
I added an explicit [FromBody], just to be safe.
If the "book-name is unused" check fails, it returns the model validation message in ASP.NET's stock BadRequest response, which is rendered as an IETF RFC 7807 response, aka ProblemDetails instead of throwing an exception and then hoping that you configured your ASP.NET Core pipeline (in Configure()) to handle it as a ProblemDetails instead of, say, invoking a debugger or using DeveloperExceptionPage.
Note that in the case of a name conflict we want to return HTTP 409 Conflict and not HTTP 400 Bad Request, so the conflictResult.StatusCode = 409; is overwritten.
The final response is generated from a new BookCreateDTO instance via AutoMapper and Ok() instead of serializing your Book entity object.
[ProducesResponseType(typeof(BookCreateDTO), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status409Conflict)]
public async Task< ActionResult<BookCreateDTO> > CreateBook( [FromBody] BookCreateDTO bookCreateDto )
{
// Does a book with the same name exist? If so, then return HTTP 409 Conflict.
if( await _context.Books.AnyAsync(x => x.Name == bookCreateDto.Name) )
{
this.ModelState.Add( nameof(BookCreateDTO.Name), "Book already exists" );
BadRequestObjectResult conflictResult = this.BadRequest( this.ModelState );
// `BadRequestObjectResult` is HTTP 400 by default, change it to HTTP 409:
conflictResult.StatusCode = 409;
return conflictResult;
}
Book addedBook;
{
addedBook = this.mapper.Map<Book>( bookCreateDto );
_ = this.context.Books.Add( book );
_ = await this.context.SaveChangesAsync();
}
BookCreateDTO responseDto = this.mapper.Map<BookCreateDTO >( addedBook );
return this.Ok( responseDto );
}

what is db.SaveChanges() in asp mvc4

I searched on many site about savechanges but i dont get any proper
answer can any one tell me about
db.SaveChanges()
ModelState
db.SaveChanges() is not part of ASP.NET MVC - it's part of Entity Framework which is a set of Object-Relational-Mapping (ORM) tools for the .NET Framework. All this method does is persist (save) data in some of your classes (entities) into a database.
Useful links:
Scott Gu - Code-First Development with Entity Framework 4
MSDN - Entity Framework
ModelState is a part of MVC and allows extra binding metadata to be passed from the Controller to the View, which is typically largely about validation.
Useful links:
MSDN - Model State Class
MSDN - Performing Simple Validation
msdn says : It persists all updates to the data source and resets change tracking in the object context.
Example
To save the changes made to the entities to a database, we need to call the ObjectContext class SaveChanges method. In the example given below, the query retrieves the first customer from the entityset -Customer.
var customer = context.Customer.First();
The context.Customer returns the Objectset of Customer types and the LINQ extension method First() returns only the first customer.
customer.FirstName = "Yasser";
customer.LastName = "Shaikh";
context.SaveChanges();
We can edit the first customer details like name and address by assigning new values to the properties and call the SaveChanges() method to save the changes back to the database.
During SaveChanges, the ObjectContext determines which fields were changed. In this example, only FirstName and LastName are changed. So, only those two values are sent into the command. To identify the row to be updated in the database, the ObjectContext uses the value of the EntityKey property.
Please read:
http://msdn.microsoft.com/en-us/library/bb336792(v=vs.110).aspx
http://www.asp.net/mvc/tutorials/older-versions/models-(data)/performing-simple-validation-cs
This should help you a little. Basically the db.SaveChanges() method is used by Entity Framework to save current changes to the database and ModelState represents validation errors when e.g. the model is not valid.

Saving changes with Entity Framework causes detached entity duplicate

I'm making a survey program that has a list of questions that are pulled from the database as entities. During the course of the survey the properties of the entity may be edited, but I do not want these changes persisted to the database. So, I detach the questions after they are loaded into the dbcontext and access them via a dictionary.
The problem arises when I attempt to save an answer to the database. Since the answer entity is related to the question entity (that has been detached) a completely new question entity is then added to the database. I'm not sure why this is occurring as the answer entity being added contains the proper ID of the detached entity (when I look at it in the debugger). However, upon saving changes it gets updated to the ID of the newly created duplicate entity.
Is there a way to stop this behavior? Or alternatively, is there a way to make changes to an entity and not have them persist to the database even when SaveChanges is called on the context for writing a answer or a log entry to the database?
Here is the code that loads, then detatches the questions (items):
public FixedLengthBlockElementRuntime(FixedLengthBlock block, RuntimeContext context) : base(block, context)
{
this._items = ((FixedLengthBlock)this.Element).ItemBank.Items.OfType<FixedLengthItem>().ToDictionary(x => x.ItemName, x => x);
foreach(FixedLengthItem fixedLengthItem in this._items.Values)
{
this.Context.DetachEntity(fixedLengthItem);
}
}
And here is the code that adds the duplicate entry:
public void SetResponse(Response response)
{
response.Session = this.Session;
this.DbContext.Responses.AddObject(response);
this.DbContext.SaveChanges();
}
Note that Response has the proper ItemId until the point of Saving Changes.
You can set the MergeOption on your questions DbSet to be OverwriteChanges.
Context.Questions.MergeOption = MergeOption.OverwriteChanges;
You'll need to be sure you set that BEFORE you make the query for the questions for this to work properly however.
More reading on how MergeOptions work

The ObjectContext instance has been disposed and can no longer be used for operations that require a connection

I'm trying to query UserMetaData for a single record using the following query
using (JonTestDataEntities context = new JonTestDataEntities())
{
return context.UserMetaData.Single(user => user.User.ID == id);
}
The error I'm receiving is: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection. It is trying to lazyload Group for UserMetaData record. How can I change my query to prevent this error?
As the message says, you cannot lazily load it after the function returns, because you've already disposed the context. If you want to be able to access Group, you can make sure you fetch it earlier. The extension method .Include(entity => entity.NavigationProperty) is how you can express this:
using (JonTestDataEntities context = new JonTestDataEntities())
{
return context.UserMetaData.Include(user => user.Group).Single(user => user.User.ID == id);
}
Also consider adding .AsNoTracking(), since your context will be gone anyway.
You need to create a strong type that matches the signature of your result set. Entity Framework is creating an anonymous type and the anonymous type is disposed after the using statement goes out of scope.
So assigning to a strong type avoids the issue altogether. I'd recommend creating a class called UserDTO since you're really creating a data transfer object in this case. The benefit of the dedicated DTO is you can include only the necessary properties to keep your response as lightweight as possible.

ASP.NET MVC: How to view query executed by SaveChanges (on the ADO.NET Entity Data Model)

When trying to add a few items to the database I'm getting this error:
UpdateException was unhandled by user code
An error occurred while updating the entries. See the InnerException for details.
The InnerException contains this:
{"Column count doesn't match value count at row 1"}
I can't see anything wrong with the objects I'm trying to add, all the required values are filled.
Is there any way of viewing the query that causes the problem?
The method's code, if required:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LaadVerrichtingenIn() {
int[] intArray = Array.ConvertAll<String, int>(Request.Form["selectedObjects"].Split(','), new Converter<String, int>(Convert.ToInt32));
List<Verrichting> gekozenVerrichtingen = new List<Verrichting>();
foreach(int i in intArray){
base._entities.AddToVerrichtingSet(((Dictionary<int, Verrichting>)Session["ingelezenVerrichtingen"])[i]);
gekozenVerrichtingen.Add(((Dictionary<int, Verrichting>)Session["ingelezenVerrichtingen"])[i]);
}
Session["ingelezenVerrichtingen"] = null;
base._entities.SaveChanges(); //Exception occurs here
return View("IngeladenVerrichtingen");
}
base._entities is an ADO.NET Entity Data Model.
Thanks
I'm not sure if there's a 'neater' way to do this with the Entity Framework, but if you're using SQL Server then I'd generally use the SQL Server Profiler to read the queries being executed against the server. If you're using a different database then there may be an equivalent - in any case it would probably be helpful if you let us know.
If you're using MySQL > 5.0.37 it has new query profiler functionality - this should be able to show you the queries being sent.
SQL server profiler will work fine if you're using SQL Server. Within the Entity Framework, you can use the ToTraceString method.
I've just come across the same problem while inserting data using the Entity Framework and MySQL. My hunch is, since I'm using double values, that the decimal separator "," is being misinterpreted as a field separator. I upgraded to Connector version 6.1.0, but still no luck. Maybe this is also going on in your case.
Check out this bug report.
BTW, I found that the following line of code works around the problem:
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");

Resources