I have Payments
between Users
. A Payment
has a FromUser
and a ToUser
. I'm testing out the entity relationships with the following:
var newPayment = new Payment() {
FromUserId = user1.UserId,
ToUserId = user2.UserId
};
db.Payments.Add(newPayment);
db.SaveChanges();
var tempPaymentId = user1.Payments.First().PaymentId;
newPayment = db.Payments.First(s => s.PaymentId == tempPaymentId);
Assert.AreEqual(newPayment.FromUserId, user1.UserId); // true
Assert.AreEqual(newPayment.ToUserId, user2.UserId); // true
Assert.AreEqual(user1.Payments.Count(), 1); // true
Assert.AreEqual(user2.Payments.Count(), 1); // false
My question is - why does user2
not have any Payments
?
Class and fluent config:
public class Payment {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int PaymentId { get; set; }
[ForeignKey("FromUser")]
public int FromUserId { get; set; }
public User FromUser { get; set; }
[ForeignKey("ToUser")]
public int ToUserId { get; set; }
public User ToUser { get; set; }
}
public PaymentConfiguration() {
HasRequired(s => s.FromUser);
HasRequired(s => s.ToUser);
}
public UserConfiguration() {
// One-to-Many
HasMany(s => s.Payments);
}
It can be due to many reasons, but, first things first, you mixed Data Annotations and Fluent API mappings. Why? I am not sure if it works as expected or not, but I am sure that it does not look right. Stick to one (I recommend Fluent API). Secondly, I suppose that, you should have two navigation properties at User
for Payments
. User.Payments
does not mean much, do you want payments that the user paid, or the payments that were paid to user by one navigation property? . And you have to map only from one side. Finally, your models and mappings should be like below:
public class User
{
public int UserId { get; set; }
// Navigation properties
public virtual ICollection<Payment> PaymentsFromUser { get; set; }
public virtual ICollection<Payment> PaymentsToUser { get; set; }
}
public class UserConfiguration
: IEntityTypeConfiguration<User>
{
public UserConfiguration()
{
// Primary key
HasKey(m => m.UserId);
Property(m => m.PaymentId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
public class Payment
{
public int PaymentId { get; set; }
public int FromUserId { get; set; }
public int ToUserId { get; set; }
// Navigation properties
public virtual User FromUser { get; set; }
public virtual User ToUser { get; set; }
}
public class PaymentConfiguration
: IEntityTypeConfiguration<Payment>
{
public PaymentConfiguration()
{
// Primary key
HasKey(m => m.PaymentId);
Property(m => m.PaymentId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// Relationship mappings
HasRequired(m => m.FromUser)
.WithMany(m => m.PaymentsFromUser)
.HasForeignKey(m => m.FromUserId)
.WillCascadeOnDelete(true);
HasRequired(m => m.ToUser)
.WithMany(m => m.PaymentsToUser)
.HasForeignKey(m => m.ToUserId)
.WillCascadeOnDelete(true);
}
}
Note: virtual
keyword is for Lazy loading
and can be skipped if you do not need it.
Now it should work as expected. Just remember to test like:
Assert.AreEqual(user1.PaymentsFromUser.Count(), 1);
Assert.AreEqual(user2.PaymentsToUser.Count(), 1);
Please note that, I suppose user1
and user2
both are tracked by EntityFramework, so EF can relate users with payments.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.