Code中的一对多关系

时间:2017-03-28 17:56:40

标签: asp.net-mvc entity-framework ef-code-first asp.net-identity

我想创建UsersNotifications之间的关系,我认为这种关系是一对多的关系。

我遇到了这个问题而不确定我是怎么回事。以下是两个类的代码:

public class ApplicationUser : IdentityUser
{
    public string FirstName { get; set; }

    public virtual ICollection<Notification> Notifications { get; set; }
}

public class Notification
{
    public int NotificationID { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public DateTime Time { get; set; }

    public string ApplicationUserID { get; set; }
    public virtual ApplicationUser ApplicationUser { get; set; }
}

我还有以下映射:

modelBuilder.Entity<Notification>()
    .HasRequired<ApplicationUser>(u => u.ApplicationUser)
    .WithMany(u => u.Notifications)
    .HasForeignKey(u => u.NotificationID);

我在尝试解决此问题时遇到了不同的错误,例如:

  • 多重性无效
  • 违反了多重约束。关系_的作用_具有多重性1或0..1

编辑:

同样的异常(违反了多重性约束。当我尝试为所有用户添加通知时,抛出Notification_ApplicationUser&#39;具有多重性1或0..1。)的角色&#39; Notification_ApplicationUser_Target&#39;我用这种方法完成了它:

public void AddNotification(Notification n)
{
    var roleId = context.Roles.Where(m => m.Name == "User").Select(m => m.Id).SingleOrDefault();
    var users = context.Users.Where(u => u.Roles.All(r => r.RoleId == roleId)).ToList();
    try
    {
        foreach(var user in users)
        {
            user.Notifications.Add(n);
        }
        context.SaveChanges();
    }
    catch (Exception e)
    {
        Console.WriteLine(e);
    }
}

2 个答案:

答案 0 :(得分:3)

我认为问题来自外键的字符串类型。默认情况下,字符串在数据库级别可以为空,但必需的外键不能为空。指定HasRequired仅强制要求关系,而不是外键属性必须不为null。我想如果您只是将[Required]添加到您的外键属性,那将解决问题:

[Required]
public string ApplicationUserID { get; set; }

显然,您需要进行迁移才能将此更改应用于您的数据库。

修改

抱歉,我读错了。您的问题的根源实际上是向每个用户添加相同的通知,就像在字面上相同的通知一样。 EF进行对象跟踪,因此在n引用的实体实例添加一次后,EF会跟踪它。然后,当您尝试将其添加到其他用户时,它会认为您尝试执行多对多,基本上,单个Notification记录将属于多个用户。

在我的头脑中,我不确定在EF中解决这种困惑的最佳方法是什么,但我有几个潜在的想法:

  1. 添加后可以尝试分离实体:

    user.Notifications.Add(n);
    context.Entry(n).State = EntityState.Detached;
    

    但是,我不确定是否允许添加它。你必须测试。如果它没有创建它,您也可以在分离之前尝试保存。我认为这样可行,但显然每次迭代都会对数据库提交效率很低。

  2. 最安全的方法是为每个实例创建一个新实例,并简单地映射来自n的数据:

    var notification = new Notification
    {
        Title = n.Title,
        Description = n.Description,
        Time = n.Time
    };
    user.Notifications.Add(notification);
    

    这将确保您添加的每个通知都是完全独立的跟踪实例。

答案 1 :(得分:2)

以下是您的关系的正确配置:

    modelBuilder.Entity<Notification>()
                .HasRequired(u => u.ApplicationUser)
                .WithMany(u => u.Notifications)
                .HasForeignKey(u => u.ApplicationUserID);

HasForeignKey方法中,您需要指定该关系的FK,而不是Notification实体的PK。

更新

问题是您的数据库中存在一对一的关系,并且您尝试在模型中配置一对一关系。如果最后一个是您真正想要的,那么我建议您使用Migrations来更改您的数据库架构,否则您可以这样配置您的关系:

    modelBuilder.Entity<Notification>()
                .HasRequired(u => u.ApplicationUser)
                .WithOptional(u => u.Notification)

并更改ApplicationUser实体中的导航属性:

public class ApplicationUser : IdentityUser
{
       public string FirstName { get; set; }

       public virtual Notification Notification { get; set; }
}