问题
I have a problem I am trying to solve with the Entity Framework 6.0 and hope someone here can give some direction on. I am much more comfortable with ADO.NET but want to do this project in EF.
I have an object called Policy and another called PayPlan
public class Policy
{
//Various properties not relevant
public PayPlan PaymentPlan { get; set; }
}
public class PayPlan
{
public int PayPlanId { get; set;}
public string Description { get; set; }
}
As you can see, in this example, a PayPlan is a child object for a Policy. It may be null, or there may be a single instance of a PayPlan associated with a policy.
When I run the model builder, it creates the tables and inserts a Foreign Key into the Policy Table for the record in the PayPlan. This doesnt really work for me though because 1) I would like to keep the Db schema similar to a previous version of the application wherein the PolicyId is a ForeignKey into the PayPlan and 2) With Cascading Deletes, if the PayPlan were to be deleted it would take the Policy with it and I need this to be the other way around. The Policy is the root object form which all other objects in the Db draw their relations. PayPlan, btw, is just a single example for this discussion but in the actual project the Policy object would contain a number of child objects associated with it in a similar manner.
My question, how do I set this up, either through Data Annotations or via the Fluent API, to achieve the schema I described?
回答1:
So, after digging a little bit about this on EF 6 after you mentioned you are using that version and found this:
Apparently alternate keys are not supported on EF 6. As @rowanmiller on this Github issue:
Unfortunately this is a limitation of EF6. You can not have a foreign key property in a one-to-one relationship, unless it is also the primary key property. This is essentially because EF6 doesn't support alternate keys/unique indexes, so you can't enforce that a non-primary key property is unique. The fact that you can do it when the foreign key property isn't in the entity is a bit of a quirk... but obviously not something we would remove :smile:.
BTW alternate keys (and therefore this scenario) is supported in EF Core.
Mapping foreign key in HasOptional().WithOptionalDependent() relation
You can still have the FK as you want, but you can't have the FK property on your PayPlan class. If you do, you'll ended up with two FKs. So, if you configure your relationship like this:
public class Policy
{
public int PolicyId { get; set; }
public string Description { get; set; }
public PayPlan PaymentPlan { get; set; }
}
public class PayPlan
{
public int PayPlanId { get; set; }
public string Description { get; set; }
public Policy Policy { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<PayPlan>()
.HasOptional(a => a.Policy)
.WithOptionalDependent(p => p.PaymentPlan)
.WillCascadeOnDelete(true);
}
You will end with this on SQL:
Didn't know about this since I never had this scenario. Sucks a lot. BUT you still can do it using EF core :), which is cool.
EF Core answer just for the record
You can solve this also using the FluentAPI. (I prefer the FluentApi rather than polluting my models with Attributes). Also, since you didn't mention which version of EF you are using, I assumed you are using EF Core.
public class Policy
{
public int PolicyId { get; set; }
public string Description { get; set; }
public PayPlan PaymentPlan { get; set; }
}
public class PayPlan
{
public int PayPlanId { get; set; }
public string Description { get; set; }
public Policy Policy { get; set; }
public int? PolicyId { get; set; }
}
Context class:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Policy>()
.HasOne(a => a.PaymentPlan)
.WithOne(b => b.Policy)
.IsRequired(false)
.HasForeignKey<PayPlan>(b => b.PolicyId)
.OnDelete(DeleteBehavior.Cascade);
}
This will produce the following tables on SQL:
回答2:
If I understood your requirements correctly, you want to build model like this:
public class Policy {
[Key]
public int PolicyId { get; set; }
// this attribute is not required, but I prefer to be specific
// this attribute means navigation property PaymentPlan
// is "anoter end" of navigation property PayPlan.Policy
[InverseProperty("Policy")]
public virtual PayPlan PaymentPlan { get; set; }
}
public class PayPlan {
[Key]
public int PayPlanId { get; set; }
// define foreign key explicitly here
[ForeignKey("Policy")]
public int PolicyId { get; set; }
public virtual Policy Policy { get; set; }
public string Description { get; set; }
}
Update: the above works in EF Core, but does not work in EF 6. EF 6 treats this as one to many relationship (and is correct in that, because one Policy could have multiple PayPlans). To create one to (zero or) one relationship, you can create model like this:
public class Policy
{
[Key]
public int PolicyId { get; set; }
[InverseProperty("Policy")]
public virtual PayPlan PaymentPlan { get; set; }
}
public class PayPlan
{
[Key, ForeignKey("Policy")]
public int PolicyId { get; set; }
public Policy Policy { get; set; }
public string Description { get; set; }
}
So PayPlan doesn't have its own Id and instead has PolicyId which is both PK and FK. That way, only one (or none) pay plan may exist for one policy.
来源:https://stackoverflow.com/questions/47748243/how-to-define-foreign-key-relationships-in-entity-framework-other-than-the-defau