[CSHARP-1463] OrderBy DateTime Created: 30/Oct/15  Updated: 14/Mar/23  Resolved: 04/Nov/15

Status: Closed
Project: C# Driver
Component/s: Linq, Serialization
Affects Version/s: 2.1
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Miguel Ángel Arilla Assignee: Unassigned
Resolution: Duplicate Votes: 0
Labels: Bug
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Duplicate
is duplicated by CSHARP-1468 Unable to determine the serialization... Closed

 Description   

I tried several times to sort by a DateTime field and throws the NotSupportedException, only fields allowed in a $sort. I explained it with more details here:

http://stackoverflow.com/questions/33435494/linq-expression-tree-avoid-inner-cast-to-object/33435721#33435721



 Comments   
Comment by Valentin Levshits [ 14/Mar/23 ]

Issue is actual again in MongoDB Driver 2.19. Works fine in 2.18

Comment by Miguel Ángel Arilla [ 04/Nov/15 ]

Works smoothly.

Thanks, Miguel.

Comment by Craig Wilson [ 04/Nov/15 ]

Thanks so much Miguel... I just fixed something very similar to this in CSHARP-1468. Would you mind trying this build (https://www.myget.org/F/mongodb/api/v2/package/MongoDB.Driver-Build/2.1.1-build-0003) and seeing if the problem is fixed?

Craig

Comment by Miguel Ángel Arilla [ 04/Nov/15 ]

I finally managed to reproduce the error with your snippet, the thing is that I developed a wrapper for the mongodb driver to integrate it in our DDD architecture, and some complex queries are passed through a custom object.

//it is more extensive, but only this field is needed for the test to fail
public class CustomQuery<TCollection>
        {
            public Expression<Func<TCollection, object>> Sort { get; set; }
        }

If i introduce this change to the snippet, the test fails.

static async Task MainAsync()
        {
            var client = new MongoClient();
            var db = client.GetDatabase("test");
            await db.DropCollectionAsync("foo");
            var col = db.GetCollection<Person>("foo");
            await col.InsertOneAsync(new Person { Name = "Joe", BirthDate = DateTime.UtcNow });
 
            var query = new CustomQuery<Person> { Sort = person => person.BirthDate };
 
            var promise = col.AsQueryable().Where(person => person.Name == "Joe");
            promise = promise.OrderByDescending(query.Sort);
 
            var result = promise.ToList();
            result = await col.Find(_ => true).SortBy(x => x.BirthDate).ToListAsync();
        }

Comment by Craig Wilson [ 03/Nov/15 ]

I tried on .NET 4.5 all the way through 4.6 as well. Anyways... let me know what you find.

Thanks,
Craig

Comment by Miguel Ángel Arilla [ 03/Nov/15 ]

We are currently using .NET framework 4.6, it may be the cause since its new. I'll try the code tomorrow early.

Thank you very much for your effort, Miguel.

Comment by Craig Wilson [ 03/Nov/15 ]

Hi Miguel,

I fully understand the problem you are indicating, but simply can't make it reproduce in any tests. What version of the .NET framework are you using?

Below is a simple console application that is working based on your sample above. I've also done the querying 2 different ways and can't make it reproduce.

using System;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Conventions;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
 
namespace MongoTest
{
    internal class Program
    {
        static void Main(string[] args)
        {
            try
            {
                MainAsync().GetAwaiter().GetResult();
            }
            catch
            {
                throw;
            }
        }
 
        static async Task MainAsync()
        {
            var pack = new ConventionPack
            {
                new DefaultDateTimeIgnoreConvention(),
                new CamelCaseUnderscoreSpaceElementNameConvention()
            };
            ConventionRegistry.Register("default", pack, _ => true);
 
            var client = new MongoClient();
            var db = client.GetDatabase("test");
            await db.DropCollectionAsync("foo");
            var col = db.GetCollection<Person>("foo");
            await col.InsertOneAsync(new Person { Name = "Joe", BirthDate = DateTime.UtcNow });
 
            var result = col.AsQueryable().OrderByDescending(x => x.BirthDate).ToList();
            result = await col.Find(_ => true).SortBy(x => x.BirthDate).ToListAsync();
        }
 
        public class Person
        {
            public ObjectId Id { get; set; }
 
            public string Name { get; set; }
 
            public DateTime BirthDate { get; set; }
        }
 
        public class DefaultDateTimeIgnoreConvention : IMemberMapConvention
        {
            public string Name { get; }
 
            public void Apply(BsonMemberMap memberMap)
            {
                if (memberMap.MemberType == typeof(DateTime))
                {
                    memberMap.SetIgnoreIfNull(false);
                    memberMap.SetIgnoreIfDefault(true);
                }
            }
        }
 
        public class CamelCaseUnderscoreSpaceElementNameConvention : IMemberMapConvention
        {
            public string Name { get; }
 
            public void Apply(BsonMemberMap memberMap)
            {
                memberMap.SetElementName(memberMap.ElementName.ToLowerInvariant());
            }
        }
 
    }
}

Let me know if this reproduces for you. If not, perhaps you could try and tweak it to make it happen.
Craig

Comment by Craig Wilson [ 03/Nov/15 ]

Thanks Miguel. Yes, I understand the problem. I simply wasn't able to make a test fail because of this. I even tried what you did above. I'll make another attempt today. Perhaps I did something subtly different in my attempt. I'll try again.

Also, thanks for the conventions. There isn't any reason the conventions should affect this, but I'll look into that angle as well if I can't repro.

Craig

Comment by Miguel Ángel Arilla [ 03/Nov/15 ]

In case this also causes any kind of internal issues when trying to sort, I got a second custom convention that simply translates the notation of .NET entities, like {{ public string SomeField }} to some_field.

public class CamelCaseUnderscoreSpaceElementNameConvention : IMemberMapConvention
    {
        public string Name { get; }
 
        public void Apply(BsonMemberMap memberMap)
        {
            memberMap.SetElementName(memberMap.ElementName.Mongify());
        }
    }

Where Mongify is the method that converts the string (just some char play).

Comment by Miguel Ángel Arilla [ 03/Nov/15 ]

Hi,

The code structure is pretty basic, but I have a custom convention related to datetime.

public class Sample
{
      public DateTime SampleDate { get; set; }
}

the operation is _collection.AsQueryable().OrderByDescending(x => x.SampleDate)

I post the custom convention in case it's the problem.

public class DefaultDateTimeIgnoreConvention : IMemberMapConvention
    {
        public string Name { get; }
 
        public void Apply(BsonMemberMap memberMap)
        {
            if (memberMap.MemberType == typeof(DateTime))
            {
                memberMap.SetIgnoreIfNull(false);
                memberMap.SetIgnoreIfDefault(true);
            }
        }
    }

It may be due to the internal cast "Convert" that the expression tree performs on the field. (as explained in the stackoverflow link, with a possible fix too)

Comment by Craig Wilson [ 02/Nov/15 ]

Hi Miguel Arilla,

I have attempted a couple of different ways to make this reproduce. Could you provide a reproducible sample I can use? I'd love to fix this problem.

Thanks,
Craig

Generated at Wed Feb 07 21:39:40 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.