C# 如何使用CodeFirstCTP5将列映射到EF4中的复杂类型?

C# 如何使用CodeFirstCTP5将列映射到EF4中的复杂类型?,c#,entity-framework,entity-framework-4,ef-code-first,C#,Entity Framework,Entity Framework 4,Ef Code First,在实体框架4中遇到了缺少enum属性支持的问题后,我发现了这篇文章,它描述了一种解决方法: 如何在代码中而不是在设计器中表示数据库列到OrderStatusWrapper(如本文所述)的映射 更新: 根据提供的答案,我没有意识到我需要将OrderStatusenum替换为OrderStatusWrapper类型,即 而不是: public class Order { public OrderStatus Status { get; set; } } 我应该使用: public cl

在实体框架4中遇到了缺少
enum
属性支持的问题后,我发现了这篇文章,它描述了一种解决方法:

如何在代码中而不是在设计器中表示数据库列到
OrderStatusWrapper
(如本文所述)的映射

更新:

根据提供的答案,我没有意识到我需要将
OrderStatus
enum替换为
OrderStatusWrapper
类型,即

而不是:

public class Order 
{
    public OrderStatus Status { get; set; }
}
我应该使用:

public class Order 
{
    public OrderStatusWrapper Status { get; set; }
}
然而,在执行以下代码时,这让我更进一步:

// 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();
} 
但我得到以下编译错误:


错误1类型“ConsoleApplication1.OrderStatusWrapper”必须是不可为null的值类型,才能将其用作泛型类型或方法“System.Data.Entity.ModelConfiguration.Configuration.Types.StructuralTypeConfiguration.Property(System.Linq.Expressions.Expression>)”中的参数“T”[剪切路径]在上下文文件中写入以下内容:

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.ComplexType<OrderStatusWrapper>();
            base.OnModelCreating(modelBuilder);
        }
如果您按照该示例所示和我所展示的一小段代码进行操作(直到编辑:),则应能按预期工作

编辑nr2: 如果我们的包装器如下所示:

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

数据库采用属性值的名称,因此如果将该属性名称更改为Status,则数据库中的列将更改为Status。

下面的代码是code First CTP5中映射的样子。在这种情况下,Code First将自动将OrderStatusWrapper识别为复杂类型,这是由于一个称为复杂类型发现的概念,该概念基于可达性约定工作。您甚至不需要使用
ComplexTypeAttribute
ComplexType()
fluent API将OrderStatusWrapper显式注册为复杂类型

有关更多信息,请查看此帖子:

更新: 由于CTP5中的一个错误,如果您想为复杂类型属性(例如,OrderStatusWrapper.Value的[Orders].[Status])拥有一个自定义列名,则必须使用
[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
}
由于您使用的是现有数据库,您还可以选择关闭数据库中的元数据表:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Conventions
                .Remove<System.Data.Entity.Database.IncludeMetadataConvention>();
}
模型创建时受保护的覆盖无效(ModelBuilder ModelBuilder)
{
modelBuilder.Conventions
.Remove();
}

但是您没有提供任何代码来表示OrderStatus列和OrderStatusWrapper之间的映射。这本身不起作用。实体将使用OrderStatusWrapper类型,您的OrderStatus将如示例所示工作,通过添加modelBuilder.ComplexType,它应该在数据库的列中接受枚举的int值(如果进行了插入/更新),这是我使用的唯一额外代码。啊哈,我没有意识到我需要在订单POCO中用
OrderStatusWrapper
替换
OrderStatus
。你能看看我的更新吗?谢谢。好吧……我知道发生了什么,为什么不让我重新映射呢。ps:+1因为我坚持这一点:)它与ObjectStatusWrapper的类型有关,如果它是值类型而不是引用类型,它将使用modelBuilder.Entity().Property(p=>p.OrderStatus)编译。HasColumnName(“OrderStatus”);但随后您将收到System.InvalidoOperationException,说明尚未在实体上声明它。除此之外,我无能为力,因为我的知识缺乏确切的细节,但我相信你或其他人会找到答案。@Morteza-你能看看我的更新吗?非常感谢。我做到了,我无法复制你得到的例外情况。我的工作正常。Code First将创建一个与复杂类型的属性名(在本例中为值)完全匹配的列名,因此我有点惊讶于您的命名状态。你能发布你的完整对象模型吗,这样我就能看到里面发生了什么?@morteza-我正在使用数据库中的一个现有表,不允许代码先创建它。我以为你可以重新映射列和属性?ps:+1,感谢你坚持我的观点:)@Kev:你当然可以:)。我更新了我的答案来告诉你怎么做。基本上,我手动将DB列名更改为Status,然后使用数据注释更改Value属性的列名。它现在可以正常工作了,我可以从我的代码第一个应用程序插入订单表。请看一看。我仍然想指出,仍然可以将Value的名称更改为数据库中列的名称,即使没有[column(name=“Status”)]它也应该可以正常工作请查看我的帖子进行第二次编辑,希望您所看到的答案能够结束这篇文章,因为在EF5.0/.NET4.5()之前,EF不会有适当的枚举支持。此外,CTP5早就让这个问题变得多余了。
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;
   }
}
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
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Conventions
                .Remove<System.Data.Entity.Database.IncludeMetadataConvention>();
}