我有一个无法更改但想使用 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
);
在表品种中,组织代表已定义品种的组织。一只狗可以有多个品种,但程序只显示一个,结果由组织 ID“过滤” - 这是在设置程序时配置的值。
可能存在的数据示例如下:
id organizationID Description
1 1 Basset Hound
2 1 Great Dane
2 2 Grande Dane
组织 2 选择将品种称为与组织 1 不同的东西。唯一的主键是 id 和组织 ID 的组合。狗有一个品种,但没有定义一个或多个相关组织的属性。它从另一个表中获取附加信息,或者从配置值(可能是枚举值)中获取狗的品种。
在我的例子中,要找到一个特定的狗品种,你必须有一个狗 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 上使用返回品种的“导航”属性,但不要认为我可以先在代码中配置它。
我尝试了一些不同的东西(在流利的 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();
}
}
理想情况下,它会像 Navigation 集合一样工作,EF 在其中发挥神奇作用并返回适当的结果。
直观地说,似乎我应该能够使用类似 POCO 的代码来配置它,或者使用/扩展代理来扩展配置以在调用访问器时使用我想要的特定查询。或者 - 似乎我应该能够在任何读取时填充属性并在写入时弄脏 POCO。我只是对 EF 不够熟悉,不知道如何做到这一点。
这可能吗?
作为第一篇文章的补充,因为我希望保持我的 POCO 类干净,我想我可能会实现 Repository 模式来封装我所描述的复杂查询,并支持其他操作。
查看我从 Code First 模型生成的 EDMX,也不清楚如何从数据库优先的角度实现该模型。
任何想法都非常感谢。