Я не могу иметь отношения один ко многим в Entity Framework - PullRequest
0 голосов
/ 27 февраля 2020

Я слежу за примерами из inte rnet, но это не работает. База данных успешно создается, ошибки нет.

Я хочу, чтобы у одного пользователя было несколько транзакций, а транзакция может иметь ссылки на двух пользователей. Один из них - это пользователь, который совершил транзакцию, второй - это пользователь, которому сделана транзакция.

Но происходит то, что я получаю три внешних ключа в таблице Users, но ни одного в таблице транзакций .

См. Изображение ниже:

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 ]

1 голос
/ 27 февраля 2020

Вам необходимо внести некоторые изменения в свой код.

Прежде всего, каждое свойство навигации должно быть помечено как виртуальное, чтобы позволить Entity Framework выполнять отложенную загрузку, если только вы не хотите всегда загружать все ваша навигация (может быть выбор, зависит от вас).

После этого каждый ваш пользователь имеет транзакции outgoing и incoming, поэтому для класса 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 class

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 о том, как вещи должны go:

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, чтобы сделать правильные предположения и создать правильную структуру базы данных, то есть два FK в таблице транзакций, каждый из которых является первичным ключом таблицы Users.

И вуаля: enter image description here

0 голосов
/ 27 февраля 2020

Это происходит потому, что EF не знает, что одно из полей FromUser и ToUser должно соответствовать коллекции Transactions - поскольку вы не следуете соглашениям об именах . У вас есть несколько вариантов решения этой ситуации:

  • Если вы хотите сопоставить Transactions коллекцию только с либо FromUser или ToUser, но не с обоими, вы можно использовать атрибуты [ForeignKey] и / или [InverseProperty] для явной установки отношения к базе данных
  • Если вы хотите использовать ОБА из них, вам нужно будет указать две коллекции в User класс - например, TransactionsFromUser и TransactionsToUser. Возможно, вам все равно придется установить отношения явно через атрибуты, хотя
0 голосов
/ 27 февраля 2020

Я хочу, чтобы один пользователь мог иметь несколько транзакций, но транзакция может иметь ссылку на двух пользователей.

Ваша текущая модель базы данных отражает это точно. Я объясню почему в остальной части моего ответа.

Таблица User не может содержать внешние ключи к таблице Transactions, поскольку один пользователь может быть связан с несколькими транзакциями. Если бы столбец FK находился в таблице «Пользователь», он должен содержать более одного TransactionId.

Вместо этого ссылки на пользователей хранятся в таблице Transaction. Таким образом, каждая транзакция должна хранить только один UserId для каждого столбца FK.

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_userId и FromUser_userId существуют, поскольку они могут ссылаться на разных пользователей.

Если семантика IList<Transaction> Transactions на самом деле является «всеми транзакциями, исходящими от этого пользователя», вы можете настроить ModelBuilder на использование FromUser_userId FK для этой коллекции вместо создания третьего FK User_userId. Смотрите ответ Сергея.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...