InvalidCastException on session Load

43 views
Skip to first unread message

Bert Vandierendonck

unread,
Apr 23, 2018, 11:46:57 AM4/23/18
to RavenDB - 2nd generation document database
Hello,

I'm hitting an InvalidCastException when I try to load a fully typed document, based on an existing id which I get from an index query.
This is my code:

    using (var session = _documentStore.OpenSession())
   
{
       
var fileFragments = session.Query<CategorizedFiles.Result, CategorizedFiles>()
           
.Where(x => x.HasVectors && x.HasExtractedData && x.HasBeenCategorized == false && x.IsReferenceImage == false)
           
.Take(numberOfFiles).ToArray()
           
.Select(v => new FileFragment { Id = v.Id }).ToArray();

       
var ids = fileFragments.Select(f => f.Id).ToArray();
       
var files = session.Load<SystemFile>(ids);

       
return files;
   
}

I use the following index to get the id:

    public class CategorizedFiles : AbstractIndexCreationTask<SystemFile>
   
{
       
public class Result
       
{
           
public string Id { get; set; }
           
public bool IsReferenceImage { get; set; }
           
public bool HasVectors { get; set; }
           
public bool HasBeenCategorized { get; set; }
           
public bool HasExtractedData { get; set; }
       
}

       
public CategorizedFiles()
       
{
           
Map = files =>
               
from file in files
               
select new
               
{
                    file
.Id,
                   
IsReferenceImage = file.Data.ContainsKey("StudentId"),
                   
HasVectors = file.Data.ContainsKey("FaceVectors"),
                   
HasBeenCategorized = file.Data.ContainsKey("HasBeenCategorized") && (bool)file.Data["HasBeenCategorized"],
                   
HasExtractedData = file.Data.ContainsKey("HasExtractedData") && (bool)file.Data["HasExtractedData"]
               
};
       
}
   
}

I have already found a solution; I don't hit the InvalidCastException if I rework my query into this:

    using (var session = _documentStore.OpenSession())
   
{
       
var fileFragments = session.Query<CategorizedFiles.Result, CategorizedFiles>()
           
.Where(x => x.HasVectors && x.HasExtractedData && x.HasBeenCategorized == false && x.IsReferenceImage == false)
           
.ProjectInto<FileFragment>()
           
.Take(numberOfFiles)
           
.ToArray();

       
var ids = fileFragments.Select(f => f.Id).ToArray();
       
var files = session.Load<SystemFile>(ids);

       
return files;
   
}

The main difference is - of course - the way I do the projection.
It looks to me like the second piece of code is more idiomatic, not to mention prettier.

However, I still want to know: why does the first query throw on session.Load() ?

I'm assuming two things here:
- session.Load always goes directly to the database, unless the document is already cached in the session
- an index returns a full document, unless I use stored fields or projections, which I don't (in the first case)

But with those assumptions I should not be getting the InvalidCastException.
Could someone enlighten me?

Thanks in advance.








Oren Eini (Ayende Rahien)

unread,
Apr 23, 2018, 11:51:41 AM4/23/18
to ravendb
Can you create a failing test for this?

Hibernating Rhinos Ltd  

Oren Eini l CEO Mobile: + 972-52-548-6969

Office: +972-4-622-7811 l Fax: +972-153-4-622-7811

 


--
You received this message because you are subscribed to the Google Groups "RavenDB - 2nd generation document database" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ravendb+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Bert Vandierendonck

unread,
Apr 25, 2018, 9:25:52 AM4/25/18
to RavenDB - 2nd generation document database
These classes are enough to recreate the error.
This code will work against 4.0.3, but not 4.0.2 (the document store initialization is slightly different).
I did test against both versions though.

I'm getting the same results if I use a docker instance or the TestDriver.


public class QuickTest
   
{
       
public void InvalidCastExceptionDemo()
       
{
           
var dbName = "Demo";

           
var store = new DocumentStore { Urls = new[] { "http://localhost:5003" }, Database = dbName };
            store
.Initialize();

           
var databaseNames = store.Maintenance.Server.Send(new GetDatabaseNamesOperation(0, 25));
           
if (databaseNames.Contains(dbName) == false)
                store
.Maintenance.Server.Send(new CreateDatabaseOperation(new DatabaseRecord(dbName)));

            store
.OnBeforeQuery += (sender, beforeQueryExecutedArgs) => { beforeQueryExecutedArgs.QueryCustomization.WaitForNonStaleResults(); };

           
IndexCreation.CreateIndexes(GetType().Assembly, store);

           
using (var session = store.OpenSession())
           
{
                session
.Store(new DemoObject { Id = "TEST 1", Hash = "HASH 1", Name = "1" });
                session
.SaveChanges();
           
}

           
using (var session = store.OpenSession())
           
{
               
var indexResults = session.Query<DemoIndex.Result, DemoIndex>()
                                         
.Where(r => r.Hash == "HASH 1")
                                         
.Take(10).ToArray()
                                         
.Select(x => new DemoContainer { Id = x.Id }).ToArray();

               
var ids = indexResults.Select(x => x.Id).ToArray();
               
var loadResults = session.Load<DemoObject>(ids);
           
}
       
}
   
}

   
public class DemoIndex : AbstractIndexCreationTask<DemoObject>

   
{
       
public class Result
       
{
           
public string Id { get; set; }

           
public string Hash { get; set; }
       
}

       
public DemoIndex()

       
{
           
Map = files =>
               
from file in
files
               
select new { file.Id, file.Hash };
       
}
   
}

   
public class DemoObject

   
{
       
public string Id { get; set; }

       
public string Name { get; set; }
       
public string Hash { get; set; }
   
}

   
public class DemoContainer

   
{
       
public string Id { get; set; }
   
}

Just yell if you need anything else.

Op maandag 23 april 2018 17:51:41 UTC+2 schreef Oren Eini:
To unsubscribe from this group and stop receiving emails from it, send an email to ravendb+u...@googlegroups.com.

Oren Eini (Ayende Rahien)

unread,
Apr 26, 2018, 5:32:02 PM4/26/18
to ravendb
You are explicitly forcing RavenDB to treat this objects as Result objects when you calll:

 session.Query<DemoIndex.Result, DemoIndex>()

Even though they are stored with DemoObject marker in the server, this client side configuration forces us to treat them as Result.

This can work with: 

 var indexResults = session.Query<DemoIndex.Result, DemoIndex>()
     .Customize(x => x.WaitForNonStaleResults())
                           .Where(r => r.Hash == "HASH 1")
                           .Take(10)
                           .OfType<DemoObject>()
                           .ToArray()
                           .Select(x => new DemoContainer { Id = x.Id })
                           .ToArray();

Or, in this case, use:
 var indexResults = session.Query< DemoObject , DemoIndex>()
     .Customize(x => x.WaitForNonStaleResults())
                           .Where(r => r.Hash == "HASH 1")
                           .Take(10)
                           .ToArray()
                           .Select(x => new DemoContainer { Id = x.Id })
                           .ToArray();
To unsubscribe from this group and stop receiving emails from it, send an email to ravendb+unsubscribe@googlegroups.com.

Bert Vandierendonck

unread,
Apr 27, 2018, 3:14:55 AM4/27/18
to RavenDB - 2nd generation document database
That explains part of my problem.
But there's still something different going on when using ProjectInto.
Consider these two queries:


            using (var session = store.OpenSession())
           
{
               
var indexResults = session.Query<DemoIndex.Result, DemoIndex>()
                                         
.Where(r => r.Hash == "HASH 1")
                                         
.Take(10).ToArray()
                                         
.Select(x => new DemoContainer { Id = x.Id }).ToArray();

               
var ids = indexResults.Select(x => x.Id).ToArray();
               
var loadResults = session.Load<DemoObject>(ids);
           
}

            using (var session = store.OpenSession())
           
{
               
var indexResults = session.Query<DemoIndex.Result, DemoIndex>()
                                         
.Where(r => r.Hash == "HASH 1")

                                         
.ProjectInto<DemoContainer>()
                                         
.Take(10).ToArray();


               
var ids = indexResults.Select(x => x.Id).ToArray();
               
var loadResults = session.Load<DemoObject>(ids);
           
}

In the first case, querying the index will put the resulting objects in cache (I guess?).
Later, when loading those ids as a different type, I get the InvalidCastException.
I specifically asked Raven to consider my docs as Result, so that kind of makes sense.

However, in the second case, I don't get the InvalidCastException.
Does that mean that when using ProjectInto, the objects are not loaded into the cache?

Note that I also get the InvalidCastException while using OfType.
The types reported in the error are different however.
Query with OfType:

            using (var session = store.OpenSession())
           
{
               
var indexResults = session.Query<DemoIndex.Result, DemoIndex>()
                                         
.Where(r => r.Hash == "HASH 1")

                                         
.OfType<DemoContainer>()
                                         
.Take(10).ToArray();


               
var ids = indexResults.Select(x => x.Id).ToArray();
               
var loadResults = session.Load<DemoObject>(ids);
           
}

Op donderdag 26 april 2018 23:32:02 UTC+2 schreef Oren Eini:

Oren Eini (Ayende Rahien)

unread,
Apr 28, 2018, 2:39:52 PM4/28/18
to ravendb
ProjectInto means that you are projecting. RavenDB knows that these aren't documents.
When you ask for them again, it fetch the document from the server again.

OfType just converts the client side type, it is similiar to if you replace the Result there, for the same reasons
To unsubscribe from this group and stop receiving emails from it, send an email to ravendb+unsubscribe@googlegroups.com.

Bert Vandierendonck

unread,
May 2, 2018, 3:30:06 AM5/2/18
to RavenDB - 2nd generation document database
Mystery solved!

Thanks for the explanation, and your patience.

Op zaterdag 28 april 2018 20:39:52 UTC+2 schreef Oren Eini:
Reply all
Reply to author
Forward
0 new messages