1

我有一张User桌子和一张Avatar桌子。一个用户可以有多个头像(或为空)。但是我需要标记哪个头像是当前的,所以我Avatar_Id在 User 表中有一个是当前头像。Avatar 中的 ForeignKeyUser_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 = NULLUser表内和表User_Id = NULLAvatar。我希望这些字段已填充(嗯,Avatar_Id可以为空)。

4

3 回答 3

1

最好在带有头像的表中创建布尔字段“IsDefault”,并在添加/更新头像时检查该用户没有更多默认头像。您也可以在头像类中添加相同的属性。

于 2013-06-25T12:03:04.777 回答
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

于 2013-06-25T14:27:33.657 回答
0

我已经给出了一点,因为我曾经模拟过一个类似的案例并且不介意重新评估这些选项。

仔细查看您的场所:

一个用户可以有多个头像(或为空)

这个简短的句子意味着一对一关联User-Avatar必须是可选的,因为User 没有 Avatars 的 a 不可能引用一个自己的头像,并且当用户有多个头像时,只有其中一个可以引用User为是用户的默认值。(他们都将用户称为所有者)。

因此,您只能将其建模为 0..1 – 0..1 关联。所以Avatar的主键不能是用户的外键。(无论如何它不能,否则用户只能有一个头像)。

如果这不会引发臭名昭著的“可能导致循环或多级联路径”异常,那么这可能由 Jonny Piazzi 的模型完成。user 和 Avatar 都相互引用,您必须明确告诉 EF 哪些 FK 没有级联。这只能通过流畅的映射来完成:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>()
      .HasOptional(u => u.Avatar)
      .WithOptionalDependent()
      .Map(m => m.MapKey("AvatarId"))
      .WillCascadeOnDelete(false);
    ...
}

AvatarId这会在 User 中放置一个可为空的、非级联的 FK 列(这就是为什么User依赖于Avatar)。


现在你的第二个问题,填充模型时的鸡问题。

这只能在您调用SaveChanges两次并将这些调用包装在事务范围内时完成。例如:

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现在 EF 可以在通过外键引用它之前决定首先生成哪个键 ( 's)。随后您将 FK 设置为User.

但是……这是最好的模型吗?

也许,也许不是。

问题是您的模型没有强制执行用户只能将其自己的化身之一作为默认化身的业务规则。User.AvatarId可以参考任何头像。所以你必须编写业务逻辑来执行业务规则。

使用 YD1m 的解决方案(不是User.AvatarId,而是一个列Avatar.IsDefault),这个业务规则是隐式执行的。但是现在你必须编写业务逻辑来强制只有一个头像是默认的。

由您决定您认为更可行的方法。

(记录在案:回来的时候,我选择了后一种选择)

于 2013-06-25T20:58:13.903 回答