EF 4.1 Code First - карта enum wrapper как сложный тип
Я пытаюсь создать общее решение проблемы перечислений с EF 4.1. Мое решение в основном представляет собой общую версию Как подделать перечисления в ef 4. Класс оболочки enum отлично работает в остальной части кода и позволяет использовать код, например:
EnumWrapper<Color> c = Color.Red;
Здесь класс оболочки enum:
public class EnumWrapper<TEnum> where TEnum : struct, IConvertible
{
public EnumWrapper()
{
if (!typeof(TEnum).IsEnum)
throw new ArgumentException("Not an enum");
}
public TEnum Enum { get; set; }
public int Value
{
get { return Convert.ToInt32(Enum); }
set { Enum = (TEnum)(object)value; }
}
public static implicit operator TEnum(EnumWrapper<TEnum> w)
{
if (w == null) return default(TEnum);
else return w.Enum;
}
public static implicit operator EnumWrapper<TEnum>(TEnum e)
{
return new EnumWrapper<TEnum>() { Enum = e };
}
public static implicit operator int(EnumWrapper<TEnum> w)
{
if (w == null)
return Convert.ToInt32(default(TEnum));
else
return w.Value;
}
}
перечисление:
public enum Color { red = 1, green = 2, blue = 3 }
ПОКО:
public class ChickenSandwich
{
public ChickenSandwich() {
CheeseColor = new EnumWrapper<Color>();
}
public int ID { get; set; }
public string Name { get; set; }
public EnumWrapper<Color> CheeseColor { get; set; }
}
Mapping:
public class ColorMapping : ComplexTypeConfiguration<EnumWrapper<Color>>
{
public ColorMapping() {
Ignore(x => x.Enum);
Property(x => x.Value);
}
}
Я также попытался сопоставить его с ChickenSandwich EntityTypeConfiguration следующим образом:
Property(x => x.CheeseColor.Value).HasColumnName("CheeseColor");
Если я оставлю это до ColorMapping и не создаю явного отображения на ChickenSandwichMapping, он просто не помещает его в базу данных. Если я сопоставляю ему путь x.CheeseColor.Value, я получаю страшное:
System.InvalidOperationException: настроенное свойство "CheeseColor" не объявленное свойство на объекте 'Куриный сендвич'. Убедитесь, что не были явно исключены из модели и что это действительный примитив свойство..
Edit
Мне не удалось получить общую версию обертки enum, поэтому я пошел писать отдельные обертки. Это не совсем то, что я хотел, потому что это нарушает принцип DRY, но это позволяет мне запрашивать столбцы как перечисление.
[ComplexType]
public class ColorWrapper
{
[NotMapped]
public Color Enum { get; set; }
public int Value
{
get { return (int)Enum; }
set { Enum = (Color)value; }
}
public static implicit operator Color(ColorWrapper w)
{
if (w == null) return default(Color);
return w.Enum;
}
public static implicit operator ColorWrapper(Color c)
{
return new ColorWrapper { Enum = c };
}
}
Мне пришлось использовать ColorWrapper в классе ChickenSandwich. Он работает более или менее прозрачно. Затем нужно было добавить это в мой конструктор класса отображения, чтобы получить нужное имя столбца:
Property(x => x.CheeseColor.Value).HasColumnName("CheeseColorId");
Ответы
Ответ 1
В EF 4 существует гораздо более простой способ сопоставления enums
: просто создайте свойство int
в вашем классе ChickenSandwich
для представления значения int для перечисления. Чтобы свойство, которое EF должно было отобразить, затем имеет свойство "mini wrapper", чтобы вы могли использовать enum
public class ChickenSandwich
{
public int ID { get; set; }
public string Name { get; set; }
// This property will be mapped
public int CheeseColorValue { get; set; }
public Color CheseColor
{
get { return (Color) CheeseColorValue; }
set { CheeseColorValue = (int) value; }
}
}
Мне действительно не нужно использовать Fluent API или любое украшение атрибутов для этого. При генерации базы данных EF будет с радостью игнорировать любой тип, который не знает, как сопоставлять, но свойство int
будет отображаться.
Я попытался отобразить enums
на основе этой статьи, но это не вызвало у меня никаких головных болей. Этот метод работает хорошо, и вы можете приспособить свое решение для использования вашей оболочки как свойства "mapping", т.е. CheeseColor
в этом случае.
Ответ 2
Я получил класс обертки enum для Nathan, просто сделав его абстрактным и двигающимся:
public static implicit operator EnumWrapper <TEnum> (TEnum e)
для таких производных классов, как это:
public class CategorySortWrapper : EnumWrapper<CategorySort>
{
public static implicit operator CategorySortWrapper(CategorySort e)
{
return new CategorySortWrapper() { Enum = e };
}
}
public abstract class EnumWrapper<TEnum> where TEnum : struct, IConvertible
{
public EnumWrapper()
{
if (!typeof(TEnum).IsEnum)
throw new ArgumentException("Not an enum");
}
public TEnum Enum { get; set; }
public int Value
{
get { return Convert.ToInt32(Enum); }
set { Enum = (TEnum)(object)value; }
}
public static implicit operator int(EnumWrapper<TEnum> w)
{
if (w == null)
return Convert.ToInt32(default(TEnum));
else
return w.Value;
}
}
в моем коде я просто использую его как это
public CategorySortWrapper ChildSortType { get; set; }
category.ChildSortType = CategorySort.AlphabeticOrder;
Я ничего не делал, и EF 4.1 создал "поле типа ComplexType" в базе данных с именем ChildSortType_Value
Ответ 3
Это может быть лучшим вариантом для перечислений, но другой идеей было бы просто использовать константы вместо перечислений:
static void Main(string[] args)
{
Console.WriteLine("There are {0} red chicken sandwiches.",
sandwiches.ChickenSandwiches
.Where(s => s.Color == Color.red)
.ToArray().Length);
}
public struct Color
{
public const int red = 1;
public const int green = 2;
}
public class ChickenSandwich
{
public ChickenSandwich()
{
}
public int ID { get; set; }
public string Name { get; set; }
public int Color { get; set; }
}
public class Sandwiches : DbContext
{
public DbSet<ChickenSandwich> ChickenSandwiches { get; set; }
}
Ответ 4
на основе ответа Хенрика Стенбека.
назначение работает нормально
category.ChildSortType = CategorySort.AlphabeticOrder
но сравнение оболочки с ее перечислением не работает.
if(category.ChildSortType == CategorySort.AlphabeticOrder)
{
}
в абстрактный класс
должен быть добавлен следующий оператор:
public static implicit operator TEnum(EnumWrapper<TEnum> w)
{
if (w == null)
return default(TEnum);
else
return w.EnumVal;
}