我无法在实体框架中建立一对多关系

时间:2020-02-27 11:53:20

标签: entity-framework foreign-keys

我正在跟踪互联网上的示例,但没有用。数据库创建成功,没有错误。

我要拥有的是:一个用户可以有多个交易,而一个交易可以有两个用户的引用。其中一个是进行交易的用户,第二个是进行交易的用户。

但是发生的事情是我在Users表中得到了三个外键,而在Transaction表中却没有。

请参见下图:

The database created image from microsoft studio management

我的课程

public class User
{
    public int userId { get; set; }
    public string UserName { get; set; }
    public string Email { get; set; }
    public string CardNumber { get; set; }
    public string Password { get; set; }
    public int Balance { get; set; }
    public string UserType { get; set; }
    public string ProfileUrl { get; set; }

    public IList<Transaction> Transactions { get; set; }
}

public class Transaction
{ 
    public Transaction()
    {
        this.TranscationDateTime = DateTime.UtcNow;
    }

    public int TransactionId { get; set; }
    public int Amount { get; set; }
    public User FromUser { get; set; }
    public User ToUser { get; set; }
    public DateTime TranscationDateTime { get; set; }
}

public class DB: DbContext
{
    public DB() : base("name=DBConnection")
    { }

    public DbSet<User> Users { get; set; }
    public DbSet<Transaction> Transactions { get; set; }
}

3 个答案:

答案 0 :(得分:1)

您需要对代码进行一些修改。

首先,每个导航属性都需要标记为virtual,以便允许Entity Framework延迟加载,除非您希望始终渴望加载所有导航(可以选择,取决于您)。

此后,您的每个用户都有outgoingincoming个交易,因此对于User类:

public class User
{
    public int userId { get; set; }
    public string UserName { get; set; }
    public string Email { get; set; }

    public string CardNumber { get; set; }

    public string Password { get; set; }

    public int Balance { get; set; }

    public string UserType { get; set; }

    public string ProfileUrl { get; set; }

    public virtual IList<Transaction> IncomingTransactions { get; set; }

    public virtual IList<Transaction> OutgoingTransactions { get; set; }
}

让我们创建Transaction类的虚拟导航属性

public class Transaction
{ 
    public Transaction()
    {
        this.TranscationDateTime = DateTime.UtcNow;
    }
    public int TransactionId { get; set; }
    public int Amount { get; set; }
    public virtual User FromUser { get; set; }
    public virtual User ToUser { get; set; }

    public DateTime TranscationDateTime { get; set; }

}

最后但并非最不重要的一点是,让我们通知DbContext应该如何进行:

public class MyContext : DbContext
{
   public MyContext(string connectionString) : base(connectionString) { }

   public DbSet<User> Users { get; set; }
   public DbSet<Transaction> Transactions { get; set; }

   protected override void OnModelCreating(DbModelBuilder builder)
   {
      base.OnModelCreating(builder);
      builder.Entity<Transaction>()
        .HasRequired<User>(t => t.FromUser)
        .WithMany(u => u.OutgoingTransactions).WillCascadeOnDelete(false);
      builder.Entity<Transaction>()
        .HasRequired<User>(t => t.ToUser)
        .WithMany(u => u.IncomingTransactions).WillCascadeOnDelete(false);
   }

}

这对于EF自动发现应该足以做出正确的假设并创建正确的数据库结构,这将是Transaction表中的两个FK,每个FK都是Users表的主键。

瞧瞧: enter image description here

答案 1 :(得分:0)

我要拥有的一个用户可以有多个交易,但是一个交易可以引用两个用户。

您当前的数据库模型正确地反映了这一点。我将在其余答案中解释原因。

User表不能保存Transactions表的外键,因为一个用户可以与多个事务关联。如果FK列位于“用户”表上,则它将需要保存多个TransactionID。

相反,对用户的引用存储在Transaction表中。因此,每个事务只需要为每个FK列存储一个UserId。

Transaction.User_userId告诉我们此交易位于用户IList<Transaction> Transactions中,其中存储了User_userId

要获取特定用户的交易列表,请查询

SELECT *
FROM Transactions t
INNER JOIN Users u on t.User_userId = u.userId
WHERE u.userId = {theUserId}

存在其他FK ToUser_userIdFromUser_userId是因为它们可能引用了不同的用户。

如果IList<Transaction> Transactions的语义实际上是“源自该用户的所有事务”,则可以将ModelBuilder配置为对该集合使用FromUser_userId FK,而不用创建第三个FK {{ 1}}。看到谢尔盖的答案。

答案 2 :(得分:0)

之所以会这样,是因为EF不知道FromUserToUser字段之一应该与集合Transactions相匹配-因为您没有关注naming conventions 。您可以通过多种方法来解决这种情况:

  • 如果您只想将Transactions集合与 FromUserToUser而不是两者都匹配,则可以使用[ForeignKey]和/或[InverseProperty]个属性以显式设置数据库关系
  • 如果要同时使用它们,则需要在User类中指定两个集合-例如TransactionsFromUserTransactionsToUser。您可能仍然需要通过属性来显式设置关系
相关问题