C# + MongoDB - 不使用MongoDB的DataTypes/Attributes如何操作ObjectId

11
使用MongoDB作为我的数据存储,默认情况下会将ObjectID类型设为主键。也可以通过使用带有[BsonId]属性的Guid进行更改。这也在MongoDB C# Driver库中定义。我想让我的实体与数据层无关。我可以只使用名称为“Id”的属性来标识主键吗?还能尝试什么别的吗?
4个回答

14

您可以使用 BsonClassMap 代替使用属性来保持类的"清洁"。

// 'clean' entity with no mongo attributes
public class MyClass 
{
    public Guid Id { get; set; }
}

// mappings in data layer
BsonClassMap.RegisterClassMap<MyClass>(cm => 
{
    cm.AutoMap();
    cm.MapIdMember(c => c.Id).SetIdGenerator(CombGuidGenerator.Instance);
});

嘿,彼得 - 这只是针对一个类,对吧?它只适用于MyClass?因此,OP需要为添加到数据访问层的每个新类添加一个新的classMap。 - bri
@bri - 没错,说得好 - 这就是你如何流畅地映射单个类的方法。你提出使用 ConventionPack 定义映射所有 Id 属性的约定是一个好例子。 - Peter
你会使用哪个?只是好奇——我个人喜欢属性,因为它清楚地告诉整个团队每个 POCO 如何映射到持久层。 - bri

7

选择1:坚持使用BsonId并使用外观模式

[BsonId]属性是用来指示_id属性应链接到特定属性的。没有其他方法可以绕过这个问题(除了完全忽略crud操作中的_id,这似乎是一个不好的想法)。

所以,如果你想将“实体”对象与“数据层”分开,那么就使用poco类。

-- 使用poco类代替记录。该类仅用于数据存储:一种快速获取mongo数据的方法,以及与bson文档一起使用的优秀替代方案。

-- 在该poco类之上使用facade作为你的实体层。我认为重新发明轮子没什么用,所以我通常要求我们的开发人员让实体接口继承数据层(poco)接口,但你可以按自己的喜好选择。

拆分示例MyObject类

IMyObjectRecord(在dal中声明,仅包含属性和mongo特定属性)

IMyObject:IMyObjectRecord(在实体级别声明,可能包括添加的属性和方法)

MyObjectRecord:IMyObjectRecord(在dal内部声明,包含mongo特定属性。如果你想非常严格地分离,可以将其声明为internal)。

MyObject:IMyObject(例如,在你从dal中获取的IMyObjectRecord类之上可能是一个facade)。

现在-你可以获得facade的所有好处,并且你有属性之间的硬链接,但是你可以在dal中保留Bson属性。

好吧,但我真的非常非常非常讨厌这个答案。

是的,我能理解。 那么怎么样使用Convention Pack呢?如果你绝对保证将你的Id命名为“Id”,并且你发誓将它们键入为字符串(或者--使用其他易于识别的约定),那么我们可以使用像我从这里窃取的那个convention pack

namespace ConsoleApp {
    class Program {

        private class Foo {
            // Look Ma!  No attributes!
            public string Id { get; set; }
            public string OtherProperty { get; set; }
        }

        static void Main(string[] args) {

            //you would typically do this in the singleton routine you use 
            //to create your dbClient, so you only do it the one time.
            var pack = new ConventionPack();   
            pack.Add(new StringObjectIdConvention());
            ConventionRegistry.Register("MyConventions", pack, _ => true);
            // Note that we registered that before creating our client...
            var client = new MongoClient();

            //now, use that client to create collections
            var testDb = client.GetDatabase("test");
            var fooCol = testDb.GetCollection<Foo>("foo");
            fooCol.InsertOne(new Foo() { OtherProperty = "Testing", Id="TEST" });

            var foundFoo = fooCol.Find(x => x.OtherProperty == "Testing").ToList()[0];
            Console.WriteLine("foundFooId: " + foundFoo.Id);
       }

        //obviously, this belongs in that singleton namespace where
        //you're getting your db client.
        private class StringObjectIdConvention : ConventionBase, IPostProcessingConvention {
            public void PostProcess(BsonClassMap classMap) {
                var idMap = classMap.IdMemberMap;
                if (idMap != null && idMap.MemberName == "Id" && idMap.MemberType == typeof(string)) {
                    idMap.SetIdGenerator(new StringObjectIdGenerator());
                }
            }
        }
    }
}

什么是约定包

它是一组小的mongo“规则”,在序列化/反序列化期间应用。您只需在设置引擎时注册一次。在这种情况下,示例包告诉mongo“如果您看到一个名为'Id'的字段,请将其保存为字符串到_id中。”

这些可以变得非常复杂和有趣。如果您真的非常讨厌其他方法,我建议深入研究约定包。这是将所有mongo“属性驱动”逻辑强制集成到一个自包含位置的好方法。


我决定采用:-- 使用 poco 类作为记录的替代。 - skalinkin
在我看来,约定包解决方案是最好的。MongoDb-Client提供了许多内置的约定包,为什么不添加自己的呢? - alsami

3

如果我理解正确,您想将实体放入其他层中而不带属性。

我认为您可以尝试这样做:

 public object Id { get; set; }

之后,您可以将来自MongoDB的ID放置在没有属性的位置。


3

我自己曾经遇到同样的问题,不想在我的类中使用mongo属性。

我创建了一个小的包装器示例来展示如何在没有数据类的Id属性的情况下保存和查找元素。

包装器类:

public static class Extensions
{
    public static T Unwrap<T>(this MongoObject<T> t)
    {
        return t.Element;
    }
}
public class MongoObject<T>
{
    [BsonId]
    private ObjectId _objectId;
    public T Element { get; }

    public MongoObject(T element)
    {
        Element = element;
        _objectId = new ObjectId();
    }
}

我还添加了一个扩展方法来轻松解包。
保存元素很简单。
public void Save<T>(T t)
{
    _collection.InsertOne(new MongoObject<T>(t));
}

为了查找元素,我们可以进行类似LINQ的查询:

假设我们有一个数据类:

public class Person
{
    public string Name { get; set; }
}

那么我们可以通过以下方式找到这个元素:

 public Person FindPersonByName(string name)
 {
     return _collection.AsQueryable().FirstOrDefault(
               personObject => personObject.Element.Name == name).Unwrap();
 }

我们还可以通过让MongoObject 实现 IQueryable<T> 接口来进行概括,这将使包装器的使用更加方便。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接