2017-08-08 22 views
5
我有保溼它由一個複合ID這反過來又具有基類中的一個問題

,我收到一個錯誤說InvalidOperationException: {document}.Identity is not supported.MongoDB的複合鍵:InvalidOperationException異常:不支持{}文件.Identity

我試圖寫入到數據庫中的類是如下:

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其低於複合值的形成(VendorIdSku本地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); 

不知道這是否是現在過於由我從複雜,我的實體層堅持直接(或如果我只是使用automapper和簡單的POCO)...或者,如果我錯過了一些映射指令。

欣賞任何幫助或指針。

+0

不知道'toString()'假設在這裏做什麼。你可以添加「實體」對象的職位?我正在通過構造函數post來查看通過'GetProperties'完成的水合作用。所以'public readonly Sku Sku;'不會通過'classMap.ClassType.GetTypeInfo() .GetProperties(_bindingFlags)'bcoz顯示出它只能被成員字段訪問。你可以把它改成公共Sku Sku {get; }'所以它通過'GetProperties'通過構造函數進行水合,並將所有隻讀('Sku - VendorId,Value'&'VendorId - Value'字段)更改爲屬性getter方法。 – Veeram

+0

也不知道爲什麼你有額外的'身份身份=> Sku'字段,可能是你可以改變'ReplaceOneAsync(x => x.Sku.Equals(entity.Sku),實體,新的UpdateOptions(){IsUpsert =真})'。在完成所有提到的更改後,我能夠將複合鍵ID與其他字段序列化。 – Veeram

+0

感謝Veeram花時間來幫助:)我正在嘗試製作一個通用的存儲庫基類。身份屬性由我的DDD框架的其他部分使用。在代碼中包含了IEntity 類型。如果我使它過於複雜的基礎知識庫類,我可以放棄,並堅持使用插入,因爲你所提到的是在每個存儲庫的基礎上工作。 – g18c

回答

3

我一直在尋找通過構造後水化這是通過GetProperties完成。

因此public readonly Sku Sku;不會通過classMap.ClassType.GetTypeInfo().GetProperties(_bindingFlags)顯示,因爲它只能作爲成員字段訪問。

你可以把它改成public Sku Sku { get; }所以通過GetProperties通過構造水合和更改所有的只讀域(Sku - VendorId, Value & VendorId - Value域)擁有財產性getter方法。

而且,你已經加入,因爲它不是一個構造ARG自動映射邏輯運行時,爲表達時,因爲Identity不能水合,並通過ImmutablePocoConvention註冊cm.MapProperty(c => c.Identity)所以x=>x.Identity.Equals(entity.Identity)可以被序列化。

代碼更改:

public class Sku : Identity<Product> 
{ 
    public VendorId VendorId { get; } 
    public string Value { get; } 
} 

public class VendorId : Identity<Vendor> 
{ 
    public string Value { get; } 
} 

BsonClassMap.RegisterClassMap<Product>(cm => 
{ 
    cm.AutoMap(); 
    cm.MapIdMember(c => c.Sku); 
    cm.MapProperty(c => c.Identity); 
}); 
+0

非常感謝你!你幫我對代碼進行排序,少許調整,最後用於產品映射'cm.MapCreator(c => new Product(c.Sku,c.Name,c.IsArchived));'和VendorID映射'cm (c => new VendorId(c.VendorShortname));'和Sku映射'cm.MapCreator(c =>新的Sku(新的VendorId(c.VendorId.VendorShortname),c.SkuValue));'正在完美工作。最後,通用的基礎倉庫不值得,save方法減少到一行(獲得集合後)。 – g18c

+0

已經彈出我的代碼作爲答案,所以你可以看到我做了什麼。再次感謝您的幫助! – g18c

1

這裏是我使用的代碼:

public class ProductMongoRepository : IProductRepository 
{ 
    public ICollection<Product> SearchBySkuValue(string sku) 
    { 
     return ProductsMongoDatabase.Instance.GetEntityList<Product>(); 
    } 

    public Product GetBySku(Sku sku) 
    { 
     var collection = ProductsMongoDatabase.Instance.GetCollection<Product>(); 

     return collection.Find(x => x.Sku.Equals(sku)).First(); 
    } 

    public void SaveAll(IEnumerable<Product> products) 
    { 
     foreach (var product in products) 
     { 
      Save(product); 
     } 
    } 

    public void Save(Product product) 
    { 
     var collection = ProductsMongoDatabase.Instance.GetCollection<Product>(); 

     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<Sku>(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<VendorId>(cm => 
     { 
      cm.MapField(c => c.VendorShortname); 
      cm.MapCreator(c => new VendorId(c.VendorShortname)); 
     }); 

     BsonClassMap.RegisterClassMap<Product>(cm => 
     { 
      cm.AutoMap(); 
      cm.MapIdMember(c => c.Sku); 
      cm.MapCreator(c => new Product(c.Sku, c.Name, c.IsArchived)); 
     }); 

     BsonClassMap.RegisterClassMap<Vendor>(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<T> GetEntityList<T>() 
    { 
     return GetCollection<T>() 
       .Find(new BsonDocument()).ToList<T>(); 
    }   

    public IMongoCollection<T> GetCollection<T>() 
    { 
     return this.Database.GetCollection<T>(typeof(T).FullName); 
    } 
} 

我的SKU域模型:

public class Sku : Identity<Product> 
{ 
    public readonly VendorId VendorId; 
    public readonly string SkuValue; 

    public Sku(VendorId vendorId, string skuValue) 
    { 
     VendorId = vendorId; 
     SkuValue = skuValue; 
    } 

    protected override IEnumerable<object> GetIdentityComponents() 
    { 
     return new object[] {VendorId, SkuValue}; 
    } 
} 

我的產品域模型:

public class Product : IEntity<Product> 
{ 
    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<Product> Identity => Sku; 
} 

我的賣方ID:

public class VendorId : Identity<Vendor> 
{ 
    public readonly string VendorShortname; 

    public VendorId(string vendorShortname) 
    { 
     VendorShortname = vendorShortname; 
    } 

    protected override IEnumerable<object> GetIdentityComponents() 
    { 
     return new object[] {VendorShortname}; 
    } 
} 

然後我有我的實體和身份類型:

public interface IEntity<T> 
{ 
    Identity<T> Identity { get; } 
} 

public abstract class Identity<T> : IEquatable<Identity<T>> 
{ 
    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<T>; 
     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<object> GetIdentityComponents(); 

    public override int GetHashCode() 
    { 
     return HashCodeHelper.CombineHashCodes(GetIdentityComponents()); 
    } 

    public bool Equals(Identity<T> other) 
    { 
     return Equals(other as object); 
    } 
}