2

我是 CodeFirst 的新手,我正在尝试创建一个 Friends Collection,以便两个用户可以在他们的 Friends Collection 中拥有彼此。

这是我的用户类:

public class User
{
    public User()
    {
        if (FriendCollection == null) FriendCollection = new Collection<Friend>();
    }
    public long ID { get; set; }
    public virtual ICollection<Friend> FriendCollection { get; set; }
}

public class UserConfiguration : EntityTypeConfiguration<User>
{
    public UserConfiguration()
    {
        HasKey(x => x.ID);
    }
}

这是我的朋友班:

public class Friend
{
    public Friend()
    {
        DateAdded = DateTime.UtcNow;
    }
    public long ID { get; set; }
    public DateTime DateAdded { get; set; }
    public long UserID { get; set; }
    public virtual User User { get; set; }
}

public class FriendConfiguration : EntityTypeConfiguration<Friend>
{
    public FriendConfiguration()
    {
        HasKey(x => x.ID);
        HasRequired(x => x.User).WithMany(x => x.FriendCollection).HasForeignKey(x => x.UserID);
    }
}

这是我的实体类:

    public Entities()
    {
        Database.SetInitializer<Entities>(new DropCreateDatabaseAlways<Entities>());
    }

    public DbSet<User> Users { get; set; }
    public DbSet<Friend> Friends { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new UserConfiguration());
        modelBuilder.Configurations.Add(new FriendConfiguration());

        base.OnModelCreating(modelBuilder);
    }
}

这是我的控制台代码,理想情况下是我想要实现的:

        using (Entities db = new Entities ())
        {
            var u1 = new User();
            db.Users.Add(u1);

            var u2 = new User();
            db.Users.Add(u2);

            u1.FriendCollection.Add(new Friend { User = u2 });
            u2.FriendCollection.Add(new Friend { User = u1 });

            try
            {
                var cnt = db.SaveChanges();
            }
            catch (DbEntityValidationException dbEx)
            {
                foreach (var validationErrors in dbEx.EntityValidationErrors)
                {
                    foreach (var validationError in validationErrors.ValidationErrors)
                    {
                        Trace.TraceInformation("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
                    }
                }
            }

每当我尝试使用 User 对象填充 Friend 类时,SaveChanges 都会引发错误:

违反了多重性约束。关系“Entities.Friend_User”的角色“Friend_User_Target”具有多重性 1 或 0..1。

如果我像这样分配给 UserID:

u1.FriendCollection.Add(new Friend { UserID = u2.ID });

这允许我保存实体,但不正确!

当我单步执行代码时,我可以隔离问题,这就是我发现的:

b.FriendCollection.Add(new Friend { UserID = a.ID });
//Friend.UserID = 1
db.SaveChanges();
//Friend.UserID = 2 matching the b.ID

因此,如果我将 Friend.User 分配给调用用户,现在我可以回到我之前的示例和 SaveChanges() 而不会引发错误。例如:

b.FriendCollection.Add(new Friend { User = b }); 
db.SaveChanges()

但是当我尝试分配不同的用户时,我得到了错误。例如:

b.FriendCollection.Add(new Friend { User = a }); 
db.SaveChanges()

我得到:

违反了多重性约束。关系“Entities.Friend_User”的角色“Friend_User_Target”具有多重性 1 或 0..1。

谁能告诉我如何修复我的模型(使用 FluentAPI),以便我可以为我的 User.FriendCollection 分配不同的用户?

4

1 回答 1

0

映射不正确,至少对于模型中关系的含义不正确。

您的映射创建一个单一的关系和两个导航属性User.FriendCollection,并且Friend.User是这个单一关系的两端。您已将它们定义为逆属性。

但这意味着如果您有一个用户 UserA 在 FriendB 中有一个FriendCollectionFriendBUser必须是 UserA 而不是任何其他用户。你的模型只能表达用户是他/她自己的朋友,可以这么说。因为您正在将另一个用户添加到集合中,所以您会遇到异常。

在我看来,实体Friend并不代表朋友=另一个用户,但它实际上表达了一种关系。它是用户之间多对多关系的一种中间实体,这种关系是一个Friendship(我更愿意这样称呼它。)

无论如何,您的模型需要两个关系。您可以保持实体不变,但使用此映射:

public class UserConfiguration : EntityTypeConfiguration<User>
{
    public UserConfiguration()
    {
        HasKey(x => x.ID);
        HasMany(x => x.FriendCollection)
            .WithRequired();
    }
}

public class FriendConfiguration : EntityTypeConfiguration<Friend>
{
    public FriendConfiguration()
    {
        HasKey(x => x.ID);
        HasRequired(x => x.User)
            .WithMany()
            .HasForeignKey(x => x.UserID)
            .WillCascadeOnDelete(false);
    }
}

(禁用其中一个关系的级联删除是必要的,以避免臭名昭著的“多级联删除路径异常”。)

请注意,您现在在Friend表中有两个外键,UserID用于第二个和User_ID第一个映射。Map(m => m.MapKey("SomeUserID"))您可以通过添加到第一个映射来重命名默认列名。

使用此映射,您的控制台代码应该可以工作。

于 2012-03-14T22:34:40.580 回答