After several hours of trial and error, I reached to this thread which explains how to establish a One-To-Many relationship and a One-To-One relationship with the same two types.
However, I cannot get this to work with Cascade Delete:
Thrown: "Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values." (System.Data.UpdateException) Exception Message = "Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.", Exception Type = "System.Data.UpdateException"
This only happens if I don't unset the 1:1 relationship (see code below), which I guess makes sense given that it would create an invalid reference. I'm just wondering if there is a better way to represent this.
Sample code:
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<Context>());
using (var ctx = new Context())
{
var user = new User();
ctx.Users.Add(user);
ctx.SaveChanges();
var source = new PaymentSource();
user.PaymentSources = new Collection<PaymentSource>();
user.PaymentSources.Add(source);
user.DefaultPaymentSource = source;
ctx.SaveChanges();
// if I don't do this, I get ordering exception
user.DefaultPaymentSource = null;
ctx.SaveChanges();
ctx.Users.Remove(user);
ctx.SaveChanges();
Assert.Equal(0, ctx.Users.Count());
Assert.Equal(0, ctx.PaymentSources.Count());
}
}
}
public class User
{
public int Id { get; set; }
public virtual ICollection<PaymentSource> PaymentSources { get; set; }
public virtual PaymentSource DefaultPaymentSource { get; set; }
public int? DefaultPaymentSourceId { get; set; }
}
public class PaymentSource
{
public int Id { get; set; }
public virtual User User { get; set; }
public int UserId { get; set; }
}
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<PaymentSource> PaymentSources { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.HasOptional(u => u.DefaultPaymentSource)
.WithMany()
.HasForeignKey(u => u.DefaultPaymentSourceId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<PaymentSource>()
.HasRequired(p => p.User)
.WithMany(p => p.PaymentSources)
.HasForeignKey(p => p.UserId)
.WillCascadeOnDelete();
}
}
I listed other options to describe your abstraction:
A.
How about using 3 tables like that:
user 1-* paymentSource
user 1-0..1 DefaultPaymentSource
DefaultPaymentSource 0..1-1 PaymentSource
or this:
B.
user 1-* paymentSource
user 1-0..1 DefaultPaymentSource
DefaultPaymentSource --derive from--> PaymentSource
or this:
C.
user 1-* paymentSource
PaymentSource has addtional boolean field for "IsDefault"
I vote for choice B as best one.
I am certain that having two relations comming from the same source table to the same destination table is not going to be a good idea.. it is probably breaking some rule or pattern regarding databases best practice.
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.