0

EntityFrameworkとの条件付き関連付けを作成したいと思います。私の知る限り、条件付き外部キーを作成できないため、データベースサーバーレベルでこの問題を解決することはできません。私はそのようなテーブルを持っています:

---Property---
int     id
string  Name
int     TypeId      --> Condition on this.
int     ValueId

---ValueString---
int     id
string  Value

---ValueInteger---
int     id
int     Value

---ValueBoolean---
int     id
bool    Value

これで、プロパティテーブルのTypeIdフィールドに値のタイプが含まれます。たとえば、TypeId == 0の場合、ValueIdはValueStringテーブルを指し、TypeId == 1の場合、ValueIdはValueIntegerテーブルを指します。

私はいくつかの回避策を実行しましたが、どこかで立ち往生しました:

私はそのような列挙型を持っています:

public enum PropertyType
{
    String = 0, 
    Integer = 1,
    Boolean = 2,
    DateTime = 3,
    List = 4 
}

そして私はそのような部分的なクラスを実装しました:

public partial class ProductProperty : EntityObject
{
    public object Value
    {
        get
        {
            switch (Property.Type)
            {
                case PropertyType.String:
                    return ValueString.Where(w => w.id == this.ValueId ).Select(s => s);//how to return?
                    break;
                case PropertyType.Integer:
                    return ValueInteger.Where(w => w.id == this.ValueId ).Select(s => s) //how to return?
                    break;
                case PropertyType.Boolean:
                    return ValueBoolean.Where(w => w.id == this.ValueId ).Select(s => s) //how to return?
                    break;
                case PropertyType.DateTime:
                    return ValueDateTime.Where(w => w.id == this.ValueId ).Select(s => s) //how to return?
                    break;
                default:
                    return null;
                    break;
            }
        }
        set
        {

        }
    }
}

しかし、EntityObject内のコンテキストオブジェクトに到達する方法がわからないため、プロパティEntityObjectのValue*テーブルに到達できませんでした。

それで、このアプローチは本当ですか、それとも私は何をすべきですか?trueの場合、EntityObjectでエンティティコンテキストオブジェクトを取得するにはどうすればよいですか?

編集:このアプローチを提案しない場合、あなたは何を提案しますか?ご意見をお聞かせください。私は、このアプローチの最良の代替案は次のようなものかもしれないと思います:

---Property---
int     id
string  ValueString
int     ValueInteger
bool    ValueBoolean
etc...

ただし、このように、別の値型を追加する場合は、テーブル構造を変更する必要があり、プロジェクトのエンティティモデルとオブジェクトモデルを更新する必要があります。値でデータをフィルタリングする必要があるため、シリアル化されたオブジェクトを使用できません。 編集が終了しました

4

2 に答える 2

3

リレーショナル側とオブジェクト指向側の両方に当てはまると思う最も近いものは、データベース内のTPC抽象化にマップし、オブジェクトモデルを作成することです。これは、既にお持ちのテーブル構造に非常に近いものです。 。簡単にするために、EF4.3.1のCodeFirstを使用してこれを示します。

次のような単純なオブジェクトモデルを定義しましょう。

public class Property
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int ValueId { get; set; }
    public virtual Value Value { get; set; }
}

public abstract class Value
{
    public int Id { get; set; }
}

public class ValueString : Value
{
    public string Value { get; set; }

    public override string ToString()
    {
        return "String value of " + Value;
    }
}

public class ValueInteger : Value
{
    public int Value { get; set; }

    public override string ToString()
    {
        return "Integer value of " + Value;
    }
}

public class ValueBoolean : Value
{
    public bool Value { get; set; }

    public override string ToString()
    {
        return "Boolean value of " + Value;
    }
}

(これらのクラスを使用するときに何が起こっているかを簡単に確認できるようにするために、いくつかのToStringメソッドを配置しました。)

これは、各タイプが独自のテーブルを取得するようにTPCを使用してマッピングできます。

public class PropertyAndValuesContext : DbContext
{
    public DbSet<Property> Properties { get; set; }
    public DbSet<Value> Values { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<ValueString>().Map(
            m =>
            {
                m.MapInheritedProperties();
                m.ToTable("ValueString");
            });

        modelBuilder.Entity<ValueInteger>().Map(
            m =>
            {
                m.MapInheritedProperties();
                m.ToTable("ValueInteger");
            });

        modelBuilder.Entity<ValueBoolean>().Map(
            m =>
            {
                m.MapInheritedProperties();
                m.ToTable("ValueBoolean");
            });
    }
}

これで、質問の最初に提供したレイアウトと一致するテーブルが作成されました。ただし、ここでは必要ないため、TypeId条件付き列がありません。

初期化子とコンソールアプリを作成して、テストデータを追加して表示してみましょう。

public class TestInitializer : DropCreateDatabaseAlways<PropertyAndValuesContext>
{
    protected override void Seed(PropertyAndValuesContext context)
    {
        new List<Property>
        {
            new Property { Name = "PropWithBool", Value = new ValueBoolean { Id = 1, Value = true } },
            new Property { Name = "PropWithString1", Value = new ValueString { Id = 2, Value = "Magic" } },
            new Property { Name = "PropWithString2", Value = new ValueString { Id = 3, Value = "Unicorn" } },
            new Property { Name = "PropWithInt1", Value = new ValueInteger { Id = 4, Value = 6 } },
            new Property { Name = "PropWithInt2", Value = new ValueInteger { Id = 5, Value = 7 } },
        }.ForEach(p => context.Properties.Add(p));
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        Database.SetInitializer(new TestInitializer());

        using (var context = new PropertyAndValuesContext())
        {
            foreach (var property in context.Properties)
            {
                Console.WriteLine("{0} with {1}", property.Name, property.Value);
            }
        }
    }
}

これを実行すると、次のように出力されます。

PropWithBool with Boolean value of True
PropWithString1 with String value of Magic
PropWithString2 with String value of Unicorn
PropWithInt1 with Integer value of 6
PropWithInt2 with Integer value of 7

さまざまなタイプの値を簡単に追加し、それらを適切なテーブルに格納してから、これらの値をクエリして戻すことができることがわかります。

ここで、例のように「オブジェクト」として入力された値を返すプロパティが本当に必要な場合があります。これで、単純な抽象プロパティを使用してこれを実行できます。

public abstract class Value
{
    public int Id { get; set; }

    public abstract object TheValue { get; set; }
}

public class ValueString : Value
{
    public string Value { get; set; }

    public override object TheValue
    {
        get { return Value; }
        set { Value = (string)value; }
    }

    public override string ToString()
    {
        return "String value of " + Value;
    }
}

public class ValueInteger : Value
{
    public int Value { get; set; }

    public override object TheValue
    {
        get { return Value; }
        set { Value = (int)value; }
    }

    public override string ToString()
    {
        return "Integer value of " + Value;
    }
}

public class ValueBoolean : Value
{
    public bool Value { get; set; }

    public override object TheValue
    {
        get { return Value; }
        set { Value = (bool)value; }
    }

    public override string ToString()
    {
        return "Boolean value of " + Value;
    }
}

必要に応じて、これを行うことも想像できます。

public class Property
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int ValueId { get; set; }
    public virtual Value Value { get; set; }

    public object TheValue
    {
        get { return Value.TheValue; }
        set { Value.TheValue = value;  }
    }
}

最後に、Propertyエンティティ/テーブルにTypeIdプロパティ/列が本当に必要な場合は、それを追加できますが、マッピングには必要ないため、適切な値に設定する必要があります。

于 2012-03-28T05:20:47.533 に答える
0

アーサーの答えは答えですが、私は私の結果を共有したいと思います。

まず、プロパティ値をジェネリック型として実装しようとしました。そして、すべてのValueString、ValueIntegerなどを実装しました。このジェネリック型から駆動するクラス。それは機能しましたが、ジェネリック型のアプローチは使用法に多くのキャストを引き起こしました。だから私はオブジェクトの値に固執しました。

これは、値とタイプを保持するプロパティクラスです。

public class ProductProperty
{
    public int ProductPropertyId { get; set; }
    public Product Product { get; set; }
    public int TypeId { get; set; }
    public int ValueId { get; set; }
    [ForeignKey("TypeId")]
    public PropertyType Type { get; set; }
    [ForeignKey("ValueId")]
    public PropertyValue Value { get; set; }
}

これはプロパティのタイプです。文字列、intなどとしてdbに保持されるstringのような単純なタイプがあります。たとえば、このプロパティタイプは、単純なタイプが文字列になる「Name」、または単純なタイプが次の「Price」にすることができます。浮く。

public class PropertyType
{
    public int PropertyTypeId { get; set; }
    [MaxLength(150)]
    public string Name { get; set; }

    //For before EF 5, there is no enum support
    [Column("SimpleType")]
    public int InternalSimpleType { get; set; }
    [NotMapped]
    public SimpleType SimpleType
    {
        get { return (SimpleType)InternalSimpleType; }
        set { InternalSimpleType = (int)value; }
    }

    public ICollection<ProductProperty> ProductProperties { get; set; }
}

public enum SimpleType : int
{
    String = 1, 
    Integer = 2,
    Float = 4,
    Boolean = 8,
    DateTime = 16,
    List = 32
}

これは、Arthurがアイデアを出した値テーブルの抽象基本クラスです。

public abstract class PropertyValue
{
    [Key]
    public int PropertyValueId { get; set; }
    [NotMapped]
    public abstract object Value { get; set; }
}

これらは値クラス/テーブルです:

public class PropertyValueString : PropertyValue
{
    [Column("Value", TypeName="ntext")]
    public string InternalValue { get; set; }
    public override object Value
    {
        get
        {
            return (string)InternalValue;
        }
        set
        {
            InternalValue = (string)value;
        }
    }
}

public class PropertyValueInteger : PropertyValue
{
    [Column("Value")]
    public int InternalValue { get; set; }
    public override object Value
    {
        get
        {
            return (int)InternalValue;
        }
        set
        {
            InternalValue = (int)value;
        }
    }
}

public class PropertyValueBoolean : PropertyValue
{
    [Column("Value")]
    public bool InternalValue { get; set; }
    public override object Value
    {
        get
        {
            return (bool)InternalValue;
        }
        set
        {
            InternalValue = (bool)value;
        }
    }
}

public class PropertyValueFloat : PropertyValue
{
    [Column("Value")]
    public float InternalValue { get; set; }
    public override object Value
    {
        get
        {
            return (float)InternalValue;
        }
        set
        {
            InternalValue = (float)value;
        }
    }
}

public class PropertyValueDateTime : PropertyValue
{
    [Column("Value", TypeName = "datetime2")]
    public DateTime InternalValue { get; set; }
    public override object Value
    {
        get
        {
            return (DateTime)InternalValue;
        }
        set
        {
            InternalValue = (DateTime)value;
        }
    }
}

これは、DbContextから駆動されたconextクラスに含まれます。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<PropertyValueString>().Map(
            m =>
            {
                m.MapInheritedProperties();
                m.ToTable("PropertyValueString");
            });

        modelBuilder.Entity<PropertyValueInteger>().Map(
            m =>
            {
                m.MapInheritedProperties();
                m.ToTable("PropertyValueInteger");
            });

        modelBuilder.Entity<PropertyValueBoolean>().Map(
            m =>
            {
                m.MapInheritedProperties();
                m.ToTable("PropertyValueBoolean");
            });

        modelBuilder.Entity<PropertyValueFloat>().Map(
            m =>
            {
                m.MapInheritedProperties();
                m.ToTable("PropertyValueFloat");
            });

        modelBuilder.Entity<PropertyValueDateTime>().Map(
            m =>
            {
                m.MapInheritedProperties();
                m.ToTable("PropertyValueDateTime");
            });

だから、私の問題は解決しました。そして、私はこれを共有したかった。

于 2012-03-29T19:38:44.947 に答える