问题
Having bumped into the of the lack of enum
property support in Entity Framework 4 I discovered this article which describes a workaround:
Entity Framework 4.0 Beta 1 – POCO Enum support?
How do I express the mapping of a database column to the OrderStatusWrapper
(as described in the article) in code rather than in the designer?
Update:
Based on the answers provided, I hadn't realised that I needed to replace the OrderStatus
enum with the OrderStatusWrapper
type, i.e.
Instead of:
public class Order
{
public OrderStatus Status { get; set; }
}
I should be using:
public class Order
{
public OrderStatusWrapper Status { get; set; }
}
This gets me a bit further, however upon executing the following code:
// OrderContext class defined elsewhere as:
// public class OrderContext : DbContext
// {
// public DbSet<Order> Orders { get; set; }
// }
using(OrderContext ctx = new OrderContext())
{
Order order = new Order { Status = OrderStatus.Created; }
ctx.Orders.Add(order);
ctx.SaveChanges();
}
The following exception is raised (trimmed for brevity):
System.Data.SqlClient.SqlException Message=Invalid column name 'Value'.
The database column is named Status
. I tried decorating the Status
property with:
[Column("Status")]
then
[Column("Status"), TypeName("OrderStatus")]
and
[Column("Status"), TypeName("OrderStatusWrapper")]
But this doesn't resolve this exception.
I also tried removing the Column
attribute and doing this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>()
.Property(p => p.OrderStatus)
.HasColumnName("OrderStatus");
}
But I get the following compile error:
Error 1 The type 'ConsoleApplication1.OrderStatusWrapper' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Data.Entity.ModelConfiguration.Configuration.Types.StructuralTypeConfiguration.Property(System.Linq.Expressions.Expression>)' [snipped path]
回答1:
in the context file write this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ComplexType<OrderStatusWrapper>();
base.OnModelCreating(modelBuilder);
}
it should work.
EDIT:
public class Order
{
public...
...
public OrderStatusWrapper OrderStatus { get; set; }
}
public ActionResult Index()
{
var result = _context.Orders.Where(o => o.OrderStatus == OrderStatus.ReadyForShipping);
_context.Orders.Add(new Order{ ..., OrderStatus = OrderStatus.Shipped });
...;
}
if you did as that example shows along with that small piece of code i showed the above (until EDIT:) should work as expected.
EDIT nr2: If our wrapper looks like this:
public class OrderStatusWrapper
{
private OrderStatus _status;
public int Value
{
get { return (int)_status; }
set { _status = (OrderStatus)value; }
}
public OrderStatus EnumValue
{
get { return _status; }
set { _status = value; }
}
public static implicit operator
OrderStatusWrapper(OrderStatus status)
{
return new OrderStatusWrapper { EnumValue = status };
}
public static implicit operator
OrderStatus(OrderStatusWrapper statusWrapper)
{
if (statusWrapper == null) return OrderStatus.OrderCreated;
else return statusWrapper.EnumValue;
}
}
The database takes the name of the property Value, so if you change that property name to Status, the column in the database would change to Status.
回答2:
The following code is how the mappings looks like in Code First CTP5. In this case Code First will automatically recognize OrderStatusWrapper
as a complex type due to a concept called Complex Type Discovery that works based on the Reachability Convention. You don't even need to use ComplexTypeAttribute
or the ComplexType<T>()
fluent API to explicitly register OrderStatusWrapper as a complex type.
For more info please take a look at this post:
Entity Association Mapping with Code First CTP5: Complex Types
Update:
Due to a bug in CTP5, if you want to have a custom column name for your complex type properties (e.g. [Orders].[Status] for OrderStatusWrapper.Value in DB), then you have to explicitly mark it with [ComplexType]
:
public class Order
{
public int OrderId { get; set; }
public DateTime CreatedOn { get; set; }
public OrderStatusWrapper Status { get; set; }
}
[ComplexType]
public class OrderStatusWrapper
{
private OrderStatus _status;
[Column(Name="Status")]
public int Value {
get { return (int)_status; }
set { _status = (OrderStatus)value; }
}
public OrderStatus EnumValue {
get { return _status; }
set { _status = value; }
}
}
public enum OrderStatus
{
OrderCreated,
OrderPayed,
OrderShipped
}
Since you are working with an existing database, you can optionally switch off metadata table in database as well:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Conventions
.Remove<System.Data.Entity.Database.IncludeMetadataConvention>();
}
来源:https://stackoverflow.com/questions/4484370/how-do-i-map-a-column-to-a-complex-type-in-ef4-using-code-first-ctp5