Querying an enum property persisted as an integer is not working

313 views
Skip to first unread message

rfuller

unread,
Oct 4, 2011, 6:32:07 PM10/4/11
to ravendb
I want to persist enums as integers instead of as strings so i've
removed the JsonEnumConverter like so:

store.Conventions.CustomizeJsonSerializer =
jsonSerializer =>
{

jsonSerializer.Converters.Remove(jsonSerializer.Converters.Where(c =>
c.GetType() == typeof(JsonEnumConverter)).First());
};

This has the desired effect and now enums are being persisted as
integers instead of as strings.

However now I am unable to query on enum properties:

var offProducts= session.Query<Product>().Where(p => p.MyEnum ==
TestEnum.Off);

This query returns no results even though matching documents are
present.

How can I fix it so that I can query on enums even when they're
persisted as integers?

-Ryan

Itamar Syn-Hershko

unread,
Oct 4, 2011, 7:12:20 PM10/4/11
to rav...@googlegroups.com
The query is being sent as a string enum to the server, you need to make it send ints

Try

var offProducts= session.Query<Product>().Where(p => (int)(p.MyEnum) == (int)TestEnum.Off);

rfuller

unread,
Oct 4, 2011, 11:05:27 PM10/4/11
to ravendb
Casting the enums to ints doesn't work either. But even if it did I
want to be able to use the enums themselves.

I tried just removing the default enum conveter as suggested in this
post: http://groups.google.com/group/ravendb/browse_thread/thread/d86ed30cdac55c4d/c8aa3ffb1f0ae49d?lnk=gst&q=enum#c8aa3ffb1f0ae49d

and I also tried inserting my own converter that uses the int value
instead of the string.

Both methods make the enum save to the document as an int. Neither
method allows me to query on the enum value and get any results. I
feel like I'm missing something. Like there's some other place I need
to add or remove a converter in order to get Raven to query enums
properly.

-Ryan

On Oct 4, 7:12 pm, Itamar Syn-Hershko <ita...@hibernatingrhinos.com>
wrote:

rfuller

unread,
Oct 5, 2011, 12:13:02 AM10/5/11
to ravendb
When I print out my query "var allProducts =
session.Query<Product>().Where(p => p.MyEnum == TestEnum.Off);" it
always reads "MyEnum:Off". So it looks to me like Raven isn't using
my custom JsonEnumConverter for querying even though it is using it
for saving and loading documents. How can I get RavenDB to use the
Custom JsonEnumConverter that I provided to generate queries?

-Ryan

On Oct 4, 11:05 pm, rfuller <rful...@gmail.com> wrote:
> Casting the enums to ints doesn't work either.  But even if it did I
> want to be able to use the enums themselves.
>
> I tried just removing the default enum conveter as suggested in this
> post:http://groups.google.com/group/ravendb/browse_thread/thread/d86ed30cd...

Ayende Rahien

unread,
Oct 5, 2011, 12:30:30 AM10/5/11
to rav...@googlegroups.com
Can you pro idea failing test?

rfuller

unread,
Oct 5, 2011, 12:49:56 AM10/5/11
to ravendb
Sure. How's this?

This is my enum:

public enum TestEnum
{
Off = 0,
On = 1,
}

This is my Product class:

public class Product
{
private Guid id;
private TestEnum myEnum;

public Product()
{
this.myEnum = TestEnum.On;
this.id = Guid.NewGuid();
}

public TestEnum MyEnum
{
get
{
return this.myEnum;
}
set
{
this.myEnum = value;
}
}

public Guid Id
{
get
{
return this.id;
}
}
}

And this is my failing test:

DocumentStore store = new DocumentStore();
store.Url = "http://localhost:8080";
store.Conventions.CustomizeJsonSerializer =
jsonSerializer =>
{

jsonSerializer.Converters.Remove(jsonSerializer.Converters.Where(c =>
c.GetType() == typeof(JsonEnumConverter)).First()); //removing this
makes Raven store enums as ints instead of strings
};
store.Initialize();

using (store)
{
using (var session = store.OpenSession())
{
var product = new Product
{
MyEnum = TestEnum.Off,
};
session.Store(product);
session.SaveChanges();
}

using (var session = store.OpenSession())
{
var offProducts = session.Query<Product>().Where(p
=> p.MyEnum == TestEnum.Off).ToList();
Assert.IsNotEmpty(offProducts);
}
}



-Ryan

Ayende Rahien

unread,
Oct 5, 2011, 2:53:38 AM10/5/11
to rav...@googlegroups.com
Um, yes. That is expected.
You store enums as ints, but query them as strings.
Why do you do that?

rfuller

unread,
Oct 5, 2011, 9:27:09 AM10/5/11
to ravendb
I'm not querying them as strings. I'm querying them as enums:

session.Query<Product>().Where(p => p.MyEnum == TestEnum.Off)

Somewhere behind the scenes these enums are being converted to their
string representation. How can I get them to be converted to their
int representation just like they were when they were saved?

Let me try to word this in a more general way. If I use my own custom
type serializer shouldn't the query generator also use this same
custom type serializer to generate queries? For example if I use my
own custom serialilzer to save enums as ints instead of strings how
can I get the query generator to use this custom serializer to create
it's queries?

-Ryan

rfuller

unread,
Oct 5, 2011, 1:49:33 PM10/5/11
to ravendb
This has gotten kinda muddled so the question is:

Is there a way to save enums to the database as ints and still be able
to query them using Linq:
session.Query<Product>().Where(p => p.MyEnum == TestEnum.Off)

-Ryan

Matt

unread,
Oct 5, 2011, 2:21:37 PM10/5/11
to ravendb
Just cast it on save and cast it on query.

session.Store<Product>(new Product { MyEnum = (int) TestEnum.Off });

session.Query<Product>().Where(p => p.MyEnum == (int) TestEnum.Off);

rfuller

unread,
Oct 5, 2011, 2:31:18 PM10/5/11
to ravendb
I don't want to treat it like an int. I want to treat it like an enum
and just persist it as an int. Just like normally you don't treat it
as a string. You treat it like an enum and Raven just persists it as
a string.

Ayende Rahien

unread,
Oct 6, 2011, 6:50:21 AM10/6/11
to rav...@googlegroups.com
RavenDB currently assumes the default behavior. 
Why are you trying to persist it like an int?

rfuller

unread,
Oct 6, 2011, 12:34:38 PM10/6/11
to ravendb
Personal preference. Persisting enums as numbers makes refactoring
easier. I think of enums as a number with a human readable label
attached. Persisting them as this number means I'm free to change the
label as I (or the client) sees fit. For example if I decide that my
On/Off enum should instead be called Active/Inactive then if the enum
number is persisted it's a really easy change (right click in visual
studio and rename). If the enum string is persisted, as in RavenDB,
I'd have to update all existing documents. In most of my enums the
number is completely arbitrary, and so has no reason to ever change.
The label part is much more likely to change as I develop my program.
Basically, If you persist an enum as a string then the number
component becomes very easy to change. If you persist an enum as a
number then the string component becomes very easy to change. I'd
rather the string part be easy to change. And I don't really care
about readability of the raw json document itself. No end user will
ever see it, and I probably won't look at the raw json document that
much either once I've gotten comfortable with RavenDB.

-Ryan

Itamar Syn-Hershko

unread,
Oct 6, 2011, 1:49:21 PM10/6/11
to rav...@googlegroups.com
You should probably use ITypeConverter then, to convert Enums to their int representation and back

rfuller

unread,
Oct 6, 2011, 2:25:56 PM10/6/11
to ravendb
Itamar,
That's what I was doing. It was a "JsonConverter" though, not
"ITypeConverter". I wrote my own replacement for the default
JsonEnumConverter and set it up like this:

documentStore.Conventions.CustomizeJsonSerializer =
jsonSerializer =>
{

jsonSerializer.Converters.Remove(jsonSerializer.Converters.Where(c =>
c.GetType() == typeof(JsonEnumConverter)).First());
jsonSerializer.Converters.Add(new
CustomJsonEnumConverter());
};

Once I did this enums then were saved and loaded correctly as ints.
HOWEVER I lost the ability to query them with linq.

var offProducts = session.Query<Product>().Where(p => p.MyEnum ==
TestEnum.Off).ToList();

Linq queries using the enum don't work. This query would return no
results even if there are matching documents.

I don't understand how implementing your own custom JsonConverter is
useful if it renders that Type unqueryable (or is this just a quirk of
the enum type specifically?).

If I did something wrong let me know. You mentioned ITypeConverter
instead of JsonConverter. How would this be done using
ITypeConverter?

-Ryan

On Oct 6, 1:49 pm, Itamar Syn-Hershko <ita...@hibernatingrhinos.com>
wrote:

Ayende Rahien

unread,
Oct 7, 2011, 4:19:36 AM10/7/11
to rav...@googlegroups.com
store.Conventions.QueryEnumsAsIntegers  = true;

As of the next build.

rfuller

unread,
Oct 7, 2011, 11:44:18 AM10/7/11
to ravendb
Awesome. Thanks!

rfuller

unread,
Oct 11, 2011, 2:44:07 PM10/11/11
to ravendb
I just downloaded RavenDB Build 499 and
store.Conventions.QueryEnumsAsIntegers is working great.

Have you considered maybe changing it to a setting like
store.Conventions.SaveEnumsAsIntegers that would do both saving and
querying of enums as integers? Since one without the other doesn't
make sense.

-Ryan

On Oct 7, 11:44 am, rfuller <rful...@gmail.com> wrote:
> Awesome.  Thanks!
>
> On Oct 7, 4:19 am, Ayende Rahien <aye...@ayende.com> wrote:
>
>
>
>
>
>
>
> > store.Conventions.QueryEnumsAsIntegers  = true;
>
> > As of the next build.
>
> > On Thu, Oct 6, 2011 at 8:25 PM, rfuller <rful...@gmail.com> wrote:
> > > Itamar,
> > > That's what I was doing.  It was a "JsonConverter" though, not
> > > "ITypeConverter".  I wrote my own replacement for the default
> > > JsonEnumConverter and set it up like this:
>
> > >            documentStore.Conventions.CustomizeJsonSerializer =
> > >             jsonSerializer =>
> > >            {
>
> > > jsonSerializer.Converters.Remove(jsonSerializer.Converters.Where(c =>
> > > c.GetType() == typeof(JsonEnumConverter)).First());
> > >                 jsonSerializer.Converters.Add(new
> > > CustomJsonEnumConverter());
> > >            };
>
> > > Once I did this enums then were saved and loaded correctly as ints.
> > > HOWEVER I lost the ability to query them with linq.
>
> > >  var offProducts = session.Query<Product>().Where(p => p.MyEnum ==
> > > TestEnum.Off).ToList();
>
> > > Linq queries using theenumdon't work.  This query would return no
> > > results even if there are matching documents.
>
> > > I don't understand how implementing your own custom JsonConverter is
> > > useful if it renders that Type unqueryable (or is this just a quirk of
> > > theenumtype specifically?).
>
> > > If I did something wrong let me know.  You mentioned ITypeConverter
> > > instead of JsonConverter.  How would this be done using
> > > ITypeConverter?
>
> > > -Ryan
>
> > > On Oct 6, 1:49 pm, Itamar Syn-Hershko <ita...@hibernatingrhinos.com>
> > > wrote:
> > > > You should probably use ITypeConverter then, to convert Enums to their
> > > int
> > > > representation and back
>
> > > > On Thu, Oct 6, 2011 at 6:34 PM, rfuller <rful...@gmail.com> wrote:
> > > > > Personal preference.  Persisting enums as numbers makes refactoring
> > > > > easier.  I think of enums as a number with a human readable label
> > > > > attached.  Persisting them as this number means I'm free to change the
> > > > > label as I (or the client) sees fit.  For example if I decide that my
> > > > > On/Offenumshould instead be called Active/Inactive then if theenum
> > > > > number is persisted it's a really easy change (right click in visual
> > > > > studio and rename).  If theenumstring is persisted, as in RavenDB,
> > > > > I'd have to update all existing documents.  In most of my enums the
> > > > > number is completely arbitrary, and so has no reason to ever change.
> > > > > The label part is much more likely to change as I develop my program.
> > > > > Basically, If you persist anenumas a string then the number
> > > > > component becomes very easy to change.  If you persist anenumas a
> > > > > number then the string component becomes very easy to change.   I'd
> > > > > rather the string part be easy to change.  And I don't really care
> > > > > about readability of the raw json document itself.  No end user will
> > > > > ever see it, and I probably won't look at the raw json document that
> > > > > much either once I've gotten comfortable with RavenDB.
>
> > > > > -Ryan
>
> > > > > On Oct 6, 6:50 am, Ayende Rahien <aye...@ayende.com> wrote:
> > > > > > RavenDB currently assumes the default behavior.
> > > > > > Why are you trying to persist it like an int?
>
> > > > > > On Wed, Oct 5, 2011 at 8:31 PM, rfuller <rful...@gmail.com> wrote:
> > > > > > > I don't want to treat it like an int.  I want to treat it like an
> > >enum
> > > > > > > and just persist it as an int.  Just like normally you don't treat
> > > it
> > > > > > > as a string.  You treat it like anenumand Raven just persists it

Ayende Rahien

unread,
Oct 12, 2011, 2:28:10 AM10/12/11
to rav...@googlegroups.com
Ryan,
The is a good idea, I'll think about this.

Ayende Rahien

unread,
Oct 12, 2011, 4:22:12 AM10/12/11
to rav...@googlegroups.com
Will be there in the next build

Benjamin Speth

unread,
Mar 15, 2016, 1:15:04 PM3/15/16
to RavenDB - 2nd generation document database
Hi,

I was developing a query when i stumbled upon a bug related to how enums are handled when they are saved as integers.

The bug occurs when you use the 'In' method on an Enum value.
When you do that, the request will be sent with the Enum values as string.

here's an example:

----------------
public enum MyEnum {
 Value1,
 Value2
}

public class MyDocument
{
public MyEnum MyProperty { get; set; }
}

var documentStore = new DocumentStore();
documentStore.Conventions.SaveEnumsAsIntegers = true;

var values = new MyEnum[]{ MyEnum.Value1, MyEnum.Value2 };

documentStore.Session
.Query<MyDocument, MyIndex>()
.Where(x => x.MyProperty.In(values));
----------------

this code will always return 0 results because it will send enum values as string and not as integers.

I managed to find a work around by casting everything to int, but it kinda look ugly...

----------------
public enum MyEnum {
 Value1,
 Value2
}

public class MyDocument
{
public MyEnum MyProperty { get; set; }
}

var documentStore = new DocumentStore();
documentStore.Conventions.SaveEnumsAsIntegers = true;

var values = new MyEnum[]{ MyEnum.Value1, MyEnum.Value2 };
var intvalues = targetTypes.Select(o => (int)o).ToArray();

documentStore.Session
.Query<MyDocument, MyIndex>()
.Where(x => ((int)x.MyProperty).In(intvalues));
----------------

I'm using RavenDB.Client v3.0.3690.

Benjamin SPETH

Oren Eini (Ayende Rahien)

unread,
Mar 16, 2016, 8:12:07 AM3/16/16
to ravendb

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+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Oren Eini (Ayende Rahien)

unread,
Mar 16, 2016, 8:15:17 AM3/16/16
to ravendb
We'll fix that, but it may take some time, in the meantime, you can use your workaround.
Reply all
Reply to author
Forward
0 new messages