Docs Menu
Docs Home
/ / /
C#/.NET
/ /

多形オブジェクト

項目一覧

  • Overview
  • 多形オブジェクトの逆直列化
  • 弁別子を使用する
  • ScalaDiscriminatorConvention
  • HierarchicalDiscriminatorConvention
  • カスタム弁別子規則

多形オブジェクトは、1 つ以上の親クラスからプロパティとメソッドを継承します。 これらのオブジェクトは、.NET/C# ドライバーが BSON ドキュメントとの間で正しく直列化するように特別なマッピングを必要とします。

このガイドでは、次の点について説明しています。

  • 多形型を逆直列化する方法

  • .NET/C# ドライバーに含まれる弁別子の規則

  • カスタム弁別子規則の作成方法

このページの例では、次の継承階層を使用します。

public class Animal
{
}
public class Cat : Animal
{
}
public class Dog : Animal
{
}
public class Lion : Cat
{
}
public class Tiger : Cat
{
}

シリアライザーが多形オブジェクトを逆直列化する前に、継承階層内のすべてのクラスの関係をドキュメントする必要があります。

オートマッパーを使用してクラスをマッピングする場合は、階層内の各基本クラスに [BsonKnownTypes]属性を適用します。 基本クラスから直接継承する各クラスを引数として渡します。

次の例えは、 Animal階層のクラスに[BsonKnownTypes]属性を適用する方法を示しています。

[BsonKnownTypes(typeof(Cat), typeof(Dog))]
public class Animal
{
}
[BsonKnownTypes(typeof(Lion), typeof(Tiger))]
public class Cat : Animal
{
}
public class Dog : Animal
{
}
public class Lion : Cat
{
}
public class Tiger : Cat
{
}

注意

BsonKownTypes の使用

[BsonKnownTypes]属性は親クラスにのみ適用します。 階層内のすべての子クラスではなく、クラスから直接継承する型のみを引数として渡します。

クラス マップを手動で作成している場合は、次の例に示すように、階層内のすべてのクラスに対してBsonClassMap.RegisterClassMap<T>()メソッドを呼び出します。

BsonClassMap.RegisterClassMap<Animal>();
BsonClassMap.RegisterClassMap<Cat>();
BsonClassMap.RegisterClassMap<Dog>();
BsonClassMap.RegisterClassMap<Lion>();
BsonClassMap.RegisterClassMap<Tiger>();

Tip

クラスマップ

クラスマッピングの詳細については、 クラスマッピングドキュメントを参照してください。

MongoDB では、弁別子とは、ドキュメントが逆直列化するクラスを識別するためにドキュメントに追加されるフィールドです。 コレクションに 1 つの継承階層から複数の型が含まれている場合、弁別子は各ドキュメントが正しいクラスに逆シリアル化されることを確認します。 .NET/C# ドライバーは、BSON ドキュメントの_tという名前のフィールドに弁別子の値を保存します。 一般的に、 _tは BSON ドキュメント内の_idに続く 2 番目のフィールドです。

弁別子規則は、弁別子フィールドに保存される値を定義します。 このセクションでは、.NET/C# ドライバーに含まれる弁別子規則と、カスタム弁別子規則を作成する方法について学習できます。

デフォルトでは、.NET/C# ドライバーはScalarDiscriminatorConventionを使用します。 この規則に従って、.NET/C# ドライバーは_tフィールドの値を、ドキュメントが直列化されたクラスの名前に設定します。

サンプルのAnimalクラスとその各サブクラスのインスタンスを作成するとします。 これらのオブジェクトを単一のコレクションに直列化すると、.NET/C# ドライバーはScalarDiscriminatorConventionを適用し、対応する BSON ドキュメントは次のように表示されます。

{ _id: ..., _t: "Animal", ... }
{ _id: ..., _t: "Cat", ... }
{ _id: ..., _t: "Dog", ... }
{ _id: ..., _t: "Lion", ... }
{ _id: ..., _t: "Tiger", ... }

ScalarDiscriminatorConventionは簡潔な弁別子の値を使用しますが、クエリを実行するのが困難になる場合があります。 たとえば、 タイプまたはサブタイプCatのすべてのドキュメントを検索するには、検索する各クラスを明示的にリストする必要があります。

var query = coll.AsQueryable().Where(
item => item.GetType() == typeof(Cat) ||
item.GetType() == typeof(Lion) ||
item.GetType() == typeof(Tiger));

注意

OpType<T>() および は 演算子

スカラー弁別子の型を確認するには、前のコード例に示す Where構文を使用します。 Aggregate().OfType<T>() メソッドを使用しようとするか、is 演算子を含む式を Aggregate().Match() メソッドに渡すと、ドライバーは例外をスローします。

多形型のコレクションに対するクエリを簡素化するには、 HierarchicalDiscriminatorConventionを使用できます。 この規則によると、 _tの値は、ドキュメントの型の継承階層にあるすべてのクラスの配列です。

HierarchicalDiscriminatorConventionを使用するには、継承階層の基本クラスにルート クラスとしてラベルを付けます。 オートマッパーを使用している場合は、クラスに[BsonDiscriminatorAttribute]属性を適用し、 RootClass = trueを引数として渡して、ルート クラスにラベルを付けます。 次のコード例では、 Animalクラスを例の継承階層のルートとしてラベル付けします。

[BsonDiscriminator(RootClass = true)]
[BsonKnownTypes(typeof(Cat), typeof(Dog)]
public class Animal
{
}

クラス マップを手動で作成している場合は、ルート クラスのクラス マップを登録するときに、 SetIsRootClass()メソッドを呼び出し、 trueを引数として渡します。 次のコード例では、5 つのサンプル クラスすべてのクラス マップを登録しますが、継承階層のルートとしてラベル付けするのはAnimalクラスのみです。

BsonClassMap.RegisterClassMap<Animal>(classMap => {
classMap.AutoMap();
classMap.SetIsRootClass(true);
});
BsonClassMap.RegisterClassMap<Cat>();
BsonClassMap.RegisterClassMap<Dog>();
BsonClassMap.RegisterClassMap<Lion>();
BsonClassMap.RegisterClassMap<Tiger>();

たとえば、例のAnimalクラスを継承階層のルートとしてラベル付けし、 Animalクラスとその各サブクラスのインスタンスを作成します。 これらのオブジェクトを単一のコレクションに直列化すると、.NET/C# ドライバーはHierarchicalDiscriminatorConventionを適用し、対応する BSON ドキュメントは次のように表示されます。

{ _id: ..., _t: "Animal", ... }
{ _id: ..., _t: ["Animal", "Cat"], ... }
{ _id: ..., _t: ["Animal", "Dog"], ... }
{ _id: ..., _t: ["Animal", "Cat", "Lion"], ... }
{ _id: ..., _t: ["Animal", "Cat", "Tiger"], ... }

重要

ルート クラス弁別子

ルート クラスにマップされたドキュメントでは、弁別子フィールドに string 値が引き続き使用されます。

HierarchicalDiscriminatorConventionを使用すると、次の例に示すように、単一のブール値条件を使用して、 Catタイプまたはサブタイプ のすべてのドキュメントを検索できます。

var query = coll.Aggregate().Match(a => a is Cat);

.NET/C# ドライバーの規則に準拠していないデータ(たとえば、別のドライバーやオブジェクト マッパーによって MongoDB に挿入されたデータ)を処理する場合は、弁別子に別の値を使用する必要がある場合があります。フィールドを使用して、クラスがこれらの規則に一致していることを確認します。

オートマッパーを使用している場合は、クラスに [BsonDiscriminator] 属性を適用し、カスタム弁別子の値をstring引数として渡すことで、クラスの弁別子フィールドにカスタム値を指定できます。 次のコード例では、 Animalクラスの弁別子フィールドの値を "myAnyalClass" に設定します。

[BsonDiscriminator("myAnimalClass")]
public class Animal
{
}

クラス マップを手動で作成している場合は、クラス マップを登録するときにSetDiscriminator()メソッドを呼び出し、カスタム弁別子の値を引数として渡します。 次のコード例では、 Animalクラスの弁別子フィールドの値を "myAnyalClass" に設定します。

BsonClassMap.RegisterClassMap<Animal>(classMap =>
{
classMap.AutoMap();
classMap.SetDiscriminator("myAnimalClass");
});

前のAnimalクラスのインスタンスは、直列化後に次のように表示されます。

{ "_id": "...", "_t": "myAnimalClass"}

戻る

POCO