locked
Help Mapping EF Code First to an Existing Database RRS feed

  • Question

  • Hello.  First let me apologize for the lengthy post.  I figured giving more details would help you understand the situation.

    I’m trying to develop the data layer of my application using EF 5 Code First and mapping everything to an existing database.  I’m wanting to use DbContext, POCOs, fluent table mappings, etc., but our existing database (the tables) isn’t straight-forward.  So, I’m having trouble with a few things.  I’ll try to briefly describe the situation now, then ask a couple questions later.

    Tables

    AlarmDefinition
    AlarmDefinitionID int (primary key) (is identity)
    ...
    
    Alarm
    AlarmID int (primary key) (is identity)
    AlarmDefinitionID int (sort of a foreign key) (is nullable)
    SampleTagID int (is nullable)
    SampleTime DateTime (is nullable)
    ...
    
    ReasonAction
    Time DateTime (primary key)
    TagID int (primary key)
    ReasonActionID int (primary key)
    
    ReasonActionDefinition
    ReasonActionID int (primary key) (is identity)
    ...
    

    So, one AlarmDefinition can have many Alarms.  And, one ReasonActionDefinition can have many ReasonActions.  There is an implicit relationship between Alarm and ReasonAction (i.e., one Alarm can have many ReasonActions).  This is where I’m having the most troubles.

    POCOs

    AlarmDefinition
    public class AlarmDefinition
    {
        public int AlarmDefinitionID { get; set; }
        ...
        public virtual ICollection<Alarm> Alarms { get; set; }
    }
    
    Alarm
    public class Alarm
    {
        public int AlarmID { get; set; }
        public Nullable<int> SampleTagID { get; set; }
        public Nullable<System.DateTime> SampleTime { get; set; }
        ...
        public Nullable<int> AlarmDefinitionID { get; set; }
        public virtual AlarmDefinition AlarmDefinition { get; set; }
        // I don’t know if this is set up correctly
        public virtual ICollection<ReasonAction> ReasonActions { get; set; }
    }
    
    ReasonAction
    public class ReasonAction
    {
        public System.DateTime Time { get; set; }
        public int TagID { get; set; }
        public virtual Alarm Alarm { get; set; }
        public int ReasonActionID { get; set; }  // Primary key
        public virtual ReasonActionDefinition ReasonActionDefinition { get; set; }
    }
    
    ReasonActionDefinition
    public class ReasonActionDefinition
    {
        public int ReasonActionID { get; set; }
        ...
        public virtual ICollection<ReasonAction> ReasonActions { get; set; }
    }
    

    DbContext

    public class AppDbContext : DbContext
    {
        public DbSet<Alarm> Alarms { get; set; }
        public DbSet<AlarmDefinition> AlarmDefinitions { get; set; }
        public DbSet<ReasonAction> ReasonActions { get; set; }
        public DbSet<ReasonActionDefinition> ReasonActionDefinitions { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new AlarmMap());
            modelBuilder.Configurations.Add(new AlarmDefinitionMap());
            modelBuilder.Configurations.Add(new ReasonActionMap());
            modelBuilder.Configurations.Add(new ReasonActionDefinitionMap());
        }
    }
    

    Fluent Mappings

    AlarmDefinitionMap
    public class AlarmDefinitionMap : EntityTypeConfiguration<AlarmDefinition>
    {
        public AlarmDefinitionMap()
        {
            this.HasKey(t => t.AlarmDefinitionID);
            ...
            this.ToTable("AlarmDefinition");
            this.Property(t => t.AlarmDefinitionID).HasColumnName("AlarmDefinitionID");
            ...
        }
    }
    
    AlarmMap
    public class AlarmMap : EntityTypeConfiguration<Alarm>
    {
        public AlarmMap()
        {
            this.HasKey(t => t.AlarmID);
            ...
            this.ToTable("Alarm");
            this.Property(t => t.AlarmID).HasColumnName("AlarmID");
            this.Property(t => t.AlarmDefinitionID).HasColumnName("AlarmDefinitionID");
            this.Property(t => t.SampleTagID).HasColumnName("SampleTagID");
            this.Property(t => t.SampleTime).HasColumnName("SampleTime");
            ...
            this.HasOptional(t => t.AlarmDefinition)
                .WithMany(d => d.Alarms)
                .HasForeignKey(t => t.AlarmDefinitionID);
        }
    }
    
    ReasonActionMap
    public class ReasonActionMap : EntityTypeConfiguration<ReasonAction>
    {
        public ReasonActionMap()
        {
            this.HasKey(t => new { t.Time, t.TagID, t.ReasonActionID });
            this.Property(t => t.TagID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
            this.Property(t => t.ReasonActionID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
            this.ToTable("ReasonAction");
            this.Property(t => t.Time).HasColumnName("Time");
            this.Property(t => t.TagID).HasColumnName("TagID");
            this.Property(t => t.ReasonActionID).HasColumnName("ReasonActionID");
            // Relationships
            // Not sure if this is correct since the related column names in the Alarm table are SampleTagId, not TagID and SampleTime, not Time.  And SampleTagID and SampleTime in the Alarm table are not a primary keys.
            // There's an implicit one-to-many relationship between the Alarm and ReasonAction entities.
            this.HasRequired(t => t.Alarm)
                .WithMany(d => d.ReasonActions)
                .HasForeignKey(t => new { t.TagID, t.Time });
            this.HasRequired(t => t.ReasonActionDefinition)
                .WithMany(d => d.ReasonActions)
                .HasForeignKey(t => t.ReasonActionID);
        }
    }
    
    ReasonActionDefinitionMap
    public class ReasonActionDefinitionMap : EntityTypeConfiguration<ReasonActionDefinition>
    {
        public ReasonActionDefinitionMap()
        {
            this.HasKey(t => t.ReasonActionID);
            ...
            this.ToTable("ReasonActionDefinition");
            this.Property(t => t.ReasonActionID).HasColumnName("ReasonActionID");
            ...
        }
    }
    

    Whew!  That was a lot of stuff.  Anyway, here are some issues with our database and mapping things to EF: 1.  No declarative referential integrity – all handled in triggers or legacy application code, 2.  In the Alarm table, SampleTagID and SampleTime are not primary keys, yet these two columns are the one-side for an implicit one-to-many relation with the ReasonAction table, 3.  Column names between the Alarms and ReasonAction tables do not match (SampleTagID and SampleTime for the Alarm table and Time and TagID for the ReasonAction table).


    So, my questions are: A.  Given the situation described above, can I make EF code first work with my existing database and tables?, B.  How do I need to change my code to make the one-to-many relationship work between the Alarm and ReasonAction tables (so that when I query for Alarms the ReasonActions navigation property is populated with all related records)?, C.  Any other suggestions?

    Thanks!!!

    Thursday, August 23, 2012 3:52 PM

Answers

  • If the database has exist and you want to use poco classes, you needn't to work with code first. 

    1. Using .net4.5 with database first, VS2012 will help you to create the poco classes.

    2. Using VS2010 with database first, after creating models, using DbContext Generator to generate your poco classes.


    Go go Doraemon!

    • Proposed as answer by Doraemon_3 Tuesday, August 28, 2012 1:12 AM
    • Marked as answer by Allen_MSDN Thursday, August 30, 2012 5:23 AM
    Monday, August 27, 2012 9:13 AM

All replies

  • Hi,

    Why don't you use model first directly? I am not clear about this line:  2.  In the Alarm table, SampleTagID and SampleTime are not primary keys, yet these two columns are the one-side for an implicit one-to-many relation with the ReasonAction table,

    Do you want to set SampleTagID and SampleTime in Alarm table as the primary keys?

    Friday, August 24, 2012 9:31 AM
  • If the database has exist and you want to use poco classes, you needn't to work with code first. 

    1. Using .net4.5 with database first, VS2012 will help you to create the poco classes.

    2. Using VS2010 with database first, after creating models, using DbContext Generator to generate your poco classes.


    Go go Doraemon!

    • Proposed as answer by Doraemon_3 Tuesday, August 28, 2012 1:12 AM
    • Marked as answer by Allen_MSDN Thursday, August 30, 2012 5:23 AM
    Monday, August 27, 2012 9:13 AM