C# + MongoDB - 不使用 MongoDB DataTypes/Attributes 的 ObjectId
C# + MongoDB - ObjectId without using MongoDB DataTypes/Attributes
使用 MongoDB 作为我的数据存储使我默认将 ObjectID 类型作为主键。它也可以通过使用带有 [BsonId] 属性的 Guid 来更改。这也在 MongoDB C# 驱动程序库中定义。我想让我的实体独立于数据层。
我可以只使用 属性 的名称 ID 来标识主键吗?我还能尝试什么?
选项 1:坚持使用 BsonId 并使用外观模式
[BsonId] 属性 是用来指示 _id 属性 应该 linked 到特定的 属性。没有办法解决这个问题(除了在您的 crud 操作中完全忽略 _id 这似乎是个坏主意)。
因此,如果您想将 "entity" object 与 "data layer" 分开,则只需使用 poco class.
--用一个poco class代替一条记录。那个class只是为了数据存储:快速获取数据的方法in/out 的 mongo,是处理 bson 文档的绝佳选择。
-- 在实体层 class 的 poco class 之上使用外观 。我觉得 re-invent 轮子没有用,所以我通常会要求我们的开发人员让实体接口继承 data-layer (poco) 接口,但你可以随心所欲地做
分解示例 MyObject class
IMyObjectRecord(在 dal 中声明,仅包含属性和 mongo-specific 属性)
IMyObject:IMyObjectRecord(在实体级别声明,可能包含添加的属性和方法)
MyObjectRecord:IMyObjectRecord(在 dal 内部声明,包含 mongo-specific 属性。如果你想对分离非常严格,可以声明为内部)。
MyObject:IMyObject(例如,可以是您从 dal 中提取的 IMyObjectRecord class 顶部的外观)。
现在 - 您可以获得外观的所有好处,并且属性之间有一个 hard-coded link 但是,您可以将 Bson 属性包含在您的 dal 中。
好的,好的。但我真的很讨厌那个答案。
是的。我可以接受。 好的,那么会议包怎么样? 如果您绝对保证您会调用您的 ID "Id" 并且发誓您会将它们键入字符串(或 -使用其他易于识别的约定),那么我们可以只使用约定包 like the one I stole from here
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());
}
}
}
}
}
什么是会议包
这是在 serialize/deserialize 期间应用的 mongo "rules" 的一小部分。你注册一次(当你设置你的引擎时)。在这种情况下,样本包告诉 mongo "if you see a field called 'Id', then save it as a string to _id, please."
这些会变得非常复杂和有趣。如果您真的非常讨厌其他方法,我会深入研究会议包。这是将所有 mongo "attribute driven" 逻辑强制到一个 self-contained 位置的好方法。
您可以使用 BsonClassMap 而不是使用属性来保持您的 类 "clean".
// '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);
});
我自己也遇到了同样的问题,我不想在我的 classes 中包含 mongo 属性。
我创建了一个小的包装器示例来展示我如何在没有 Id 属性 我的业务逻辑数据 class 的情况下保存和查找元素。
包装器class:
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 的查询:
假设我们有一个数据 class:
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>
来概括,这将使包装器的使用更加方便
如果我没理解错的话。你想把你的实体放到没有属性的其他层。
我想你可以试试这个
public object Id { get; set; }
之后你可以输入来自 mongodb 的不带属性的 ID
使用 MongoDB 作为我的数据存储使我默认将 ObjectID 类型作为主键。它也可以通过使用带有 [BsonId] 属性的 Guid 来更改。这也在 MongoDB C# 驱动程序库中定义。我想让我的实体独立于数据层。 我可以只使用 属性 的名称 ID 来标识主键吗?我还能尝试什么?
选项 1:坚持使用 BsonId 并使用外观模式
[BsonId] 属性 是用来指示 _id 属性 应该 linked 到特定的 属性。没有办法解决这个问题(除了在您的 crud 操作中完全忽略 _id 这似乎是个坏主意)。
因此,如果您想将 "entity" object 与 "data layer" 分开,则只需使用 poco class.
--用一个poco class代替一条记录。那个class只是为了数据存储:快速获取数据的方法in/out 的 mongo,是处理 bson 文档的绝佳选择。
-- 在实体层 class 的 poco class 之上使用外观 。我觉得 re-invent 轮子没有用,所以我通常会要求我们的开发人员让实体接口继承 data-layer (poco) 接口,但你可以随心所欲地做
分解示例 MyObject class
IMyObjectRecord(在 dal 中声明,仅包含属性和 mongo-specific 属性)
IMyObject:IMyObjectRecord(在实体级别声明,可能包含添加的属性和方法)
MyObjectRecord:IMyObjectRecord(在 dal 内部声明,包含 mongo-specific 属性。如果你想对分离非常严格,可以声明为内部)。
MyObject:IMyObject(例如,可以是您从 dal 中提取的 IMyObjectRecord class 顶部的外观)。
现在 - 您可以获得外观的所有好处,并且属性之间有一个 hard-coded link 但是,您可以将 Bson 属性包含在您的 dal 中。
好的,好的。但我真的很讨厌那个答案。
是的。我可以接受。 好的,那么会议包怎么样? 如果您绝对保证您会调用您的 ID "Id" 并且发誓您会将它们键入字符串(或 -使用其他易于识别的约定),那么我们可以只使用约定包 like the one I stole from here
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());
}
}
}
}
}
什么是会议包
这是在 serialize/deserialize 期间应用的 mongo "rules" 的一小部分。你注册一次(当你设置你的引擎时)。在这种情况下,样本包告诉 mongo "if you see a field called 'Id', then save it as a string to _id, please."
这些会变得非常复杂和有趣。如果您真的非常讨厌其他方法,我会深入研究会议包。这是将所有 mongo "attribute driven" 逻辑强制到一个 self-contained 位置的好方法。
您可以使用 BsonClassMap 而不是使用属性来保持您的 类 "clean".
// '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);
});
我自己也遇到了同样的问题,我不想在我的 classes 中包含 mongo 属性。
我创建了一个小的包装器示例来展示我如何在没有 Id 属性 我的业务逻辑数据 class 的情况下保存和查找元素。
包装器class:
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 的查询:
假设我们有一个数据 class:
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>
来概括,这将使包装器的使用更加方便
如果我没理解错的话。你想把你的实体放到没有属性的其他层。
我想你可以试试这个
public object Id { get; set; }
之后你可以输入来自 mongodb 的不带属性的 ID