0

Ich möchte bedingte Verknüpfung mit Entity Framework erstellen. Soweit ich weiß, können wir keine bedingten Fremdschlüssel erstellen, daher kann ich dieses Problem nicht auf Datenbankserverebene lösen. Ich habe Tabellen wie folgt aus:Bedingte Assoziation mit Entity Framework

---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 

Nun enthält das TypeId Feld in der Eigenschaftstabelle den Typ des Wertes. Zum Beispiel, wenn der TypeId == 0, ValueID dann Punkte zu Tabelle value, wenn die TypeId == 1, dann ValueID Punkte ValueInteger Tisch usw.

ich etwas Abhilfe tat, aber ich steckte irgendwo:

ich habe eine Enumeration wie folgt aus:

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

und ich implementiert eine partielle Klasse wie folgt aus:

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 
     { 

     } 
    } 
} 

Aber ich weiß nicht, wie Kontextobjekt innerhalb eines EntityObject zu erreichen Daher konnte ich die Value * -Tabellen im Property EntityObject nicht erreichen.

Also, ist dieser Ansatz richtig oder was soll ich tun? Wenn es wahr ist, wie kann ich Entitätskontextobjekt in einem EntityObject erhalten?

Bearbeiten: Wenn Sie diesen Ansatz nicht vorschlagen, was würden Sie vorschlagen? Bitte teilen Sie Ihre Meinung mit uns. Ich denke, die beste Alternative zu diesem Ansatz so etwas wie dies auch sein mag:

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

Aber auf diese Weise, wenn ich einen anderen Wert Typ hinzugefügt werden soll, muss ich Tabellenstruktur ändern, und ich werde Entität aktualisieren müssen und Objektmodelle in meinem Projekt. Ich kann serialisierte Objekte nicht verwenden, da ich Daten über den Wert filtern muss. Bearbeiten beendet

+0

Einige DBs (Oracle) erlauben einen solchen Schlüssel. Sie erwähnen nicht, welche DB Sie verwenden. Abgesehen davon ist dieses Design weder für eine Standard-SQL-Datenbank noch für die EF geeignet. Ich würde dir raten, dein Problem auf andere Weise zu lösen. –

+0

Danke Craig, ich benutze MS SQL Server. Ich kann dieses Problem mit zusätzlichen öffentlichen Funktionen im Code verwalten. Aber es verwirrt nur meine Teammitglieder und es macht die Dinge komplexer. Ich brauche so einen Ansatz wie (String) SomeProperty.Value oder (int) SomeProperty.Value zu verwenden. Ich möchte keine zusätzlichen Funktionen implementieren, um den Wert der Eigenschaft zu erhalten. – oruchreis

Antwort

3

Der nächstgelegene ich glaube, Sie in der Lage sein kann, das passt sowohl auf der relationalen Seite und auf die objektorientierte Seite abzubilden und Objektmodell zu einer TPC Abstraktion in der Datenbank zu bekommen, das ist schon sehr nah an der Tischstruktur, die du hast. Der Einfachheit halber zeige ich dies anhand von Code First in EF 4.3.1.

Lassen Sie uns ein einfaches Objektmodell definieren wie folgt:

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; 
    } 
} 

(ich nur in einigen ToString Methoden setzen, um es einfach zu sehen, was los ist, wenn wir diese Klassen verwenden.)

Dies kann Verwendung von TPC so abgebildet werden, dass jede Art seinen eigenen Tisch bekommt:

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"); 
      }); 
    } 
} 

so, jetzt die Tabellen haben wir das Layout passen Sie zu Beginn Ihrer Frage zur Verfügung gestellt, mit der Ausnahme, dass die TypeId co Eine herkömmliche Spalte fehlt, da sie hier nicht benötigt wird.

Schreiben wir eine initializer und eine Konsolenanwendung einige Testdaten hinzuzufügen und anzuzeigen:

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); 
      } 
     } 
    } 
} 

Ausführen dieses ausdruckt:

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 

Sie können sehen, dass wir leicht in der Lage sind, zu Fügen Sie verschiedene Arten von Werten hinzu, lassen Sie sie in den entsprechenden Tabellen speichern und dann diese Werte abfragen.

Jetzt möchten Sie vielleicht wirklich eine Eigenschaft die gibt den Wert als "Objekt" wie in Ihrem Beispiel eingegeben. Nun, wir können jetzt tun dies mit einer einfachen abstrakten Eigenschaft:

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; 
    } 
} 

Sie könnten auch tun dies vorstellen, wenn man will:

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; } 
    } 
} 

Schließlich, wenn Sie wirklich die TypeId Eigenschaft/Spalte in der Notwendigkeit Eigenschaft entity/table, dann können Sie sie hinzufügen, aber Sie müssen sicherstellen, dass Sie sie auf einen geeigneten Wert setzen, da sie für das Mapping nicht benötigt wird.

+0

Wow, tolle Antwort. Ich lese es gerade und versuche es zu verstehen, indem ich mich in meinem Projekt bewerbe. Nach einem Erfolg werde ich das als Antwort akzeptieren. Danke vielmals. – oruchreis

0

Arthurs Antwort ist die Antwort, aber ich möchte meine Ergebnisse teilen.

Zuerst habe ich versucht, Eigenschaftswerte als generischen Typ zu implementieren. Und dann habe ich jeden ValueString, ValueInteger, etc. Klassen, die von diesem generischen Typ ausgehen. Es funktionierte, aber generische Art Ansatz verursacht eine Menge Casting in der Nutzung. Also habe ich mich an Objektwerte gehalten.

Dies ist Festigkeitsklasse, die den Wert und die Art gilt:

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; } 
} 

Dies ist die Art der Immobilie. Es hat einen einfachen Typ wie string, der in db als string, int usw. gespeichert wird. Zum Beispiel kann dieser Eigenschaftstyp "Name" sein, dessen einfacher Typ eine Zeichenkette wäre, oder er könnte "Preis" sein, was sein einfacher Typ wäre schweben.

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 
} 

Dies ist die abstrakte Basisklasse für Wertetabellen, die Arthur die Idee hat gived:

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

Dies sind Wertklassen/Tabellen:

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; 
     } 
    } 
} 

Diese im Conext sein Klasse, die von DbContext gefahren ist:

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"); 
      }); 

Also, mein Problem wurde gelöst. Und ich wollte das teilen.