使用实体框架代码优先库的CTP 5(如宣布here)时,我正在尝试创建一个映射到非常简单的层次表的类。
这是构建表的SQL:
CREATE TABLE [dbo].[People]
(
Id uniqueidentifier not null primary key rowguidcol,
Name nvarchar(50) not null,
Parent uniqueidentifier null
)
ALTER TABLE [dbo].[People]
ADD CONSTRAINT [ParentOfPerson]
FOREIGN KEY (Parent)
REFERENCES People (Id)
这是我希望自动映射回该表的代码:
class Person
{
public Guid Id { get; set; }
public String Name { get; set; }
public virtual Person Parent { get; set; }
public virtual ICollection<Person> Children { get; set; }
}
class FamilyContext : DbContext
{
public DbSet<Person> People { get; set; }
}
我在app.config文件中设置了connectionstring,如下所示:
<configuration>
<connectionStrings>
<add name="FamilyContext" connectionString="server=(local); database=CodeFirstTrial; trusted_connection=true" providerName="System.Data.SqlClient"/>
</connectionStrings>
</configuration>
最后,我正在尝试使用该类添加父类和子实体,如下所示:
static void Main(string[] args)
{
using (FamilyContext context = new FamilyContext())
{
var fred = new Person
{
Id = Guid.NewGuid(),
Name = "Fred"
};
var pebbles = new Person
{
Id = Guid.NewGuid(),
Name = "Pebbles",
Parent = fred
};
context.People.Add(fred);
var rowCount = context.SaveChanges();
Console.WriteLine("rows added: {0}", rowCount);
var population = from p in context.People select new { p.Name };
foreach (var person in population)
Console.WriteLine(person);
}
}
这里显然缺少一些东西。我得到的例外是:
无效的列名称'PersonId'。
我理解约定对配置的价值,我和我的团队对放弃edmx /设计师噩梦的前景感到非常兴奋 - 但似乎没有关于约定的明确文件。 (我们只是幸运地参与了多个表名的概念,对于单数类名)
如何使这个非常简单的例子落实到位的一些指导将不胜感激。
更新:
将People表中的列名从Parent更改为PersonId允许继续添加fred
。然后你会注意到pebbles
被添加到fred
的儿童集合中,因此我希望在添加Fred时将鹅卵石添加到数据库中,但事实并非如此。这是一个非常简单的模型,所以我有点沮丧的是,在将几行输入数据库时应该进行这么多的猜测工作。
答案 0 :(得分:13)
您需要下拉到流畅的API以实现所需的架构(数据注释不会这样做)。确切地说,您有一个Independent One-to-Many Self Reference Association
,它还具有外键列(People.Parent)的自定义名称。以下是它应该如何完成EF Code First:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasOptional(p => p.Parent)
.WithMany(p => p.Children)
.IsIndependent()
.Map(m => m.MapKey(p => p.Id, "ParentID"));
}
但是,这会抛出InvalidOperationException
此消息 Sequence包含多个匹配元素。根据Steven在回答中提到的链接,这听起来像是一个CTP5错误。
您可以使用解决方法,直到在RTM中修复此错误,并接受FK列的默认名称PersonID
。为此,您需要稍微更改架构:
CREATE TABLE [dbo].[People]
(
Id uniqueidentifier not null primary key rowguidcol,
Name nvarchar(50) not null,
PersonId uniqueidentifier null
)
ALTER TABLE [dbo].[People] ADD CONSTRAINT [ParentOfPerson]
FOREIGN KEY (PersonId) REFERENCES People (Id)
GO
ALTER TABLE [dbo].[People] CHECK CONSTRAINT [ParentOfPerson]
GO
然后使用这个流畅的API将您的数据模型与DB Schema匹配:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasOptional(p => p.Parent)
.WithMany(p => p.Children)
.IsIndependent();
}
using (FamilyContext context = new FamilyContext())
{
var pebbles = new Person
{
Id = Guid.NewGuid(),
Name = "Pebbles",
};
var fred = new Person
{
Id = Guid.NewGuid(),
Name = "Fred",
Children = new List<Person>()
{
pebbles
}
};
context.People.Add(fred);
context.SaveChanges();
}
using (FamilyContext context = new FamilyContext())
{
var fred = new Person
{
Id = Guid.NewGuid(),
Name = "Fred",
};
var pebbles = new Person
{
Id = Guid.NewGuid(),
Name = "Pebbles",
Parent = fred
};
context.People.Add(pebbles);
var rowCount = context.SaveChanges();
}
两个代码都具有相同的效果,即添加一个带有子(Pebbles)的新父(Fred)。
答案 1 :(得分:0)
它应该使用如下的映射:
class FamilyContext : DbContext
{
public DbSet<Person> People { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Person>().HasMany(x => x.Children).WithMany().Map(y =>
{
y.MapLeftKey((x => x.Id), "ParentID");
y.MapRightKey((x => x.Id), "ChildID");
});
}
}
然而,抛出异常:Sequence包含多个匹配元素。 显然这是一个错误。
请参阅此主题以及@shichao问题的答案:http://blogs.msdn.com/b/adonet/archive/2010/12/06/ef-feature-ctp5-fluent-api-samples.aspx#10102970
答案 2 :(得分:0)
在Entity Framework 6中,您可以这样做,请注意public Guid? ParentId { get; set; }
。外键必须为空才能正常工作。
class Person
{
public Guid Id { get; set; }
public string Name { get; set; }
public Guid? ParentId { get; set; }
public virtual Person Parent { get; set; }
public virtual ICollection<Person> Children { get; set; }
}
答案 3 :(得分:0)
class Person
{
[key()]
public Guid Id { get; set; }
public String Name { get; set; }
[ForeignKey("Children")]
public int? PersonId {get; set;} //Add ForeignKey
public virtual Person Parent { get; set; }
public virtual ICollection<Person> Children { get; set; }
}
builder.Entity<Menu>().HasMany(m => m.Children)
.WithOne(m => m.Parent)
.HasForeignKey(m => m.PersonId);