2016-07-05 17 views
0

Mongoには3つのコレクションがあります:flavorcolor、およびcupcakeです。各コレクションは、独自の_id(明らかに)持っているとcupcakeコレクションはそうのように、flavorcupcake_id秒を参照していますC#で型安全なMongoオブジェクトIDを取得するには?

{ 
    "_id": ObjectId("123"), 
    "flavorId": ObjectId("234"), 
    "colorId": ObjectId("345"), 
    "moreData": {} 
} 

は、これは当然のことながら、おもちゃの一例であり、これらのコレクションに多くのものがあります。それはこの質問にとって重要ではありませんが、私が照会するときに実際に探しているのはmoreDataです。

cupcakeオブジェクトをflavorIdcolorIdで検索できます(これらの検索には適切なインデックスが付けられています)。しかし、両方のフィールドがObjectIdであるので、私は誰かが偶然colorIdflavorIdと探してしまうのを避けたいと思います。 colorIdflavorIdが異なるタイプになるようにオブジェクトとリポジトリクラスを設計するにはどうすればいいですか?コンパイラはそれらを交換することはできませんが、両方のIDをObjectIdとして保存しますか?

私の最初の考えは、ObjectIdを拡張して拡張オブジェクトを渡すことでしたが、ObjectIdは拡張できないstructです。

+0

これは少し冗長なようですか?*誰かが間違って色味を探しているのを避けたいのです* *。誰かがバグを導入することを許可することはできません。 – Liam

+0

この特定のバグを導入するのは簡単かもしれません。リポジトリオブジェクトを持っていて、 "すべての' cupcake'オブジェクトを 'flavorId'で取得します。したがって、IntelliSenseが非常に有益に示唆し、引数名を読まなかったため、cupcakeRepository.Find(ObjectId flavorId)を呼び出します。ねえ、それはコンパイル!あなたが思ったことに従ってメソッドを嘲笑したので、あなたの単体テストも合格します。 – meustrus

+0

または 'cupcakeRepository.Find(ObjectId colorId)'です。私は実際にそれらの2つの文章を入力する間にどちらを使用しようとしているのか忘れていました。これは他の開発者だけではなく、自分自身から私を守るためです。 – meustrus

答えて

0

だから私はかむことになったようにリポジトリを拡張するように、追加のパラメータを渡す代わりに、ベースオブジェクトを変更する強制可能性を念頭にこれを持ちますこのためにMongo固有の迷惑メールを作成して、カスタムクラスを作成してください。だからここに私のドロップインのObjectIdの代替です:

public struct DocumentId<T> : IEquatable<DocumentId<T>> 
{ 
    static DocumentId() 
    { 
     BsonSerializer.RegisterSerializer(typeof(DocumentId<T>), DocumentIdSerializer<T>.Instance); 
     BsonSerializer.RegisterIdGenerator(typeof(DocumentId<T>), DocumentIdGenerator<T>.Instance); 
    } 

    public static readonly DocumentId<T> Empty = new DocumentId<T>(ObjectId.Empty); 
    public readonly ObjectId Value; 

    public DocumentId(ObjectId value) 
    { 
     Value = value; 
    } 

    public static DocumentId<T> GenerateNewId() 
    { 
     return new DocumentId<T>(ObjectId.GenerateNewId()); 
    } 

    public static DocumentId<T> Parse(string value) 
    { 
     return new DocumentId<T>(ObjectId.Parse(value)); 
    } 

    public bool Equals(DocumentId<T> other) 
    { 
     return Value.Equals(other.Value); 
    } 

    public override bool Equals(object obj) 
    { 
     if (ReferenceEquals(null, obj)) return false; 
     return obj is DocumentId<T> && Equals((DocumentId<T>)obj); 
    } 

    public static bool operator ==(DocumentId<T> left, DocumentId<T> right) 
    { 
     return left.Value == right.Value; 
    } 

    public static bool operator !=(DocumentId<T> left, DocumentId<T> right) 
    { 
     return left.Value != right.Value; 
    } 

    public override int GetHashCode() 
    { 
     return Value.GetHashCode(); 
    } 

    public override string ToString() 
    { 
     return Value.ToString(); 
    } 
} 

public class DocumentIdSerializer<T> : StructSerializerBase<DocumentId<T>> 
{ 
    public static readonly DocumentIdSerializer<T> Instance = new DocumentIdSerializer<T>(); 

    public override DocumentId<T> Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) 
    { 
     return new DocumentId<T>(context.Reader.ReadObjectId()); 
    } 

    public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, DocumentId<T> value) 
    { 
     context.Writer.WriteObjectId(value.Value); 
    } 
} 

public class DocumentIdGenerator<T> : IIdGenerator 
{ 
    public static readonly DocumentIdGenerator<T> Instance = new DocumentIdGenerator<T>(); 

    public object GenerateId(object container, object document) 
    { 
     return DocumentId<T>.GenerateNewId(); 
    } 

    public bool IsEmpty(object id) 
    { 
     var docId = id as DocumentId<T>? ?? DocumentId<T>.Empty; 
     return docId.Equals(DocumentId<T>.Empty); 
    } 
} 

型パラメータTは何もすることができます。それは決して使用されません。それはそうと同じように、あなたのオブジェクトの型でなければなりません:

public class Cupcake { 
    [BsonId] 
    public DocumentId<Cupcake> Id { get; set; } 
    // ... 
} 

この方法で、あなたのFlavorクラスはタイプDocumentId<Flavor>のIDを持っており、あなたのColorクラスがタイプDocumentId<Color>のIDを持っていない、と決して2を入れ替えなければなりません。今、私は同様に、以下の明確な方法でCupcakeRepositoryを作成することができます。

public interface ICupcakeRepository { 
    IEnumerable<Cupcake> Find(DocumentId<Flavor> flavorId); 
    IEnumerable<Cupcake> Find(DocumentId<Color> colorId); 
} 

をシリアライズされた表現は、ちょうどObjectId("1234567890abcef123456789")まったく同じであるので、これは同様に既存のデータと安全でなければなりません。

1

これらのエラーを防ぐことはできませんが、数字の間隔を使用して「誰か」が問題を見つけやすくなります。

私が誤っていない場合は、IDを設定することができますので、あらゆる種類の「接頭辞」を使用できます。

色は、1000年と2000年との味を開始することができ、ほとんどのリポジトリにIDは、(整数のような)一般的なものであるためように...

+0

残念ながら、これは問題を解決するためには機能しません。間違った呼び出しをすると常に何も返されないようにすることができますが、プログラマーはObjectIdが 'colorId'か' flavorId'かどうかについて考える必要はなく、*もっと* gotchasについても知る必要があります。 – meustrus

+0

彼らはそれを知る必要はありません。それを知っていることはオプションですが、彼がそうしていれば、彼はもっと簡単になります。彼がこれらのIDをじっくりと操るとすぐに、彼はドキュメントなしでパターンを見つける可能性が最も高いでしょう。 – Woozar

0

うーん、それは、ソフトの問題の一種です。だから我々はこの防弾ソリューション

cupcakeRepository.Find(ObjectId flavorId, ÒbjectType ÒbjectType.Flavor) 

またはちょうどより冗長

cupcakeRepository.FindByColor(ObjectId id) 

cupcakeRepository.FindByFlavor(ObjectId id) 
関連する問題