0

Я хочу создать условную связь с Entity Framework. Насколько я знаю, мы не можем создавать условные внешние ключи, поэтому я не могу решить эту проблему на уровне сервера баз данных. У меня есть таблицы так:Условная ассоциация с инфраструктурой Entity

---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 , поэтому я не смог получить таблицы Value * в Property EntityObject.

Итак, этот подход верен или что мне делать? если это правда, как я могу получить объект контекста объекта в EntityObject?

Редактировать: Если вы не предлагаете такой подход, что бы вы предложили? Пожалуйста, поделитесь своим мнением с нами. Я думаю, что лучшая альтернатива такого подхода может быть что-то вроде этого:

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

Но этот путь, если я хочу добавить еще один тип значения, я должен изменить структуру таблицы, и я должен буду обновлять объект и объектные модели в моем проекте. Я не могу использовать сериализованные объекты, потому что мне нужно фильтровать данные по значению. Edit закончилась

+0

Некоторые БД (Oracle) допускают такой ключ. Вы не упоминаете, какую БД вы используете. Сказав это, этот дизайн не подходит для стандартной SQL DB или EF. Я бы посоветовал вам решить вашу проблему по-другому. –

+0

Спасибо Крейг, я использую MS SQL Server. Я могу решить эту проблему с помощью дополнительных публичных функций в коде. Но это только смущает членов моей команды, и это делает вещи более сложными. Мне нужен такой подход, как использовать (string) SomeProperty.Value или (int) SomeProperty.Value. Я не хочу реализовывать дополнительные функции для получения значения свойства. – oruchreis

ответ

3

Ближайший я думаю, что вы можете быть в состоянии добраться до этого подходит как на реляционной стороне и на объектно-ориентированной сторона на карту и объектная модель для TPC абстракции в базе данных, которая уже очень близко к структуре таблицы, которую вы имеете. Для простоты я покажу это, используя Code First в EF 4.3.1.

Давайте определим простую объектную модель, как так:

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

Наконец, если вам действительно нужно свойство TypeID/столбец в Свойство/таблица объекта, затем вы можете добавить его, но вам нужно убедиться, что вы установите его на какое-то подходящее значение, поскольку оно не требуется для сопоставления.

+0

Ничего себе, отличный ответ. Я читаю его сейчас, и я пытаюсь понять, применив в своем проекте. После успеха я приму это как ответ. Большое спасибо. – oruchreis

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

Это тип недвижимости. Он имеет простую строку типа string, которая хранится в db как строка, int и т. Д. Например, этот тип свойства может быть «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 
} 

Это абстрактный базовый класс для таблиц значений, которые Артур имеет даваемое идею:

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

Это будет в conext класс, который был получен из DbContext:

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

Итак, моя проблема решена. И я хотел поделиться этим.

Смежные вопросы