【发布时间】:2017-08-08 23:34:13
【问题描述】:
我在对包含复合 ID 的类进行补水时遇到问题,该复合 ID 又具有基类,我收到一条错误消息,提示 InvalidOperationException: {document}.Identity is not supported.
我要写入数据库的类如下:
public class Product : IEntity<Product>
{
public readonly Sku Sku;
public string Name { get; private set; }
public string Description { get; private set; }
public bool IsArchived { get; private set; }
public Identity<Product> Identity => Sku;
public Product(Sku sku, string name, bool isArchived)
{
Sku = sku;
Name = name;
IsArchived = isArchived;
}
}
public interface IEntity<T>
{
Identity<T> Identity { get; }
}
依次具有 ID Sku,它是由以下复合值(VendorId 和 Sku 中的本地 Value)组成的类:
public class Sku : Identity<Product>
{
public readonly VendorId VendorId;
public readonly string Value;
public Sku(VendorId vendorId, string value)
{
VendorId = vendorId;
Value = value;
}
protected override IEnumerable<object> GetIdentityComponents()
{
return new object[] {VendorId, Value};
}
}
public class VendorId : Identity<Vendor>
{
public readonly string Value;
public VendorId(string value)
{
Value = value;
}
protected override IEnumerable<object> GetIdentityComponents()
{
return new object[] {Value};
}
}
我的实体Identity 有一个基类,我在我的 DDD 库中使用它,本质上,这里的 ToString() 输出可以用作 ID,如果这样可以简化事情的话:
public abstract class Identity<T> : IEquatable<Identity<T>>
{
public override bool Equals(object obj) { /* snip */ }
public bool Equals(Identity<T> 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<object> GetIdentityComponents();
}
我在应用启动时注册了映射:
// 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<Product>(cm =>
{
cm.AutoMap();
cm.MapIdMember(c => c.Sku);
});
BsonClassMap.RegisterClassMap<Vendor>(cm =>
{
cm.AutoMap();
cm.MapIdMember(c => c.Id);
});
但是当我去写作时,我得到InvalidOperationException: {document}.Identity is not supported.
// my respositoru method
public void Upsert<T>(T entity) where T : IEntity<T>
{
this.Database
.GetCollection<T>(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)......或者我是否缺少一些映射指令。
感谢任何帮助或指点。
【问题讨论】:
-
不确定
toString()应该在这里做什么。您可以将entity对象添加到帖子中吗?我正在通过GetProperties完成的构造函数帖子查看水合作用。所以public readonly Sku Sku;不会通过classMap.ClassType.GetTypeInfo() .GetProperties(_bindingFlags)显示出来,因为它只能作为成员字段访问。您可以将其更改为public Sku Sku { get; },以便通过GetProperties的构造函数对其进行水合,并将所有只读(Sku - VendorId, Value和VendorId - Value字段)更改为具有属性getter 方法。 -
也不确定为什么你有额外的
Identity<Product> Identity => Sku字段,可能你可以更改为ReplaceOneAsync(x=>x.Sku.Equals(entity.Sku), entity, new UpdateOptions() {IsUpsert = true})。在进行所有提到的更改后,我能够将复合键 ID 与其他字段序列化。 -
感谢 Veeram 花时间提供帮助 :) 我正在尝试创建一个通用存储库基类。我的 DDD 框架的其他部分使用了 Identity 属性。已在代码中包含 IEntity
类型。如果我使用基础存储库类使其过于复杂,我可以放弃它并坚持插入,因为您提到的是在每个存储库的基础上工作。 -
Np。你的方法看起来不错。您可以尝试添加
cm.MapProperty(c => c.Identity) so x=>x.Identity.Equals(entity.Identity)可以在用作表达式时序列化 bcozIdentity不能通过ImmutablePocoConvention进行水合和注册,因为它不是构造函数arg。
标签: c# mongodb composite-primary-key