12

In the database, I have already stored hundreds of documents. Now the system architecture has changed and (among others) models was migrated into different namespace (in different assembly).

Below, metadata of sample document is shown:

enter image description here

and code I'm using to fetch such document:

var configuration = documentSession.Load<One.Social.Core.Entities.Setting>("Setting");

which throws casting exception:

[InvalidCastException: Unable to cast object of type 'One.QA.Core.Entities.Setting' to type 'One.Social.Core.Entities.Setting'.]

UPDATE:

Similiar error but from NewtonsoftJson rises, while I have collection of specified type inside the dosument, which now changed.

In database I have Question document, which contains a list of Answers:

enter image description here

In the code, the type looks like that:

namespace One.Social.Ask.Web.Models
{
    public class Question
    {        
        public string Content { get; set; }
        public IList<One.Social.Ask.Web.Models.Answer> Answers { get; set; }        
    }
}

Answers namespace changed. In addition, now it's derived from IList<>, no ICollection<>. I don't need the $type meta now, it should be:

enter image description here.

While it is a list now, an error rises because of the old $type information:

Newtonsoft.Json.JsonSerializationException: Error resolving type specified in JSON 'System.Collections.ObjectModel.Collection`1[[One.QA.Core.Entities.Answer, One.QA.Core]], mscorlib'. ---> Newtonsoft.Json.JsonSerializationException: Could not find type 'System.Collections.ObjectModel.Collection`1[[One.QA.Core.Entities.Answer, One.QA.Core]]' in assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.

What is the best way to migrate all documents to reflect current types names ? Is there any built in mechanism ?

Btw: I'm using RavenDB - Build #960

jwaliszko
  • 16,942
  • 22
  • 92
  • 158

4 Answers4

4

I had the same problem and ended up doing this:

Advanced.DatabaseCommands.UpdateByIndex(
    "Raven/DocumentsByEntityName",
        new IndexQuery {Query = "Tag:Album"},
        new []{ new PatchRequest() { 
            Type = PatchCommandType.Modify, 
            Name = "@metadata", 
            Nested= new []{ 
                new PatchRequest{
                    Name= "Raven-Clr-Type",
                    Type = PatchCommandType.Set,
                    Value = "Core.Model.Album, Core" }}}},
        false);
Rasmus
  • 2,783
  • 2
  • 33
  • 43
3

Jarek, The reason for the issue is that you HAVE both types. If you remove the QA type, it will just work. Or, you can do as Wyatt suggested and force this.

Ayende Rahien
  • 22,925
  • 1
  • 36
  • 41
  • Thanks, indeed I had second type. After delete raven has automatically changed document meta info to current type "Raven-Clr-Type": "One.Social.Ask.Web.Models.Setting, One.Social.Ask.Web". Can You just have a look at the update of my question ? I have another similar problem. – jwaliszko Jun 28 '12 at 10:24
  • I've had the same issue where I couldn't even delete the documents from the RavenDB Studio. This was because the old clr-type was still present in a plugin! I've had to stop ravendb, delete the plugin, restart ravendb, delete all documents via the studio (it worked now), then stop ravendb again, reïnstall the plugin and start ravendb again. – ldx Feb 27 '14 at 09:50
2

You'll need to patch the ravendb documents directly without attempting to deserialize them. I haven't ever had to do this myself, but I think the magic is done using the methods found in IDocumentSession.Advanced.DatabaseCommands, particularly the Patch method.

I don't really have anything to test against, but I think the code should look like:

//s is your document session
var toUpdate = s.Advanced.DatabaseCommands.StartsWith("Setting", 0, 128);
foreach (var d in toUpdate)
{
    var p = new PatchRequest();
    p.AllPositions = true;
    p.Type = PatchCommandType.Modify;
    d.Metadata["Raven-Clr-Type"] = "MyNewType";
    p.Value = d.ToJson();
    s.Advanced.DatabaseCommands.Patch(d.Key, new []{p});
}
// push forward and repeat for all items in collection

There is also a way to do this without looping through the collection but I'm not sure how to effect that properly.

Wyatt Barnett
  • 15,573
  • 3
  • 34
  • 53
  • 1
    to do it without looping through all the docs, you can use set-based operations, see http://ravendb.net/docs/client-api/set-based-operations – Matt Warren Jun 27 '12 at 15:00
  • 1
    Thanks Matt, not sure if those work if you are monkeying about with metadata though. – Wyatt Barnett Jun 27 '12 at 15:12
  • Thanks for the tip. What if I have a collection of some type inside the document, and the collection type namespace changes ? (see update) – jwaliszko Jun 28 '12 at 10:07
  • First, I'd defer to Ayende. He invented the thing :). Same general technique would apply I'd think. You'd have to query the data document -- d in the example above -- to find the property and then modify it accordingly. Could be posted in the same patch though. – Wyatt Barnett Jun 28 '12 at 12:20
0

I faced the exact same issue of casting exception after a renaming. As advised in the previous answers, I ended up patching all my documents based on this snippet.

We need to update two fields: Raven-Entity-Name and Raven-Clr-Type.

Example

Renaming MyType to MyNewType, updating the namespace from My.Namespace.MyType to My.New.Namespace.MyNewType.

Raven-Entity-Name needs to be changed from MyTypes to MyNewTypes. Why plural? "Convention over configuration" approach, as explained here.

Raven-Clr-Type needs to be updated from My.Namespace.MyType to My.New.Namespace.MyNewType.

Code

public static void PatchMetadata()
        {
            var operation = session.Advanced.DocumentStore.DatabaseCommands
            .UpdateByIndex(
                // You can check your index name in the Studio Under INDEXES.
                    "Raven/DocumentsByEntityName",
                //  query that will be performed
                    new IndexQuery
                    {
                        // A collection in RavenDB is a set of documents with the same tag. 
                        // The tag is defined in Raven-Entity-Name.
                        Query = "Tag:MyTypes"
                    }, new[]
                                {
                                    new PatchRequest
                                        {
                                            Type = PatchCommandType.Modify,
                                            Name = "@metadata",
                                            Nested = new[]
                                                {
                                                    new PatchRequest
                                                        {
                                                            Type = PatchCommandType.Set,
                                                            Name = "Raven-Entity-Name",
                                                            Value = new RavenJValue("MyNewTypes")
                                                        }
                                                        ,
                                                    new PatchRequest
                                                        {
                                                            Type = PatchCommandType.Set,
                                                            Name = "Raven-Clr-Type",
                                                            Value = new RavenJValue("My.New.Namespace.MyNewType, RavenDbPatching")
                                                        }
                                                }
                                        }
                                },
                    new BulkOperationOptions() { AllowStale = true }
            );
        }
Community
  • 1
  • 1
Andrew
  • 7,848
  • 1
  • 26
  • 24