如何访问具有复合键的表我无法使用Code First建模?

时间:2012-07-19 21:01:17

标签: entity-framework ef-code-first poco code-first entity-framework-5

我有一个现有的数据库,我无法更改,但想要使用EF访问。对于90%的数据库,我已经能够使Code First EF工作,我印象非常深刻。

我遇到了一个案例,我想知道如何通过导航属性建模或访问数据。

在一种情况下,表格是这样的(这个例子是完全组成的,但代表了问题):

CREATE TABLE Dog (
    id            INTEGER      PRIMARY KEY,
    name          VARCHAR(50)  NULL,
    breed_id      INTEGER      NOT NULL
);

CREATE TABLE Breed (
    id               INTEGER      NOT NULL,
    organization_id  INTEGER      NOT NULL,
    description      VARCHAR(100) NOT NULL, 
    Primary key (id, organization)
);

CREATE TABLE Organization (
    id            INTEGER      PRIMARY KEY,
    description   VARCHAR(100) NOT NULL
);

在Table品种中,组织代表已定义品种的组织。狗可以有多个品种,但程序只显示一个,结果由组织ID“过滤” - 这是在程序设置时配置的值。

可能存在的数据示例如下:

id    organizationID    Description
 1                 1    Basset Hound
 2                 1    Great Dane
 2                 2    Grande Dane

组织2选择调用与组织不同的品种1.唯一主键是id和organizationID的组合。狗有品种,但没有定义一个或多个相关组织的属性。它需要来自另一个表的附加信息,或者配置的值(可能是枚举值)来查找狗的品种。

在我的情况下,要找到一个特定的狗品种,你必须有一个狗id和另一条与程序配置相关的信息(organization_id)。

狗,品种和组织类看起来像这样:

public class Dog {
  public int id  { get; set; };
  public string name  { get; set; };
  public int breedID  { get; set; };

  public virtual Breed { get; set; }           
}

public class Breed {
  public int id  { get; set; };
  public int organizationID  { get; set; };            
  public string description  { get; set; };

  public virtual Organization { get; set; }  
}

public class Organization {
  public int id  { get; set; };
  public string description  { get; set; };
}

如代码中所示,我想在Dog上使用返回品种的“Navigation”属性,但我认为我不能先在代码中配置它。

我尝试了一些不同的东西(流畅的API,并让组织离开 - 因为这很容易)并且还会记录我认为不起作用的事情:

1) 
   modelBuilder.Entity<Dog>().HasKey(t => t.id);
   modelBuilder.Entity<Breed>().HasKey(t => t.id);
   modelBuilder.Entity<Dog>().HasRequired(d => d.Breed).WithMany().HasForeignKey(d => d.breedID);

当然,问题在于,将返回多个品种,实体框架将抛出​​异常,因为品种ID本身不足以产生单个值 - 模型正在调用该值。

2) 
   Change class dog:
      Remove:
          public virtual Breed { get; set; }
      Add:
          public virtual ICollection<Breed> Breeds { get; set; }

          public Breed Breed {
            get {
              // Assume 1 is configured organization value
              return Breeds.Single(t => t.OrganizationId == 1);
            };
            set {
              Breeds.Add(value);
            }
          }

     Change model:
        I don't know how to do this for the given classes.  Since it's a Collection, it must look something like

        modelBuilder.Entity<Dog>().HasMany(d => d.Breeds)...

        but I don't see how to specify that breedID is a foriegn key into the breed table.

如果我能让这个模型发挥作用,其余的都会起作用,但它确实看起来很奇怪且效率低下。

 3)
    Change model to account for composite key (using first set of classes):
       modelBuilder.Entity<Breed>().HasKey(b => new { b.id, b.organizationId });

       modelBuilder.Entity<Dog>().HasRequired(t => t.Breed).WithMany().HasForeignKey(t => new { t.breedID, configuredValue });

我不知道如何在最后一行“注入”configuredValue,所以这也不起作用。

如果以上方法都不起作用,或者我找不到另一种方法来首先正确配置代码,那么我想指定当调用Breed导航属性getter时,它应该使用一个可以查询的查询获得适当的品种并适当地返回。

但是,我不希望使用Context调用来弄脏我的POCO以返回查询结果。换句话说,我想在Dog上有一个看起来不像这样的属性:

public Breed Breed {
   get {
      return context.Breed.Where(b => b.id == this.id && b.organizationID == 1).Single();
   }
}   

理想情况下,它会像导航集合那样工作,其中EF使其具有魔力,并返回适当的结果。

直观地说,似乎我应该能够使用类似POCO的代码来配置它,或者使用/扩展代理来扩展配置以在调用访问器时使用我想要的特定查询。或者 - 似乎我应该能够在任何读取时填充属性并在写入时弄脏POCO。我对EF不熟悉,知道如何做到这一点。

这可能吗?

作为第一篇文章的补充,因为我希望保持我的POCO类干净,我想我可能会实现Repository模式来封装像我所描述的复杂查询,以及支持其他操作

看看我从Code First模型生成的EDMX,从数据库的第一个角度来看,如何实现模型并不明显。

非常感谢任何想法。

1 个答案:

答案 0 :(得分:0)

简而言之,你不能这样做。你有不合逻辑的数据库结构(外键引用非唯一列),你必须将它映射到某个地方。你不能在EF配置中这样做,因为它是动态的,所以你必须在你的实体类中做到这一点。如果没有直接调用实体类(或辅助类)中的上下文,我认为没有办法做到这一点。