使用MongoDB作为我的数据存储使我默认将ObjectID类型作为主键。它也可以通过使用Guid和[BsonId]属性来更改。这也是在MongoDB C#驱动程序库中定义的。我想让我的实体独立于数据层。我可以只使用名称ID来识别主键吗?我还能尝试什么?
[BsonId]属性是用于指示_id属性应链接到特定属性的属性。没有办法解决这个问题(完全忽略了你的crud操作中的_id似乎是一个坏主意)。
因此,如果您想将“实体”对象与“数据层”分开,则只需使用poco类。
- 使用poco class代替记录。该类仅用于数据存储:快速获取/输出mongo的数据,以及使用bson文档的绝佳替代方法。
- 在poco类的顶部使用外观作为实体层。我发现重新发明轮子没有用,所以我通常会问我们的开发人员有实体接口继承数据层(poco)界面,但是你可以这样做但是你喜欢
IMyObjectRecord(在dal中声明,仅包含属性和特定于mongo的属性)
IMyObject:IMyObjectRecord(在实体级别声明,可能包含添加的属性和方法)
MyObjectRecord:IMyObjectRecord(在dal中声明,包含特定于mongo的属性。如果你想对分离非常严格,可以声明为内部)。
MyObject:IMyObject(例如,可以是从dal拉出的IMyObjectRecord类之上的外观)。
现在 - 您可以获得外观的所有好处,并且您在属性之间有一个硬编码链接但是您可以保持dal中包含的Bson属性。
是啊。我可以接受。好的,那么约会呢?如果你绝对承诺你会称你的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());
}
}
}
}
}
这是在序列化/反序列化期间应用的一小组mongo“规则”。您注册一次(当您设置引擎时)。在这种情况下,样本包告诉mongo“如果你看到一个名为'Id'的字段,那么请将它保存为字符串给_id。”
这些可以变得非常复杂和有趣。如果你真的真的讨厌另一种方法,我会深入了解会议包。这是将所有mongo“属性驱动”逻辑强制转换为一个自包含位置的好方法。
您可以使用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);
});
我自己偶然发现了同样的问题,我不想在我的课程中使用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>
来概括,这将使包装器的使用更加方便
如果我理解正确的话。您希望将实体放在没有属性的其他层。
我想你可以尝试一下
public object Id { get; set; }
在那之后你可以把你的Id来自mongodb没有属性