【问题标题】:How do I map a column to a complex type in EF4 using code first CTP5? [closed]如何使用代码优先 CTP5 将列映射到 EF4 中的复杂类型? [关闭]
【发布时间】:2010-12-19 18:11:16
【问题描述】:

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

Entity Framework 4.0 Beta 1 – POCO Enum support?

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

更新:

根据提供的答案,我没有意识到我需要将 OrderStatus 枚举替换为 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();
} 

引发以下异常(为简洁起见):

System.Data.SqlClient.SqlException 消息=无效的列名“值”。

数据库列名为Status。我尝试用以下方式装饰Status 属性:

[Column("Status")]

然后

[Column("Status"), TypeName("OrderStatus")]

[Column("Status"), TypeName("OrderStatusWrapper")]

但这并不能解决此异常。

我还尝试删除 Column 属性并这样做:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Order>()
      .Property(p => p.OrderStatus)
      .HasColumnName("OrderStatus");
}

但我得到以下编译错误:

错误 1 ​​类型“ConsoleApplication1.OrderStatusWrapper”必须是不可为空的值类型,才能将其用作泛型类型或方法“System.Data.Entity.ModelConfiguration.Configuration”中的参数“T”。 Types.StructuralTypeConfiguration.Property(System.Linq.Expressions.Expression>)' [截断路径]

【问题讨论】:

  • 查看我的帖子进行第二次编辑,希望得到您寻求的答案
  • 我关闭了它,因为 EF 在 EF5.0/.NET 4.5 (blogs.msdn.com/b/adonet/archive/2011/10/18/…) 之前不会有适当的枚举支持。此外,CTP5 早就让这个问题变得多余了。

标签: c# entity-framework entity-framework-4 ef-code-first


【解决方案1】:

在上下文文件中这样写:

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.ComplexType<OrderStatusWrapper>();
            base.OnModelCreating(modelBuilder);
        }

它应该可以工作。

编辑:

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

    ...;
}

如果您按照该示例所示操作以及我在上面显示的那一小段代码(直到编辑:)应该按预期工作。

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

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

【讨论】:

  • 但是您没有提供任何代码来表示 OrderStatus 列和 OrderStatusWrapper 之间的映射。这不能单独工作。
  • 实体将使用 OrderStatusWrapper 类型,并且您的 OrderStatus 将如示例所示工作,通过添加 modelBuilder.ComplexType 它应该接受枚举的 int 值到数据库中的列中(如果插入/更新),这是我用来让它工作的唯一额外代码。
  • 啊哈,我没有意识到我需要在 POCO 中将 OrderStatus 替换为 OrderStatusWrapper。你能看看我的更新吗?赞赏。
  • 好吧...我知道发生了什么,买它为什么不让我重新映射它。 ps:+1 支持我 :)
  • 它与 ObjectStatusWrapper 的类型有关,如果它是值类型而不是引用类型,它将使用 modelBuilder.Entity().Property(p => p.OrderStatus).HasColumnName("OrderStatus");但随后您会收到 System.InvalidOperationException 说明尚未在实体上声明它。我无法提供更多帮助,因为我的知识缺乏确切的细节,但我相信您或其他人将能够找到答案。
【解决方案2】:

以下代码是 Code First CTP5 中映射的样子。在这种情况下,Code First 将自动将OrderStatusWrapper 识别为复杂类型,这是因为基于可达性约定 的称为复杂类型发现 的概念。您甚至不需要使用 ComplexTypeAttributeComplexType&lt;T&gt;() fluent API 来将 OrderStatusWrapper 显式注册为复杂类型。

有关更多信息,请查看此帖子:
Entity Association Mapping with Code First CTP5: Complex Types

更新:

由于 CTP5 中的一个错误,如果您想为您的复杂类型属性(例如 [Orders].[Status] 用于 DB 中的 OrderStatusWrapper.Value)自定义列名,那么您必须使用 @ 显式标记它987654325@:

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

【讨论】:

  • @Morteza - 你能看看我的更新吗?赞赏。
  • 我做到了,但我无法重现您遇到的异常。我的工作正常。 Code First 将创建一个与复杂类型的属性名称完全匹配的列名称(在本例中为 Value),所以我有点惊讶您的命名为 Status。能否请您发布您的完整对象模型,以便我看看里面发生了什么?
  • @morteza - 我正在使用数据库中的现有表,并且不允许代码先创建它。我以为你可以重新映射列和属性?ps:+1 支持我 :)
  • @Kev:你打赌你可以:)。我更新了我的答案,向您展示如何。基本上,我手动将我的数据库列名更改为状态,然后使用数据注释更改 Value 属性的列名。它现在可以正常工作了,我可以从我的代码优先应用程序中插入订单表。请看一看。
  • 我仍然想指出,仍然可以将 Value 的名称更改为数据库中列的名称,即使没有 [Column(Name="Status ")]
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-27
  • 2012-02-04
  • 2023-03-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多