Условная ассоциация с Entity Framework - PullRequest
0 голосов
/ 27 марта 2012

Я хочу создать условную ассоциацию с 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

Теперь поле 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 * в свойстве EntityObject.

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

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

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

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

Ответы [ 2 ]

3 голосов
/ 28 марта 2012

Самое близкое, я думаю, вы сможете найти то, что подходит как для реляционной, так и для объектно-ориентированной стороны, - это сопоставление и объектную модель с абстракцией 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/ column в сущности / таблице Property, затем вы можете добавить его, но вам нужно убедиться, что вы установили для него какое-то подходящее значение, так как оно не требуется для отображения.

0 голосов
/ 29 марта 2012

Ответ Артура - это ответ, но я хочу поделиться своими результатами.

Сначала я попытался реализовать значения свойств как универсальный тип.А затем я внедрил все 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 как string, int и т. Д. Например, этот тип свойства может быть «Name», его простым типом будет строка, или может быть «Price», его простым типом будетfloat.

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

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

...