TPH Simple Config / Discriminator问题

时间:2012-04-07 20:48:21

标签: entity-framework ef-code-first

我正在尝试使用代码的EF - 我无法弄清楚我的错误在哪里就是我自己的例子。我只是出于想法,并想确定我出错的地方......

首先是表示位置的简单POCO类 - 位置可以是RadioStation,也可以是商家。我没有添加其他字段(稍后会出现),所以现在它只是一个简单的配置中的TPH,我可以做到。

namespace EFDataClasses.Entities
{

  public class RadioStation : Location
  {
    public RadioStation()
    {
    }

  }

  public class Merchant : Location
  {
    public Merchant()
     {
     } 
   }


  public class Location
  {
   public Location()
   {

   }

public int Loc_ID { get; set; }
public string Loc_Code { get; set; }
public string Loc_Name { get; set; }
public string  Loc_Type {get;set;}

 }
}

然后是配置类:

namespace EFDataClasses.Mapping
{


  public class LocationMap : EntityTypeConfiguration<Location>
  {
    public LocationMap()
    {
      // Primary Key
      this.HasKey(t => t.Loc_ID);

      // Properties
      this.Property(t => t.Loc_ID)
          .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

      // Properties
      this.Property(t => t.Loc_Code)
          .IsRequired()
          .HasMaxLength(50);

      this.Property(t => t.Loc_Name)
          .IsRequired()
          .HasMaxLength(50);


      this.Property(t => t.Loc_ID).HasColumnName("Loc_ID");
      this.Property(t => t.Loc_Code).HasColumnName("Loc_Code");
      this.Property(t => t.Loc_Name).HasColumnName("Loc_Name");

      //my discriminator property
      this.Property(t => t.Loc_Type).HasColumnName("Loc_Type").HasColumnType("varchar").HasMaxLength(50).IsRequired();

      // Table & Column Mappings
      this.Map(m =>
      {
        m.ToTable("Location");
        m.Requires("Loc_Type").HasValue("Location");
      }
        )
        .Map<RadioStation>(m =>
        {
          m.ToTable("Location");
          m.Requires("Loc_Type").HasValue("RadioStation");
        }
        )
        .Map<Merchant>(m =>
        {
          m.ToTable("Location");
          m.Requires("Loc_Type").HasValue("Merchant");
        }
        )
        ;



    }
  }
}

这是上下文:

namespace EFDataClasses
{
  public class MyContext : DbContext
  {
    static MyContext()
    {
      Database.SetInitializer<MyContext>(new DropCreateDatabaseAlways<MyContext>());
    }

    public DbSet<EFDataClasses.Entities.Location> Locations {get; set;}

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
      modelBuilder.Configurations.Add(new LocationMap());
    }
  }
}

最后是尝试添加广播电台的节目类..

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EFConsole
{
  class Program
  {
    static void Main(string[] args)
    {

      var db = new EFDataClasses.MyContext();
      db.Locations.Add(new EFDataClasses.Entities.RadioStation() { Loc_Name = "Radio Station Name 1",  Loc_Code = "RD1" });
      int chngs = db.SaveChanges();
      System.Diagnostics.Debugger.Break();

    }
  }
}

我得到的错误是Loc_Type上的验证错误,说明它是必填字段。我的印象是EF会在我选择合适的类型时填写 - 而且我所有的阅读都支持它。

如果我确实添加了适当的位置类型 - EF会给我另一个错误....

arggghhh!

最后我想把位置抽象,但这是否意味着我可以删除hasvalue(“位置”)?

我想继续前进,但我很好奇我做错了什么。谢谢!

2 个答案:

答案 0 :(得分:1)

我认为你的事情过于复杂,这对CF来说很危险 使用代码优先,您必须坚持使用“经过试验和测试”的模式,否则混合内容和结构会很快让您陷入困境。

首先,为什么你需要映射并拥有Loc_Type,我的意思是在类模型中?这是多余的,你真的会用你的'类'类型得到它,你的类类型是鉴别器,反之亦然。

这就是我认为错误的说法,无论是提供还是“从列中删除列”等等。

所以只需从'Location'中删除它,它就可以解决问题。你也不需要在大多数时候指定鉴别器,除非你想让你的Db有意义并且被其他方面使用等等。

如果你想要你可以使它抽象,位置 - 这通常已经完成,我认为你可以从映射中删除它,只有'真正实现'的类应该映射为只有那些可以'实例化'。

最后,TPH出于各种原因而不是最“幸运”的解决方案,而且我认为最容易出错,TPT,TPC都更加“流畅”。你不能让你的'子类''属性为非null等等。有帖子就可以了。

希望这会有所帮助。

编辑:如果你想控制'隐藏'鉴别器I think,你可以尝试专门手动调整该列的migration file,设置它的大小等。如果这很重要(代码首先应该在那里选择正确的值我猜)。如果你没有改变任何重要的东西,那么事情应该如何发挥作用。

答案 1 :(得分:1)

问题在于,当您使用列作为TPH映射的鉴别器时,您也无法将该列映射到类中的属性。这是因为EF现在根据.NET类的类型控制该列的值。因此,您应该删除执行Loc_Type属性映射到Location列的这一行:

// Remove this line:
this.Property(t => t.Loc_Type).HasColumnName("Loc_Type").HasColumnType("varchar").HasMaxLength(50).IsRequired();

如果您需要(或想要)为discriminator列指定显式列类型,大小等,那么您可以在Map调用中执行此操作。例如:

Map(m =>
{
    m.ToTable("Location");
    m.Requires("Loc_Type")
        .HasValue("Location")
        .HasColumnType("varchar")
        .HasMaxLength(50)
        .IsRequired();
}

您不需要在表示位置类型的类中包含任何属性。如果你想获得一个等价于Loc_Type的字符串值,那么你可以在Location:

中使用这样的东西
public virtual string Loc_Type
{
    get { return "Location"; }
}

然后在其他类中覆盖它。例如:

public override string Loc_Type
{
    get { return "RadioStation"; }
}

其余的代码看起来很好。