2011-04-14 11 views
15

Estoy tratando de construir una solución genérica al problema de las enumeraciones con EF 4.1. Mi solución es básicamente una versión genérica de How to fake enums in ef 4. La clase de enumeración envoltorio funciona de maravilla en el resto del código y permite código como:EF 4.1 Code First - map enum wrapper como tipo complejo

EnumWrapper<Color> c = Color.Red; 

Aquí es la clase de enumeración envoltorio:

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

enumeración:

public enum Color { red = 1, green = 2, blue = 3 } 

POCO:

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

Mapeo:

public class ColorMapping : ComplexTypeConfiguration<EnumWrapper<Color>> 
{ 
    public ColorMapping() { 
     Ignore(x => x.Enum); 
     Property(x => x.Value); 
    } 
} 

También he intentado mapear en EntityTypeConfiguration del ChickenSandwich así:

Property(x => x.CheeseColor.Value).HasColumnName("CheeseColor"); 

Si lo dejo hasta la asignación de color y hacer ninguna asignación explícita en la ChickenSandwichMapping, sólo doesn No lo pongas en la base de datos. Si me Mapa de la manera x.CheeseColor.Value, consigo la temida:

System.InvalidOperationException: La propiedad configurado 'CheeseColor' es no una propiedad declarada en la entidad 'ChickenSandwich'. Verificar que no ha sido explícitamente excluidos del modelo y que es válida una primitiva propiedad ..


Editar

yo no era capaz de obtener la versión genérica de la envoltura enum funcionando, así que me he ido con la redacción de envoltorios individuales. No es exactamente lo que quería porque infringe el principio DRY, pero me permite consultar la columna como una enumeración.

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

Tuve que usar el ColorWrapper en la clase ChickenSandwich. Funciona de manera más o menos transparente. Entonces tuvo que añadir esto a mi constructor de la clase de mapeo para obtener el nombre de la columna que quería:

Property(x => x.CheeseColor.Value).HasColumnName("CheeseColorId"); 

Respuesta

30

Hay una manera mucho más simple de asignar enums en EF 4: solo crea una propiedad int en tu clase ChickenSandwich para representar el valor int de la enumeración. Esa es la propiedad que EF debe asignar, a continuación, tiene una propiedad "mini envoltorio" para permitir el uso de la 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; } 
    } 
} 

que en realidad no tiene que utilizar la API de Fluido o cualquier tipo de decoración atributo para que esto trabajo.Al generar la base de datos, EF ignorará cualquier tipo que no sepa cómo mapear, pero se asignará la propiedad int.

He intentado mapear enums basado en ese artículo también, pero me causó innumerables dolores de cabeza. Este método funciona bien, y puede adaptar su solución para usar su contenedor como la propiedad "mapeo", es decir, CheeseColor en este caso.

+0

¿No tendríamos que hacer consultas linq contra el valor int para que pueda consultar la base de datos subyacente? Es decir. no tendríamos que hacer "desde cs en ChickenSandwiches donde CheeseColorValue = (int) CheeseColor.Red" –

+3

@Nathan - Eso es correcto, me temo. Lo vi como un pequeño precio a pagar después de todos los problemas que el otro método me dio. Debo decir que en mi caso las propiedades int/enum no han sido el foco de mis cláusulas 'where'. –

+0

Terminé yendo con envoltorios individuales para cada enumeración. Es repetitivo, pero me permite consultar con enumeraciones, lo cual hacemos bastante en realidad. La aplicación real no es realmente sobre sandwiches de pollo. Es más como consultar una lista de clientes en función de si son empresas, personas, etc. –

4

que podría ser la mejor opción para las enumeraciones, pero otra idea sería simplemente usar constantes en vez de enumeraciones:

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

Esa es una manera de resolverlo, pero destruye la seguridad de tipos de la enumeración. Cualquier función que tuviéramos donde pasaste en un Color, ahora pasaría en un int. –

7

que tiene Nathan genérica clase de enumeración envoltorio para trabajar simplemente por lo que es abstracto y movimiento:

public static implicit operator EnumWrapper <TEnum> (TEnum e) 

a las clases derivadas de este tipo:

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

en mi código acabo utilizando el que como este

public CategorySortWrapper ChildSortType { get; set; } 

category.ChildSortType = CategorySort.AlphabeticOrder; 

No he hecho nada más y EF 4.1 ha creado un "Tipo complejo tipo campo" en la base de datos llamada ChildSortType_Value

+0

Realmente me gustó esta solución, ojalá OP (OA? OQ?) Vuelva para agregar algo de SECADO. –

+0

Esta respuesta fue la solución para mí también. Más elegante que la respuesta aceptada. – OSH

0

según la respuesta de Henrik Stenbæk. asignación está trabajando muy bien

category.ChildSortType = CategorySort.AlphabeticOrder 

pero la comparación de la envoltura para su enumeración no funciona.

if(category.ChildSortType == CategorySort.AlphabeticOrder) 
{ 

} 

el siguiente operador debe añadirse a la clase abstracta

public static implicit operator TEnum(EnumWrapper<TEnum> w) 
     { 
      if (w == null) 
       return default(TEnum); 
      else 
       return w.EnumVal; 
     } 
Cuestiones relacionadas