我试图使用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 is not supported.
另外,当我试了一下。
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}’
所以我的问题是,我怎样才能在一个ExpandoObject类型的对象上使用AsQueryable和LINQ进行查询?
谢谢!我正在尝试查询mong的数据,我的问题是如何使用AsQueryable和LINQ对ExpandoObject类型的对象进行查询。
无论你想传递到 .Where()
方法是一个表达式树,需要翻译成MongoDB查询语言。C#编译器将接受 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();
这种表达式的转换在LINQ中并不好用。如果你把字段定义为字符串,它的效果会更好。比你可以 Inject
你的 Where
挽留 IQueryable
如果你喜欢>
更新(你可以有多个条件,以及不同的空检查)
string connectionString = "mongodb://localhost:27017";
var client = new MongoClient(connectionString);
var db = client.GetDatabase("test");
var records = db.GetCollection<Record>("recordData");
//dynamic content = new ExpandoObject();
//content.dateOfBirth = DateTime.Today;
//records.InsertOne(new Record
//{
// AppName = "my app", Created = DateTime.Today, CreatedBy = "some user", FormName = "form name",
// OrganisationId = "organization id", SchemaVersion = 1, Version = "1.1", Contents = content
//});
// first option for multiple conditions:
var filter =
Builders<Record>.Filter.Lt("contents.dateOfBirth", DateTime.Now) &
Builders<Record>.Filter.Gt("contents.dateOfBirth", DateTime.Now.AddDays(-10)) &
// checking if the field is there. Whether the value is null or not.
Builders<Record>.Filter.Exists("contents.dateOfBirth") &
// checking if the field is there, and it's not null
Builders<Record>.Filter.Ne("contents.dateOfBirth", BsonNull.Value);
// second option for multiple conditions
var dateOfBirths = records.AsQueryable().Where(_ => filter.Inject()).Where(x => x.AppName == "my app").ToList();
从那里,你可以继续你的 IQueryable
语法。
更新2
如果是 .Where(x => x.AppName.Contains("app"))
你会得到
"pipeline" : [
{
"$match" : {
"contents.dateOfBirth" : {
"$exists" : true,
"$gt" : ISODate("2020-05-15T12:48:14.483+02:00"),
"$lt" : ISODate("2020-05-25T12:48:14.480+02:00"),
"$ne" : null
}
}
},
{
"$match" : {
"appName" : /app/g
}
}
]
从剖析数据库。所以 <string>.Contains(<string>)
落实在 IQueryable
作为不在内存中的正则表达式过滤器。