5

I have two classes. A Company has a County set against it:

public class Company
{
    public int Id { get; set; }
    public string CompanyName { get; set; }
    public Country HomeCountry { get; set; }
}

public class Country
{
    public int Id { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
}

I am trying to map to an existing database where the Company table contains the foreign key of the Country record. So I presumably need to tell code first the name of the foreign key column.

Below is the complete code example. It's currently failing with different exceptions based on different things that I try. There's seems to be a lack of cohesive documentation on this as yet.

So using Code First Fluent API how do I define the name of the foreign key column?

Test app:

Create database as follows: CREATE DATABASE CodeFirst; GO

Use CodeFirst

create table Companies
(
    Id int identity(1,1) not null,
    HomeCountryId int not null,
    Name varchar(20) not null,
    constraint PK_Companies primary key clustered (Id)
)

create table Countries
(
   Id                    int identity(1,1) not null
,  Code                  varchar(4)        not null                
,  Name                  varchar(20)       not null                
,  constraint PK_Countries primary key clustered (Id)         
)

alter table Companies
  add
     constraint FK_Company_HomeCountry foreign key (HomeCountryId)
        references Countries (Id) on delete no action

Now run the following C# app:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity.ModelConfiguration;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Data;

namespace CodeFirstExistingDatabase
{

    class Program
    {
        private const string ConnectionString = @"Server=.\sql2005;Database=CodeFirst;integrated security=SSPI;";

        static void Main(string[] args)
        {

            // Firstly, add a country record, this works fine.
            Country country = new Country();
            country.Code = "UK";
            country.Name = "United Kingdom";

            MyContext myContext = new MyContext(ConnectionString);
            myContext.Countries.Add(country);
            myContext.Entry(country).State = EntityState.Added;
            myContext.SaveChanges();
            Console.WriteLine("Saved Country");

            // Now insert a Company record
            Company company = new Company();
            company.CompanyName = "AccessUK";
            company.HomeCountry = myContext.Countries.First(e => e.Code == "UK");

            myContext.Companies.Add(company);
            myContext.Entry(company).State = EntityState.Added;
            myContext.Entry(country).State = EntityState.Unchanged;
            myContext.SaveChanges();

            Console.WriteLine("Saved Company"); // If I can get here I'd he happy!

        }
    }

    public class MyContext
        : DbContext
    {
        public DbSet<Company> Companies { get; set; }

        public DbSet<Country> Countries { get; set; }

        public MyContext(string connectionString)
            : base(connectionString)
        {
            Database.SetInitializer<MyContext>(null);
            Configuration.LazyLoadingEnabled = false;
            Configuration.ProxyCreationEnabled = false;
        }
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new CountryConfiguration());
            modelBuilder.Configurations.Add(new CompanyConfiguration());

            base.OnModelCreating(modelBuilder);
        }
    }

    public class CompanyConfiguration
        : EntityTypeConfiguration<Company>
    {

        public CompanyConfiguration()
            : base()
        {

            HasKey(p => p.Id);
            Property(p => p.Id)
                .HasColumnName("Id")
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
                .IsRequired();
            Property(p => p.CompanyName)
                .HasColumnName("Name")
                .IsRequired();
            ToTable("Companies");
        }

    }

    public class CountryConfiguration
        : EntityTypeConfiguration<Country>
    {

        /// <summary>
        /// Initializes a new instance of the <see cref="CountryConfiguration"/> class.
        /// </summary>
        public CountryConfiguration()
            : base()
        {

            HasKey(p => p.Id);
            Property(p => p.Id)
                .HasColumnName("Id")
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
                .IsRequired();
            Property(p => p.Code)
                .HasColumnName("Code")
                .IsRequired();
            Property(p => p.Name)
                .HasColumnName("Name")
                .IsRequired();

            ToTable("Countries");
        }

    }

    public class Company
    {
        public int Id { get; set; }
        public string CompanyName { get; set; }
        public Country HomeCountry { get; set; }
    }

    public class Country
    {
        public int Id { get; set; }
        public string Code { get; set; }
        public string Name { get; set; }
    }
}

The above fails with the following when saving the country: Invalid column name 'HomeCountry_Id

Any help would be very much appreciated!!

Thanks, Paul.

P2l
  • 915
  • 2
  • 11
  • 25
  • Mapping against existing database **is not code first** it is using fluent-api in database first scenario. – Ladislav Mrnka Apr 07 '11 at 16:54
  • "Code First also can be used in scenarios where you have an existing database" - http://romiller.com/2010/07/18/ef-ctp4-tips-tricks-mapping-to-an-existing-database/ – P2l Apr 07 '11 at 17:48
  • Just think about that words: Code-first = I will code before I have any database and my code will define the database. Fluent API != code-first. – Ladislav Mrnka Apr 07 '11 at 18:11
  • 2
    @Ladislav: I am coding first (as in creating the entities) but then manually creating the database (rather than letting EF do it). For simplicity here though I said it's an existing database (rather than one EF Code First creates), but that is neither here nor there. I'm basing my terminology on the blog post by Rowan Miller who works on the EF team which I refer to above. – P2l Apr 07 '11 at 18:25

2 Answers2

5
public CompanyConfiguration()
{
    //...
    HasRequired(x => x.HomeCountry).WithMany()
        .Map(x => x.MapKey("HomeCountryId"));
}
Diego Mijelshon
  • 52,548
  • 16
  • 116
  • 154
  • Excellent, thanks, that does indeed work! Could you explain the syntax though? Why WithMany? I only have one Country associated with a Company. Sorry but the intellisense doesn't help much. – P2l Apr 07 '11 at 13:27
  • @Paul: you'd have to ask the ADO.NET team... I kinda dislike it too. WithMany is used to specify the "one-to-many" side using an expression; in this case the relationship is unidirectional, that's why it's empty. – Diego Mijelshon Apr 07 '11 at 13:31
0

We are moving a Web Forms app to MVC3 using Code First against an existing db without any problems. Here are 2 sample Models and the DbContext I'm using. prDepartments & prCategories map to tables in the db and ApplicationDBContext matches the connection string in Web.config

The DeptID field in prCategory is a Foreign Key to prDepartment - Everything works great

public class prCategory
{
    [Key]
    public int CatgID { get; set; }
    public int DeptID { get; set; }
    [Required(ErrorMessage="Category Description Is Required")]
    [DisplayName("Desc Name")]
    [CssClass("ui-Field-Name")]
    public string Description { get; set; }
    public string Route { get; set; }
    public string OrderBy { get; set; }
    public virtual prDepartment Department { get; set; }
    public virtual List<prProduct> prProducts { get; set; }
}

public class prDepartment
{
    [Key]
    public int DeptID { get; set; }
    [Required(ErrorMessage = "Department Description Is Required")]
    [RequiredMessage("This is the Required Message")]
    public string Description { get; set; }
    public string Route { get; set; }
    public string OrderBy { get; set; }

    public virtual List<prCategory> prCategories { get; set; }
}

    public class ApplicationDbContext : DbContext
{
        public DbSet<prDepartment> prDepartments { get; set; }
        public DbSet<prCategory> prCategories { get; set; }
        public DbSet<prProduct> prProducts { get; set; }
}
  • I've edited the original question slightly as I've now noticed that what it's doing in my case is expecting a foreign key column named 'HomeCountry_Id' when in actual fact it's called 'HomeCountryId'. So my question really is how do I tell Code First what the foreign key column is called? – P2l Apr 07 '11 at 12:01