字段上的MongoDB Linq OfType()

时间:2017-06-19 12:15:13

标签: c# mongodb linq

这是我的MongoDB文档结构:

{
    string _id;
    ObservableCollection<DataElement> PartData;
    ObservableCollection<DataElement> SensorData;
    ... 
    other ObservableCollection<DataElement> fields
    ...
    other types and fields
    ...
}

是否有可能检索类型为ObservableCollection<DataElement>的字段串联?使用LINQ我会做类似

的事情
var query = dbCollection
  .AsQueryable()
  .Select(x => new { 
     data = x
       .OfType(typeof(ObservableCollection<DataElement>))
       .SelectMany(x => x)
       .ToList()
   });

或者

data = x.Where(y => typeof(y) == typeof(ObservableCollection<DataElement>)
.SelectMany(x => x).ToList()

不幸的是.Where().OfType()不能在文档上工作,只能在可查询/列表上工作,那么还有另一种可能吗?文档结构必须保持不变。

修改 在无知的回答之后,我尝试了方法1b),这非常适合以他们在集合中的方式获取字段。谢谢!

不幸的是,它并不是我想要的,因为我希望将所有那些特定类型的字段放在一个列表中,它将由OfType或{{返回1}}陈述。

e.g。数据为Where(typeof)的{​​{1}},以便我可以使用SelectMany()来最终获得所有序列的连接。

很抱歉不准确地提出问题而不包括做data = [x.PartData , x.SensorData, ...]的最后一步

最后我找到了一个解决方案,但它对我来说似乎并不优雅,因为它需要一个ObsverableCollection<DataElement>[]来表示每一个元素(而且我有更多的元素)它需要制作一个查找不存在的字段时的新集合:

SelectMany()/Concat()

1 个答案:

答案 0 :(得分:0)

为了限制返回的字段,您需要以某种方式使用MongoDB Projection feature

根据您的具体要求,我可以考虑以下几种方法:

选项1a (相当静态的方法):创建一个自定义类型,如果您事先了解它们,则只包含您感兴趣的字段。像这样:

public class OnlyWhatWeAreInterestedIn
{
    public ObservableCollection<DataElement> PartData { get; set; }
    public ObservableCollection<DataElement> SensorData { get; set; }
    // ...
}

然后您可以像这样查询您的收藏:

var collection = new MongoClient().GetDatabase("test").GetCollection<OnlyWhatWeAreInterestedIn>("test");
var result = collection.Find(FilterDefinition<OnlyWhatWeAreInterestedIn>.Empty);

使用这种方法可以获得一个很好的类型结果,而无需自定义投影。

选项1b (仍然非常静态):选项1a的一个小变体,只是没有新的显式类型,而是投影阶段而不是限制返回的字段。有点像:

var collection = new MongoClient().GetDatabase("test").GetCollection<Test>("test");
var result = collection.Find(FilterDefinition<Test>.Empty).Project(t => new { t.PartData, t.SensorData }).ToList();

同样,你会得到一个很好的C#实体,你可以继续操作。

选项2:使用一些暗反射魔法来动态创建投影阶段。缺点:你不会得到一个反映你的属性的类型实例,而是一个BsonDocument,所以你必须在之后处理它。此外,如果您有任何自定义MongoDB映射,则需要添加一些代码来处理它们。

以下是完整的示例代码:

首先,您的实体:

public class Test
{
    string _id;
    public ObservableCollection<DataElement> PartData { get; set; }
    public ObservableCollection<DataElement> SensorData { get; set; }
    // just to have one additional property that will not be part of the returned document
    public string TestString { get; set; }
}

public class DataElement
{
}

然后是测试程序:

public class Program
{
    static void Main(string[] args)
    {
        var collection = new MongoClient().GetDatabase("test").GetCollection<Test>("test");

        // insert test record
        collection.InsertOne(
            new Test
            {
                PartData = new ObservableCollection<DataElement>(
                    new ObservableCollection<DataElement>
                    {
                        new DataElement(),
                        new DataElement()
                    }),
                SensorData = new ObservableCollection<DataElement>(
                    new ObservableCollection<DataElement>
                    {
                        new DataElement(),
                        new DataElement()
                    }),
                TestString = "SomeString"
            });

        // here, we use reflection to find the relevant properties
        var allPropertiesThatWeAreLookingFor = typeof(Test).GetProperties().Where(p => typeof(ObservableCollection<DataElement>).IsAssignableFrom(p.PropertyType));

        // create a string of all properties that we are interested in with a ":1" appended so MongoDB will return these fields only
        // in our example, this will look like
        // "PartData:1,SensorData:1"
        var mongoDbProjection = string.Join(",", allPropertiesThatWeAreLookingFor.Select(p => $"{p.Name}:1"));

        // we do not want MongoDB to return the _id field because it's not of the selected type but would be returned by default otherwise
        mongoDbProjection += ",_id:0";

        var result = collection.Find(FilterDefinition<Test>.Empty).Project($"{{{mongoDbProjection}}}").ToList();

        Console.ReadLine();
    }
}