问题
I have to admit, the features of EF 4.1 RC Codefirst, DataAnnotations and FluentAPI are still overwhelming to me. Sometimes I really don't know what I am doing ;-) Please see the following POCOs:
public class Country
{
[Key]
public Guid ID { get; set; }
[Required]
public virtual Currency Currency { get; set; }
}
public class Currency
{
[Key]
public Guid ID { get; set; }
public virtual ICollection<Country> Countries { get; set; }
}
The general idea: Every country needs to have a currency. But a currency does not need to be assigned to a country at all.
If you let EF create the corresponding database, the relationship will be set to CASCADE DELETE by convention. In other words: if you delete a currency, the corresponding countries are deleted as well. But in my case this is not what I want.
I came up with some code in FluentAPI in order to disable CASCADE DELETE:
modelBuilder.Entity<Country>()
.HasRequired(cou => cou.Currency)
.WithOptional()
.WillCascadeOnDelete(false);
I thought this means: Every country requires a currency. And this currency might have zero, one or more countries assigned (optional). And whenever I delete a currency, the corresponding countries (if there are any) will NOT be cascade deleted.
Surprisingly the given approach will still cascade delete a country if I delete the corresponding currency. Can anybody tell me what I miss?
回答1:
Firstly you've specified the currency as a required field on country, so you can't delete a currency. You'll need to remove the [Required].
Secondly, your model builder need the following:
modelBuilder.Entity<Country>()
.HasRequired(cou => cou.Currency) //note optional, not required
.WithMany(c=>c.Countries) //define the relationship
.WillCascadeOnDelete(false);
Thirdly, you need to explicitly remove the reference to the entity you are deleting from it's children:
Currency c = context.Currencies.FirstOrDefault();
c.Countries.Clear(); //these removes the link between child and parent
context.Currencies.Remove(c);
context.SaveChanges();
[EDIT] Because I suspect there is something lost in translation find the complete code that demonstrates how no-cascading deletes would work.
public class Country{
[Key]
public Guid ID { get; set; }
public virtual Currency Currency { get; set; }
}
public class Currency{
[Key]
public Guid ID { get; set; }
public virtual ICollection<Country> Countries { get; set; }
}
public class MyContext : DbContext{
public DbSet<Currency> Currencies { get; set; }
public DbSet<Country> Countries { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder){
modelBuilder.Entity<Country>()
.HasRequired(country => country.Currency)
.WithMany(currency => currency.Countries)
.WillCascadeOnDelete(false);
}
}
class Program{
static void Main(string[] args){
Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());
using (MyContext context1 = new MyContext()){
Currency c = new Currency{ID = Guid.NewGuid()};
context1.Currencies.Add(c);
c.Countries = new List<Country>();
c.Countries.Add(new Country{ID = Guid.NewGuid()});
context1.SaveChanges();
}
using (MyContext context2 = new MyContext()){
Currency c = context2.Currencies.FirstOrDefault();
context2.Currencies.Remove(c);
//throws exception due to foreign key constraint
//The primary key value cannot be deleted
//because references to this key still exist.
//[ Foreign key constraint name = Country_Currency ]
context2.SaveChanges();
}
}
}
You will get an error on saving, because your deleting something that is a required foreign key.
来源:https://stackoverflow.com/questions/5402964/ef-4-1-rc-weird-cascade-delete