我有一个现有的数据库,我无法更改,但想要使用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,从数据库的第一个角度来看,如何实现模型并不明显。
非常感谢任何想法。
答案 0 :(得分:0)
简而言之,你不能这样做。你有不合逻辑的数据库结构(外键引用非唯一列),你必须将它映射到某个地方。你不能在EF配置中这样做,因为它是动态的,所以你必须在你的实体类中做到这一点。如果没有直接调用实体类(或辅助类)中的上下文,我认为没有办法做到这一点。