单独映射内部集合属性,而不是使用 MongoDB 驱动程序和 Automapper 检索整个集合属性 - C# .Net

问题描述 投票:0回答:1

我在获取要按属性投影集合属性而不是从 MongoDB 中的 bson 文档中投影整个字段时遇到了一些麻烦。

以下是

Evt
集合中的文档示例:

[
    {
        "Inner": {
            "RootName" : "FooBar",
            "RootExtra" : "abcdefghijlmnopqrstuvwxyz",
            "Items": [
                {
                    "Val" : 1,
                    "Name" : "Foo",
                    "ExtraProperty" : "abcdefghijlmnopqrstuvwxyz",
                    "InnerItems" : [
                        {
                            "InnerVal" : "Importante thing",
                            "InnerExtra" : "Unimportant thing"
                        }
                    ]
                },
                {
                    "Val" : 2,
                    "Name" : "Bar",
                    "ExtraProperty" : "abcdefghijlmnopqrstuvwxyz",
                    "InnerItems" : [
                        {
                            "InnerVal" : "Importante thing",
                            "InnerExtra" : "Unimportant thing"
                        }
                    ]
                }
            ],
            "Singular" : {
                "Prop1" : "123ABC",
                "Prop2" : "ABC!@#",
                "Prop3" : "Unwanted"
            }
        }
    }
]

这是我的 C# 类模型。值得一提的是,我的模型仅用于读取目的,也就是说写入是由另一个应用程序完成的。此外,文档是非规范化的,因此,我的模型仅包含我绝对确定将在我映射它们的位置的属性。

[DisplayName("Evt")]
public class Evt1
{
    public InnerEvt1 Inner { get; set; }
}

public class InnerEvt1
{
    public List<EvtItem1> Items { get; set; }
    public string RootName { get; set; }
    public Singular Singular { get; set; }
}

public class EvtItem1
{
    public int Val { get; set; }
    public int Name { get; set; }
    public List<InnerItem> InnerItems { get; set; }
}

public class InnerItem
{
    public int InnerVal { get; set; }
    public string InnerName { get; set; }
}

public class Singular
{
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }
}

如您所见,我在模型中遗漏了一些属性(例如

RootExtra
InnerExtra
ExtraProperty
)。所有这些(文档示例和模型)都只是示例,因为在我的实际情况中,我在模型中遗漏了许多沉重的属性,也就是说我不希望它们加载到我的模型中。查询。

如果我只是使用

检索整个集合
database.GetCollection<Evt1>("Evt").AsQueryable()

我的投影看起来像这样:

databaseName.SchemaName.Evt.Aggregate(
    [
        {
            "$match": {
                "Disabled": {
                    "$ne": true
                }
            }
        },
        {
            "$project": {
                "_id": "$_id",
                "Evt": "$Evt",
            }
        }
    ]
)

这意味着在构建模型之前整个文档将被加载到内存中并丢弃剩余/额外的属性。这在性能上会非常糟糕。

因此,我使用 Automapper 为我创建投影 linq/查询:

CreateMap<Evt1, Evt1>();
CreateMap<InnerEvt1, InnerEvt1>();
CreateMap<EvtItem1, EvtItem1>();
CreateMap<InnerItem, InnerItem>();
CreateMap<Singular, Singular>();

查询本身看起来像这样:

database.GetCollection<Evt1>("Evt").AsQueryable().ProjectTo<Evt1>(mapper.ConfigurationProvider);

这使得我的投影看起来像这样:

[
    {
        "$match": {
            "Disabled": {
                "$ne": true
            }
        }
    },
    {
        "$project": {
            "_id": "$_id",
            "Inner": {
                "$cond": {
                    "if": {
                        "$eq": [
                            "$Inner",
                            null
                        ]
                    },
                    "then": null,
                    "else": {
                        "Items": "$Evt.Items",
                        "_id": "$Evt._id",
                        "RootName": "$Evt.RootName",
                        "Singular": {
                            "$cond": {
                                "if": {
                                    "$eq": [
                                        "$Evt.Singular",
                                        null
                                    ]
                                },
                                "then": null,
                                "else": {
                                    "Prop1": "$Evt.Singular.Prop1",
                                    "Prop2": "$Evt.Singular.Prop2"
                                }
                            }
                        }
                    }
                }
            }
        }
    }
]

对于类

Inner
(
RootName
) 和奇异引用类型属性 (
Singular
) 中的值类型属性,投影按预期工作,换句话说,投影其各个值,而不是检索整个块,因此,不会从数据库中获取不需要的值。

问题在于集合属性 (

Items
) 的行为,即使已添加其地图,该集合属性仍被投影为块。

我尝试了几种替代方案,包括:

  1. List<EvtItem1>
    映射到
    List<EvtItem1>
  2. 使用
    ForMember(a => a.Items, opts.MapFrom(a.Items))
  3. EvtItem
    中创建自定义构造函数,因此每个属性都会单独初始化
  4. 创建第二个模型,与第一个模型相同,因此可以映射到

唯一有效的选择是第四个。这是复制的模型:

[DisplayName("Evt")]
public class Evt2
{
    public InnerEvt2 Inner { get; set; }
}

public class InnerEvt2
{
    public List<EvtItem2> Items { get; set; }
    public string RootName { get; set; }
    public Singular Singular { get; set; }
}

public class EvtItem2
{
    public int Val { get; set; }
    public int Name { get; set; }
    public List<InnerItem> InnerItems { get; set; }
}

地图:

CreateMap<EvtBasesTrab, EvtBasesTrab2>();
CreateMap<EvtBasesTrabRoot, EvtBasesTrabRoot2>();
CreateMap<InfoCp, InfoCp2>();

查询:

database.GetCollection<Evt1>("Evt").AsQueryable().ProjectTo<Evt2>(mapper.ConfigurationProvider);

还有投影:

[
    {
        "$match": {
            "Disabled": {
                "$ne": true
            }
        }
    },
    {
        "$project": {
            "_id": "$_id",
            "Inner": {
                "$cond": {
                    "if": {
                        "$eq": [
                            "$Inner",
                            null
                        ]
                    },
                    "then": null,
                    "else": {
                        "Items": {
                            "$map": {
                                "input": "$Inner.Items",
                                "as": "dtoItems",
                                "in": {
                                    "Val": "$$dtoItems.Val",
                                    "Name": "$$dtoItems.Name",
                                    "InnerItems": "$$dtoItems.InnerItems"
                                }
                            }
                        },
                        "_id": "$Evt._id",
                        "RootName": "$Evt.RootName",
                        "Singular": {
                            "$cond": {
                                "if": {
                                    "$eq": [
                                        "$Evt.Singular",
                                        null
                                    ]
                                },
                                "then": null,
                                "else": {
                                    "Prop1": "$Evt.Singular.Prop1",
                                    "Prop2": "$Evt.Singular.Prop2"
                                }
                            }
                        }
                    }
                }
            }
        }
    }
]

主要区别在于

Items
属性的属性是单独映射的,这正是我的意图。问题是,在现实世界中,有太多的类需要重复,所以这个解决方案是不可行的。

有没有人有更好的解决方案?

c# .net mongodb automapper projection
1个回答
0
投票

我相信,无论您如何实现应用程序代码,数据库引擎将始终加载文档(及其所有字段),然后仅过滤到您已投影的字段。您可能需要重新访问您的架构并遵循 MongoDB(和文档数据库)架构设计的最佳实践。

© www.soinside.com 2019 - 2024. All rights reserved.