我有一个User
表和一个Avatar
表。一个用户可以拥有许多头像(或null)。但是我需要标记当前的哪个头像,所以我在User表中有一个Avatar_Id
,它是当前的头像。和“阿凡达”中的ForeignKey User_Id
告诉我哪个用户是所有者。
当我尝试填充一些数据以测试关系时,尝试这样做会给我带来很多错误和麻烦。
public class User
{
[Key]
public int Id { get; set; }
public Avatar Avatar { get; set; }
public virtual ICollection<Avatar> Avatars { get; set; }
}
public class Avatar
{
[Key, ForeignKey("User")]
public int Id { get; set; }
public User User { get; set; }
}
测试部分:
var user = new User();
var avatar = new Avatar()
{
User = user
};
// user.Avatar = avatar; // <- this gives [a circular] error; without this I have null.
db.Users.Add(user);
db.Avatars.Add(avatar);
db.SaveChanges();
这导致我Avatar_Id = NULL
表中的User
和User_Id = NULL
表中的Avatar
。我希望这些字段填满(好吧,Avatar_Id
可以为null)。
答案 0 :(得分:1)
最好在表中使用头像制作布尔字段'IsDefault'并检查,同时添加/更新头像,不再为此用户提供默认头像。您也可以在头像类中添加相同的属性。
答案 1 :(得分:1)
@Fabricio我不能在发布之前测试这段代码,但我相信它会起作用。
public class User
{
[Key]
public int UserId { get; set; }
public int AvatarId { get; set; }
[ForeignKey("AvatarId")]
public Avatar Avatar { get; set; }
public ICollection<Avatar> Avatars { get; set; }
}
public class Avatar
{
[Key]
public int AvatarId { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }
public User User { get; set; }
}
问题是你把两个外键合并为一个。现在你在Avatar表中有一个外键,在User表中有一个外键,每个表示一种关系模式。
外键"AvatarId"
表示外键的特殊形式,唯一的+外键(构建一对一关系的第二种形式)。您可以在此处详细了解此信息:http://weblogs.asp.net/manavi/archive/2011/05/01/associations-in-ef-4-1-code-first-part-5-one-to-one-foreign-key-associations.aspx
答案 2 :(得分:0)
我已经给了这个一点但是因为我曾经模拟了一个类似的案例,并且不介意重新评估这些选项。
仔细看看你的处所:
一个用户可以拥有多个头像(或null)
这个简短的句子意味着一对一关联User-Avatar
必须是双向可选的,因为没有 User
的{{1}} 不能可能会引用一个自己的头像,当用户有多个头像时,只有其中一个可以将Avatar
称为用户的默认值。 (它们都将用户称为所有者)。
因此,您只能将其建模为0..1 - 0..1关联。因此User
的主键不能是用户的外键。 (无论如何,否则用户只能拥有一个头像)。
如果这不会引起臭名昭着的“可能导致周期或多个级联路径”异常,那么这可能是由Jonny Piazzi的模型完成的。用户和阿凡达都互相引用,你必须明确告诉EF哪些FK没有级联。这只能通过流畅的映射来完成:
Avatar
这会在用户中放置一个可以为空的非级联FK列protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasOptional(u => u.Avatar)
.WithOptionalDependent()
.Map(m => m.MapKey("AvatarId"))
.WillCascadeOnDelete(false);
...
}
(这就是AvatarId
依赖于User
)的原因。
现在你的第二个问题是填充模型时的鸡 - 例如问题。
只有在您调用Avatar
两次并将这些调用包装在事务范围内时,才能执行此操作。例如:
SaveChanges
现在EF可以在通过外键引用它之前决定首先生成哪个键(using (var tran = new TransactionScope())
{
var user = new User();
var avatar = new Avatar();
user.Avatars = new HashSet<Avatar>();
user.Avatars.Add(avatar);
user.Avatars.Add(new Avatar());
user.Avatars.Add(new Avatar());
db.Users.Add(user);
db.SaveChanges();
user.Avatar = avatar; // set FK
db.SaveChanges();
tran.Complete();
}
)。随后在User
中设置FK。
但......这是最好的模特吗?
也许,也许不是。
问题在于,您的模型不会强制执行业务规则,即用户只能将其自身的某个头像作为默认头像。 User
可以参考任何头像。因此,您必须编写业务逻辑来强制执行业务规则。
使用YD1m的解决方案(无User.AvatarId
,但列User.AvatarId
),此业务规则将被隐式强制执行。但是现在你必须编写业务逻辑来强制只有一个头像是默认的。
由你决定你认为哪种更可行。
(对于记录:回过头来,我采用了后一种选择)