简体   繁体   中英

How can I add a base table to an existing entity creating TPH model using Entity Framework?

I have an existing table in my database called CarDesign that has a one to one relationship with Project. I want to add a base table called Design to use in a TPH table. So then I can add discriminators for things like BoatDesign. The problem is entity framework wants to just drop the old table and make a new one. How can I keep my data and have a discriminator to represent the current records?

I'm using entity framework 5 with SQL. I am also going to update to entity framework 6 if that helps the issue. Project is asp.net MVC4 with c#

Mapping

public class CarDesignMap : EntityTypeConfiguration<CarDesign>
    {
        public CarDesignMap()
        {
            // Primary Key
            this.HasKey(t => t.Id);

            // Properties
            //this.Property(t => t.Name)
            //.IsRequired();

            this.Property(t => t.RowVersion)
                .IsRequired()
                .IsFixedLength()
                .HasMaxLength(8)
                .IsRowVersion();



            // Relationships
            this.HasMany(t => t.InputVoltages)
                .WithMany(t => t.CarDesign)
                .Map(m =>
                {
                    m.ToTable("InputVoltageCarDesign");
                    m.MapLeftKey("CarDesign_Id");
                    m.MapRightKey("InputVoltage_Id");
                });

            this.HasMany(t => t.LensColors)
                .WithMany(t => t.CarDesign)
                .Map(m =>
                {
                    m.ToTable("LensCarDesign");
                    m.MapLeftKey("CarDesign_Id");
                    m.MapRightKey("LensColor_Id");
                });

            this.HasMany(t => t.LightSourceColors)
                .WithMany(t => t.CarDesign)
                .Map(m =>
                {
                    m.ToTable("LightSourceColorCarDesign");
                    m.MapLeftKey("CarDesign_Id");
                    m.MapRightKey("LightSourceColor_Id");
                });

            this.HasMany(t => t.Standards)
                .WithMany(t => t.CarDesign)
                .Map(m =>
                {
                    m.ToTable("StandardCarDesign");
                    m.MapLeftKey("CarDesign_Id");
                    m.MapRightKey("Standard_Id");
                });

            this.HasOptional(t => t.LightSource)
                .WithMany(t => t.CarDesign)
                .HasForeignKey(d => d.LightSourceId);
        }
    }

model

public class CarDesign
{
    public CarDesign()
    {
        Approvals = new List<Approval>();
        Standards = new List<Standard>();
        Connectors = new List<Connector>();
        InputVoltages = new List<InputVoltage>();
        LensColors = new List<LensColor>();
        LightSourceColors = new List<LightSourceColor>();
    }


    [Required]
    [Display(Name = "Product Name")]
    public string Name { get; set; }

    public PdsStatus Status { get; set; }

    public Guid SubmittedById { get; set; }
    [Display(Name = "Submitted By")]
    public virtual User SubmittedBy { get; set; }

    public Guid? ApprovedById { get; set; }
    [Display(Name = "Approved By")]
    public virtual User ApprovedBy { get; set; }

    [Display(Name = "Approval Date")]
    [DataType(DataType.Date)]
    public DateTime? ApprovalDate { get; set; }

    [Display(Name = "Submit Date")]
    [DataType(DataType.Date)]
    public DateTime SubmittalDate { get; set; }
    public string SubmittalDateDisplay
    {
        get
        {
            return (SubmittalDate == null) ? "Not Set" : ((DateTime)SubmittalDate).ToString("MM/dd/yy");
        }
    }

    public string Description { get; set; }

    [Display(Name = "Market and Uses")]
    public string MarketAndUses{get;set;}

    [Display(Name = "Target M & L")]
    [DataType(DataType.Currency)]
    public double? TargetPrice { get; set; }



    [Display(Name = "Annual Qty")]
    public int? AnnualQuantities { get; set; }

    [Display(Name = "Current Draw")]
    public double? CurrentDraw { get; set; }



    [Display(Name = "Light Source")]
    public int? LightSourceId { get; set; }
    public virtual LightSource LightSource { get; set; }



    public LengthUnitOfMeasure LengthUnitOfMeasure { get; set; }
    public WeightUnitOfMeasure WeightUnitOfMeasure { get; set; }
    public PhotometricIntensityUnitOfMeasure LightIntensityUnitOfMeasure{ get; set; }

    public virtual ICollection<Approval> Approvals { get; set; }
    public virtual ICollection<Standard> Standards { get; set; }
    public virtual ICollection<Connector> Connectors { get; set; }
    public virtual ICollection<InputVoltage> InputVoltages { get; set; }
    public virtual ICollection<LensColor> LensColors { get; set; }
    public virtual ICollection<LightSourceColor> LightSourceColors { get; set; }
        }
    }

so I would desire to have

public class CarDesign : Design

{

}

Since you want to adopt TPH inheritance, EF will have to create a table for the base class Design , thus droping your old CarDesign table. You can try to use migrations to avoid losing data but I've never tried to implement TPH with existing data so I cannot garantee that migrations will work for it (I'm afraid it probably won't).

As for the discriminator, EF already handles this for you.

The classes would look like this:

public abstract class Design
{
    Public int Id { get; set; }
    ...
}

public class CarDesign : Design
{
    ...
}

public class BoatDesign : Design
{
    ...
}

And then, you can choose to create a single DbSet for Design or you can create one DbSet for each subtype. Your choice here:

public DbSet<Design> Designs { get; set; }

or:

public DbSet<CarDesign> CarDesigns { get; set; }
public DbSet<BoatDesign> BoatDesigns { get; set; }

Or you can have all three DbSets.

In case you want query Designs DbSet and have it return only CarDesigns, you can do it like this:

var finishedCarDesigns = context.Designs.OfType<CarDesign>().Where(cd => cd.Finished = true);

Also, you may find this link useful: http://weblogs.asp.net/manavi/archive/2010/12/24/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-1-table-per-hierarchy-tph.aspx

If you started with Foo :

public class Foo
{
    public int ID { get; set; }
    public string Name { get; set; }
    public int BarID { get; set; }
    public virtual Bar Bar { get; set; }
}

Then added a base class Baz

public class Foo : Baz
{
    public string Name { get; set; }
    public int BarID { get; set; }
    public virtual Bar Bar { get; set; }
}

public class Baz
{
    public int ID { get; set; }
    public int ApprovedBy { get; set; 
}

You could add your sql to the migration like this:

public override void Up()
{
    DropForeignKey("dbo.Foos", "BarID", "dbo.Bars");
    DropIndex("dbo.Foos", new[] { "BarID" });
    CreateTable(
        "dbo.Bazs",
        c => new
            {
                ID = c.Int(nullable: false, identity: true),
                ApprovedBy = c.Int(nullable: false),
                Name = c.String(),
                BarID = c.Int(),
                Discriminator = c.String(nullable: false, maxLength: 128),
            })
        .PrimaryKey(t => t.ID)
        .ForeignKey("dbo.Bars", t => t.BarID)
        .Index(t => t.BarID);

    //Put your sql here to run before the table is dropped
    Sql(@"INSERT INTO dbo.Bazs (Name, BarID, Discriminator) 
          SELECT Name, BarID, 'Foo' FROM dbo.Foos");

    DropTable("dbo.Foos");
}

The sql gets more complicated if you have foreign keys to fix up. The only other thing I can think of is to go in stages:

  1. Make BoatDesign inherit from CarDesign and Migrate (a discriminator column will be added to the existing table)

  2. Add a new class CarDesign2 that also inherits from CarDesign (no database change required)

  3. Move the properties applicable only to CarDesign into CarDesign2 (no database change required)

  4. Script a table name change in the database for CarDesigns to Designs (Sql Server will generate a script that will move the data as well as drop and re-create)

  5. In the code change CarDesign to Design and CarDesign2 to CarDesign

  6. Look at the migration created by step 5. If it is dropping and recreating the CarDesign table then replace it with the sql generated in step 4, If it isn't woo-hoo!

  7. Let us know how you get on

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM