MongoDB复合键:InvalidOperationException:{document} .Identity不受支持

我遇到了一个问题,即为一个由复合ID组成的类进行保护,而该复合ID又有一个基类,我收到的错误是InvalidOperationException: {document}.Identity is not supported.

我试图写入数据库的类如下:

 public class Product : IEntity { public readonly Sku Sku; public string Name { get; private set; } public string Description { get; private set; } public bool IsArchived { get; private set; } public Identity Identity => Sku; public Product(Sku sku, string name, bool isArchived) { Sku = sku; Name = name; IsArchived = isArchived; } } public interface IEntity { Identity Identity { get; } } 

反过来有一个ID Sku ,它是由以下复合值( VendorIdVendorId的本地Value )组成的类:

 public class Sku : Identity { public readonly VendorId VendorId; public readonly string Value; public Sku(VendorId vendorId, string value) { VendorId = vendorId; Value = value; } protected override IEnumerable GetIdentityComponents() { return new object[] {VendorId, Value}; } } public class VendorId : Identity { public readonly string Value; public VendorId(string value) { Value = value; } protected override IEnumerable GetIdentityComponents() { return new object[] {Value}; } } 

我有我的实体Identity的基类,我在我的DDD库中使用,基本上这里的ToString()输出可以用作ID,如果这会简化事情:

 public abstract class Identity : IEquatable<Identity> { public override bool Equals(object obj) { /* snip */ } public bool Equals(Identity other) { /* snip */ } public override int GetHashCode() { /* snip */ } public override string ToString() { var id = string.Empty; foreach (var component in GetIdentityComponents()) { if (string.IsNullOrEmpty(id)) id = component.ToString(); // first item, dont add a divider else id += "." + component; } return id; } protected abstract IEnumerable GetIdentityComponents(); } 

我在app start上注册了映射:

 // rehydrate readonly properties via matched constructor // https://stackoverflow.com/questions/39604820/serialize-get-only-properties-on-mongodb ConventionRegistry .Register(nameof(ImmutablePocoConvention), new ConventionPack { new ImmutablePocoConvention() }, _ => true); BsonClassMap.RegisterClassMap(cm => { cm.AutoMap(); cm.MapIdMember(c => c.Sku); }); BsonClassMap.RegisterClassMap(cm => { cm.AutoMap(); cm.MapIdMember(c => c.Id); }); 

但是当我去写时,我得到InvalidOperationException: {document}.Identity is not supported.

 // my respositoru method public void Upsert(T entity) where T : IEntity { this.Database .GetCollection(product.GetType().FullName)() .ReplaceOneAsync(x=>x.Identity.Equals(entity.Identity), entity, new UpdateOptions() {IsUpsert = true}) .Wait(); } var product = new Product(new Sku(new VendorId("dell"), "12434" ),"RAM", false ); myProductRepo.Upsert(product); 

不确定这是否因为我直接从我的实体层持久化而过于复杂(或者如果我只使用自动化器和更简单的POCO)……或者如果我缺少一些映射指令。

感谢任何帮助或指针。

我正在通过构造函数post查看水合作用,这是通过GetProperties完成的。

所以public readonly Sku Sku; 不会通过classMap.ClassType.GetTypeInfo().GetProperties(_bindingFlags)因为它只能作为成员字段访问。

您可以将其更改为public Sku Sku { get; } 所以它通过构造函数通过GetPropertiesSku - VendorId, Value更改所有只读字段( Sku - VendorId, ValueVendorId - Value字段)以具有属性getter方法。

此外,您还要添加cm.MapProperty(c => c.Identity)因此当用作表达式时, x=>x.Identity.Equals(entity.Identity)可以序列化,因为Identity不能通过ImmutablePocoConvention进行水合和注册,因为它当automap逻辑运行时,它不是构造函数arg。

代码更改:

 public class Sku : Identity { public VendorId VendorId { get; } public string Value { get; } } public class VendorId : Identity { public string Value { get; } } BsonClassMap.RegisterClassMap(cm => { cm.AutoMap(); cm.MapIdMember(c => c.Sku); cm.MapProperty(c => c.Identity); }); 

这是我使用的代码:

 public class ProductMongoRepository : IProductRepository { public ICollection SearchBySkuValue(string sku) { return ProductsMongoDatabase.Instance.GetEntityList(); } public Product GetBySku(Sku sku) { var collection = ProductsMongoDatabase.Instance.GetCollection(); return collection.Find(x => x.Sku.Equals(sku)).First(); } public void SaveAll(IEnumerable products) { foreach (var product in products) { Save(product); } } public void Save(Product product) { var collection = ProductsMongoDatabase.Instance.GetCollection(); collection .ReplaceOneAsync( x => x.Sku.Equals(product.Sku), product, new UpdateOptions() { IsUpsert = true }) .Wait(); } } 

在这里设置映射并通过构造函数支持只读字段,对于更复杂的场景和手动POCO映射,我们可以使用BsonSerializer.RegisterSerializer(typeof(DomainEntityClass), new CustomerSerializer());

 public sealed class ProductsMongoDatabase : MongoDatabase { private static volatile ProductsMongoDatabase instance; private static readonly object SyncRoot = new Object(); private ProductsMongoDatabase() { BsonClassMap.RegisterClassMap(cm => { cm.MapField(c => c.VendorId); cm.MapField(c => c.SkuValue); cm.MapCreator(c => new Sku(new VendorId(c.VendorId.VendorShortname), c.SkuValue)); }); BsonClassMap.RegisterClassMap(cm => { cm.MapField(c => c.VendorShortname); cm.MapCreator(c => new VendorId(c.VendorShortname)); }); BsonClassMap.RegisterClassMap(cm => { cm.AutoMap(); cm.MapIdMember(c => c.Sku); cm.MapCreator(c => new Product(c.Sku, c.Name, c.IsArchived)); }); BsonClassMap.RegisterClassMap(cm => { cm.AutoMap(); cm.MapIdMember(c => c.Id); cm.MapCreator(c => new Vendor(c.Id, c.Name)); }); } public static ProductsMongoDatabase Instance { get { if (instance != null) return instance; lock (SyncRoot) { if (instance == null) instance = new ProductsMongoDatabase(); } return instance; } } } 

上面的实现(单例)派生自以下基础(任何查询或写入都在父实现中完成):

 public abstract class MongoDatabase { private readonly IConfigurationRepository _configuration; private readonly IMongoClient Client; private readonly IMongoDatabase Database; protected MongoDatabase() { //_configuration = configuration; var connection = "mongodb://host:27017"; var database = "test"; this.Client = new MongoClient(); this.Database = this.Client.GetDatabase(database); } public List GetEntityList() { return GetCollection() .Find(new BsonDocument()).ToList(); } public IMongoCollection GetCollection() { return this.Database.GetCollection(typeof(T).FullName); } } 

我的Sku域名模型:

 public class Sku : Identity { public readonly VendorId VendorId; public readonly string SkuValue; public Sku(VendorId vendorId, string skuValue) { VendorId = vendorId; SkuValue = skuValue; } protected override IEnumerable GetIdentityComponents() { return new object[] {VendorId, SkuValue}; } } 

我的产品域模型:

 public class Product : IEntity { public readonly Sku Sku; public string Name { get; private set; } public bool IsArchived { get; private set; } public Product(Sku sku, string name, bool isArchived) { Sku = sku; Name = name; IsArchived = isArchived; } public void UpdateName(string name) { Name = name; } public void UpdateDescription(string description) { Description = description; } public void Archive() { IsArchived = true; } public void Restore() { IsArchived = false; } // this is used by my framework, not MongoDB public Identity Identity => Sku; } 

我的VendorID:

 public class VendorId : Identity { public readonly string VendorShortname; public VendorId(string vendorShortname) { VendorShortname = vendorShortname; } protected override IEnumerable GetIdentityComponents() { return new object[] {VendorShortname}; } } 

然后我有我的实体和身份类型:

 public interface IEntity { Identity Identity { get; } } public abstract class Identity : IEquatable> { private const string IdentityComponentDivider = "."; public override bool Equals(object obj) { if (ReferenceEquals(this, obj)) return true; if (ReferenceEquals(null, obj)) return false; if (GetType() != obj.GetType()) return false; var other = obj as Identity; return other != null && GetIdentityComponents().SequenceEqual(other.GetIdentityComponents()); } public override string ToString() { var id = string.Empty; foreach (var component in GetIdentityComponents()) { if (string.IsNullOrEmpty(id)) id = component.ToString(); // first item, dont add a divider else id += IdentityComponentDivider + component; } return id; } protected abstract IEnumerable GetIdentityComponents(); public override int GetHashCode() { return HashCodeHelper.CombineHashCodes(GetIdentityComponents()); } public bool Equals(Identity other) { return Equals(other as object); } }