Question

I have a PUT API endpoint and I'd like to check that a record with the specified ID already exists before updating and storing.

Coming from an EntityFramework/SQLServer world, I found it odd that trying to update a record that doesn't exist would simply create a new document with the erroneous ID provided instead of throwing an exception.

Here is my code for my controller endpoint and my repo methods:

 public Models.Widget Put(Models.Widget entity)
    {
        using (_unitOfWork)
        {
            var originalWidget = _widgetRepository.Get(entity.Id);

            if(null == originalWidget)
             throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, string.Format("Widget with id of {0} could not be found.", entity.Id)));

            _widgetRepository.Store(entity);
            _unitOfWork.SaveChanges();
            return entity;
        }
    }

Repo methods:

 public Widget Get(long id)
    {
        var widget = Session.Load<Widget>("widgets/"+id);
        return widget;
    }

 public void Store(Widget model)
    {
        Session.Store(model);
    }

Assuming we make it past the null check in my controller, the error I get is the following:

Message: "An error has occurred."
ExceptionMessage: "Attempted to associate a different object with id 'Widgets/33'."

I know why I get the error, I end up "tracking" 2 entities with the same id.

I have two possible solutions in mind, would like your feedback.

  1. Find an efficient way to do a check for existence by ID that doesn't load the original for tracking.
  2. Fetch the original and "patch" them together with the incoming PUT DTO and Store it back in the db.

What do you guys think?

Was it helpful?

Solution

Raven's Store method is analogous to INSERT in SQL Server. There is no UPDATE, you just load the object from the database (which puts it under change tracking), change it, and then call SaveChanges.

It wouldn't be a good idea to tell Raven "Here's this object I got over HTTP, please update this document id with these contents" because then you would be giving up Raven's E-Tags that give you control over concurrency.

Depending on your situation, it may or may not be the best idea to send your Raven models directly to the client via WebAPI and then get them directly back. Whether or not you use the exact same classes or separate DTO type classes to communicate to the client, you could use AutoMapper to map the properties from the object coming in over WebAPI to the RavenDB model you load from the database.

OTHER TIPS

Try using optimistic concurrency. This way you would detect if you're writing a document with an id already in use:

session.Advanced.UseOptimisticConcurrency = true;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top