首页 > 解决方案 > 在 Mongodb C# 中使用 AsQueryable 进行过滤 - ExpandoObject

问题描述

我正在尝试使用 AsQueryable 和 LINQ 函数查询 mongodb 数据库。

我当前的数据库结构是我有一些集合,主要是在 C# 中定义的记录,如下所示:

public class Record  
    {
        [BsonElement("formName")]
        public string FormName { get; set; }

        [BsonElement("created")]
        public DateTime Created { get; set; }

        [BsonElement("createdBy")]
        public string CreatedBy { get; set; }

        [BsonId]
        [BsonIgnoreIfDefault]
        [BsonRepresentation(BsonType.ObjectId)]
        private string InternalId { get; set; }

        [BsonElement("recordId")]
        [Newtonsoft.Json.JsonProperty("recordId")]
        public string Id { get; set; }

        [BsonElement("organisationId")]
        public string OrganisationId { get; set; }

        // TODO: Consider making configurable
        private string appName = "MediLog";

        [BsonElement("appName")]
        public string AppName
        {
            get { return this.appName; }
            set { this.appName = value; }
        }

        [BsonElement("schemaVersion")]
        public int SchemaVersion { get; set; }

        [BsonElement("contents")]
        public ExpandoObject Contents { get; set; }

        [BsonElement("modified")]
        public DateTime Modified { get; set; }

        [BsonElement("modifiedBy")]
        public string ModifiedBy { get; set; }

        [BsonElement("majorVersion")]
        public int MajorVersion { get; private set; }

        [BsonElement("minorVersion")]
        public int MinorVersion { get; private set; }

        [BsonElement("version")]
        public string Version
        {
            get
            {
                return (MajorVersion + "." + MinorVersion);
            }

            set
            {
                MajorVersion = Convert.ToInt32(value?.Substring(0, value.IndexOf(".", StringComparison.OrdinalIgnoreCase)), CultureInfo.InvariantCulture);
                MinorVersion = Convert.ToInt32(value?.Substring(value.IndexOf(".", StringComparison.OrdinalIgnoreCase) + 1), CultureInfo.InvariantCulture);
            }
        }

    }

像您一样,数据主要存储在Contents其中,这就是ExpandoObject我的问题所在。

我正在尝试做这样的事情:

 var collection =
                database.GetCollection<Record>("recordData").AsQueryable()
                    .Where(x =>  x.Contents.SingleOrDefault(z => z.Key == "dateOfBirth").Value.ToString().Length > 0)
                        .ToList();  

但我得到了这个例外:

System.InvalidOperationException HResult=0x80131509 Message={document}{contents}.SingleOrDefault(z => (z.Key == "dateOfBirth")).Value.ToString().Length 不受支持。

另外,当我尝试时:

  var collection1 = database.GetCollection<Record>("recordData").AsQueryable()
                .Where(x => (DateTime)(x.Contents.SingleOrDefault(z => z.Key == "dateOfBirth").Value)  > DateTime.Now)
                .ToList();

我得到这个例外:

System.InvalidOperationException
  HResult=0x80131509
  Message=Convert({document}{contents}.SingleOrDefault(z => (z.Key == "dateOfBirth")).Value) is not supported.

另外,当我这样做时:

var collection =
database.GetCollection(“recordData”).AsQueryable()
.Where(x => (DateTime)x.Contents.First(z => z.Key == “dateOfBirth”).Value > DateTime.Now ).ToList();

我得到了这个例外:

System.NotSupportedException: ‘The expression tree is not supported: {document}{contents}’

所以我的问题实际上是如何使用 AsQueryable 和 LINQ 对 ExpandoObject 类型的对象运行查询。

谢谢!

标签: c#mongodblinqmongodb-.net-driver

解决方案


无论你试图传递给.Where()方法的是一个表达式树,它需要被翻译成 MongoDB 查询语言。C# 编译器将接受(extension method)ExpandoObject之类的SingleOrDefault方法,但是当需要将此类表达式树转换为 Mongo 查询时,这将在运行时失败。

ExpandoObject使用 MongoDB 并没有什么特别之处。它必须以某种方式翻译成 BSON 文档(MongoDB 格式),例如下面的代码:

dynamic o = new ExpandoObject();
o.dateOfBith = new DateTime(2000, 1, 1);
r.Contents = o;
col.InsertOne(r);

ExpandoObject以相同的方式插入,BsonDocument或者Dictionary<string, T>它只是变成:

{ "_id" : ObjectId(",,,"), "contents" : { "dateOfBith" : ISODate("2000-01-01T07:00:00Z") } }

在您的数据库中。

知道您可以使用点表示法构建查询。您还可以使用一个StringFieldDefinition类,因为您无法ExpandoObject以强类型方式构建表达式树:

var fieldDef = new StringFieldDefinition<Record, DateTime>("contents.dateOfBith");
var filter = Builders<Record>.Filter.Gt(fieldDef, new DateTime(2000, 1, 1));

var res = col.Find(filter).ToList();

推荐阅读