我已经使用mongo / c#实现了一个场景,我在同一个集合中存储了不同类型的类。一切都很好,我最终可以做到这一点:
var collection = _mongoDb.GetCollection<TLogType>(CollectionNames.Logs).OfType<TLogType>();
现在我需要类似的功能但属性。基本上,我有一系列联系人的集合(正常的,不是多态的)。我希望数组中的每个项目都是特定的联系类型。我提出的文件看起来像这样(为了简洁起见较小):
Contacts" : [
{
"_t" : [
"Contact",
"ExternalContact"
],
"Role" : "Product Emergency",
"Name" : "Fred",
"Email" : "fred@gmail",
"Phone" : "1231",
"Availability" : "",
},
{
"_t" : [
"Contact",
"InternalContact"
],
"Role" : "Manager",
"Name" : "Mickey"
}
]
我现在想要的是按类型检索联系人。我们有OfType<TResult>
但它只适用于MongoQueryable。所以,像这样:
public async Task<IEnumerable<string>> GetContacts<TContactType>(CancellationToken token) where TContactType : Contact
{
var collection = _mongoDb.GetCollection<Person>(CollectionNames.Person)();
var projectedListOfContacts =
await collection.Find(
s => s.Contacts != null &&
s.Contacts.OfType<TContactType> // how to filter by type on a property?
)
.Project(dto => dto.Contacts).
.ToListAsync(token);
...
}
无需获取所有联系人并在内存中使用它们的任何方式吗?
相关课程是:
public class Person
{
public Guid Id { get; set; }
public string Name { get; set; }
public IEnumerable<Contact> Contacts {get; set;}
}
public abstract class Contact : IContact
{
public string Role { get; private set; }
public string Name { get; private set; }
protected Contact(string role, string name)
{
Role = role ?? throw new ArgumentNullException(nameof(role));
Name = name ?? throw new ArgumentNullException(nameof(name));
}
}
public class ExternalContact : Contact
{
public string Email { get; private set; }
public string Phone { get; private set; }
public string Availability { get; private set; }
public ExternalContact(string role, string name, string email, string phone, string availability)
: base(role, name)
{
Email = email ?? throw new ArgumentNullException(nameof(email));
Phone = phone ?? throw new ArgumentNullException(nameof(phone));
Availability = availability;
}
}
更新:答案帮我解决了..所以最后我可以这样做:
var contacts =
await Collection.Find(s => s.Contacts != null)
.Project(dto => dto.Contacts.OfType<TContactType>())
.ToListAsync(token);
原来,要求已经改变..并且Contact数组将始终至少有一种类型。所以没有理由过滤..但我利用了只返回我想要的类型的投影。
答案 0 :(得分:0)
这里只是疯狂拍摄,但我相信这可能就是你要找的东西,但如果没有mcve,很难确定。
var nullFilter = Builders<Person<TContactType>>.Filter.Ne(person => person.Contacts, null);
var typeFilter = Builders<Person<TContactType>>.Filter.OfType<Contact, TContactType>(person => person.Contacts);
var combinedFilter = Builders<Person<TContactType>>.Filter.And(nullFilter, typeFilter);
var projectedListOfContact = collection.Find(combinedFilter).Project(dto => dto.Contacts).ToListAsync(token);
更新构建器
var query = Builders<Person>.Filter.ElemMatch(x => x.Contacts, Builders<Contact>.Filter.OfType<ExternalContact>());
collection.Find(query);
当然也可以与您的通用T一起使用。
这应该返回find({ "Contacts" : { "$elemMatch" : { "_t" : "ExternalContact" } } })