Jim
Jim

Reputation: 16012

storing objects with raven db exception on SaveChanges for key generation

I get the following error when storing my object(s) in RavenDb
I am only assigning one key to the stored object, I am using the overloaded store method to store the entity.
Store is only called once so why would an exception like this be thrown. There is something about object identity I don't understand.

The Id property has another purpose besides ravendb persistence.

Entity UserQuery+MyItem had document key 'mykeypath/44' but now has document key property 'MyItems/44'. You cannot change the document key property of a entity loaded into the session.

Edited to include complete source code

void Main()
{
    var host = "http://myhost:8020";

    var _documentStore = new DocumentStore { Url = host, DefaultDatabase = "sdb" };
    _documentStore.Initialize();
    using (var session = _documentStore.OpenSession()) 
    {
        var path = "mykeypath/44";
        session.Store(new MyItem { Id = 303, Test = "TEST" }, path);
        session.SaveChanges();
    }
}

public class MyItem : AuditableEntity {
    public string Test { get; set; }
}

[Serializable]
public abstract class AuditableEntity : IAuditable
{
   [DataMember]
   [Display(Name = "Id", Description = "Id")]
   public long Id { get; set; }
   [DataMember]
   [Display(Name = "Modified By", Description = "Modified By")]
    public string ModifiedBy { get; set; }
   [DataMember]
   [Display(Name = "Modified Date", Description = "Modified Date")]
    public DateTime ModifiedDate { get; set; }
}

Upvotes: 2

Views: 1177

Answers (1)

Cristian Lupascu
Cristian Lupascu

Reputation: 40526

If your class has a property called Id, RavenDB will use that as document id by default.

In your case, having an instance of MyItem with Id set to 303, means that the document will be saved in the database with an Id of MyItems/303.

You can test that easily by modifying the

session.Store(new MyItem { Id = 303, Test = "TEST" }, path);

line with

session.Store(new MyItem { Id = 303, Test = "TEST" });

This will work and the result will be:

Saved document

So, the issue is that you are instructing Raven to assign two IDs to your object:

  • MyItems/303, due to the value of the Id property
  • mykeypath/44, due to the fact that you're explicitly setting this Id by using the Store overload.

The solution I would apply in this case is to rename the Id property. If that's not possible (ex: the class is already used for other purposes, like a WCF contract), consider creating a new class used solely for RavenDB persistence and map between the two (tools like AutoMapper make this easy).

Update

I found a way to bend the Id field convention by looking at this thread. In your case, you should do the following prior to opening the session:

_documentStore.Conventions.FindIdentityProperty =
    prop =>
        {
            if (prop.DeclaringType == typeof (AuditableEntity))
                return prop.Name == "CustomId";

            return prop.Name == "Id";
        };

What this says is that the property Id defined by the type AuditableEntity should not be used as a document identifier. In all other cases, do the default.

I tested it and it works fine, showing two Id fields in the RavenDb studio :) :

enter image description here

Upvotes: 3

Related Questions