EF 4.1 RC Code First - 映射到现有数据库并指定外键名称

Posted

技术标签:

【中文标题】EF 4.1 RC Code First - 映射到现有数据库并指定外键名称【英文标题】:EF 4.1 RC Code First - Mapping to existing database & specifying foreign key name 【发布时间】:2011-04-07 10:21:11 【问题描述】:

我有两节课。一家公司有一个反对它的县:

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; 

我正在尝试映射到 Company 表包含 Country 记录的外键的现有数据库。所以我大概需要先告诉代码外键列的名称。

以下是完整的代码示例。根据我尝试的不同事情,它目前因不同的例外而失败。似乎目前还缺乏相关的文档。

那么使用 Code First Fluent API 如何定义外键列的名称?

测试应用:

创建数据库如下: 创建数据库 CodeFirst; 去吧

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

现在运行以下 C# 应用程序:

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; 
    

上述方法在拯救国家时失败并出现以下情况: 列名“HomeCountry_Id”无效

任何帮助将不胜感激!

谢谢,保罗。

【问题讨论】:

对现有数据库的映射不是代码优先它在数据库优先场景中使用 fluent-api。 “Code First 也可用于已有数据库的场景” - romiller.com/2010/07/18/… 想想这句话:代码优先 = 我会在我有任何数据库之前编码,我的代码将定义数据库。 Fluent API != 代码优先。 @Ladislav:我先编码(如创建实体),然后手动创建数据库(而不是让 EF 来做)。为了简单起见,虽然我说它是一个现有的数据库(而不是一个 EF Code First 创建的数据库),但这既不是这里也不是那里。我的术语基于我在上面提到的 EF 团队工作的 Rowan Miller 的博客文章。 【参考方案1】:
public CompanyConfiguration()

    //...
    HasRequired(x => x.HomeCountry).WithMany()
        .Map(x => x.MapKey("HomeCountryId"));

【讨论】:

太好了,谢谢,确实有效!你能解释一下语法吗?为什么选择 WithMany?我只有一个国家与公司相关联。抱歉,智能感知并没有太大帮助。 @Paul:你得问问 ADO.NET 团队......我也有点不喜欢它。 WithMany 用于使用表达式指定“一对多”端;在这种情况下,关系是单向的,这就是它为空的原因。【参考方案2】:

我们正在使用 Code First 针对现有数据库将 Web 表单应用程序迁移到 MVC3,没有任何问题。这是我正在使用的 2 个示例模型和 DbContext。 prDepartments & prCategories 映射到数据库中的表,ApplicationDBContext 匹配 Web.config 中的连接字符串

prCategory 中的 DeptID 字段是 prDepartment 的外键 - 一切正常

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; 

【讨论】:

我已经稍微编辑了原始问题,因为我现在注意到它在我的案例中所做的是期待一个名为“HomeCountry_Id”的外键列,而实际上它被称为“HomeCountryId”。所以我的问题实际上是如何告诉 Code First 外键列的名称?

以上是关于EF 4.1 RC Code First - 映射到现有数据库并指定外键名称的主要内容,如果未能解决你的问题,请参考以下文章

EF 4.1 中使用 Code First 的 ComplexType 集合属性

在使用 EF 4.1 Code-First 的 Include 和/或 Select 方法时订购导航属性?

EF 4.1 Code-First 项目上的 MvcMiniProfiler 不分析 SQL

EF 4.1 Code First 添加到外键集合

EF 4.1 Code First - 我应该使用啥模式?

使用EF 4.1 Fluent Code First的每类型表继承