Multiple relations of same type - c#

For my models, I have RealEstateTransaction and Agent. A RealEstateTransaction can have a ListingAgent and a SellingAgent.
How would I build out my models to make that relation? Something like this?
public class Agent
{
public long AgentId { get; set; }
public List<RealEstateTransaction> ListingRealEstateTransactions { get; set; }
public List<RealEstateTransaction> SellingRealEstateTransactions { get; set; }
}
public class RealEstateTransaction
{
public long RealEstateTransactionId { get; set; }
public long ListingAgentId { get; set; }
public Agent ListingAgent { get; set; }
public long SellingAgentId { get; set; }
public Agent SellingAgent { get; set; }
}

Something like this?
Indeed.
But it requires some additional mapping of the navigation properties (the FK property names follow the EF Core conventions, so no mapping is required for that). Normally EF Core is able to pair the navigation properties of the two ends of the relationship, but not when you have two relationships to one and the same entity. In such case you need to tell which navigation property of the principal corresponds to (is inverse of) the each navigation property in dependent.
Normally you can do that by either data annotation ([InverseProperty] attribute) or fluent API. But since more than one FK relationships to the same entity also introduce the so called multiple cascade paths issue with SqlServer (and some other databases), you'd need also to turn the cascade delete off for at least one of the relationships, and this can be done only with fluent API, so the minimal mapping required is something like this:
modelBuilder.Entity<Agent>()
.HasMany(e => e.ListingRealEstateTransactions)
.WithOne(e => e.ListingAgent)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Agent>()
.HasMany(e => e.SellingRealEstateTransactions)
.WithOne(e => e.SellingAgent)
.OnDelete(DeleteBehavior.Restrict);
You can skip .OnDelete(DeleteBehavior.Restrict) for one of the relationships (or change it to DeleteBehavior.Cascade which is the default for required relationships like these). Note that deleting the principal (Agent in this case) will require first manually deleting all the related dependents for each relationship having DeleteBehavior.Restrict.
That's the mandatory part. Optionally, if the RealEstateTransaction entity serves only as standard many-to-many "link" entity (has no additional properties and the pair (ListingAgentId, SellingAgentId) is unique), you could remove the RealEstateTransactionId PK property and configure fluently a composite PK:
modelBuilder.Entity<RealEstateTransaction>()
.HasKey(e = new { e.ListingAgentId, e.SellingAgentId });
References:
Relationships
Keys (primary)

do this :
public class Agent
{
public long AgentId { get; set; }
public Virtual ICollection<RealEstateTransaction> ListingRealEstateTransactions { get; set; }
public Virtual ICollection<RealEstateTransaction> SellingRealEstateTransactions { get; set; }
}
public class RealEstateTransaction
{
public long RealEstateTransactionId { get; set; }
[ForeignKey("ListingAgentId")]
public Agent ListingAgent { get; set; }
public long ListingAgentId { get; set; }
[ForeignKey("SellingAgentId ")]
public Agent SellingAgent { get; set; }
public long SellingAgentId { get; set; }
}

Related

One-To-One relationship with FK distinct from PK

I have 2 tables in database: ReceivedGoods and ReceivedGoodsProperties
ReceivedGoods contains ReceivingId as PK and must have its extending data in ReceivedGoodsProperties which contains ReceivingId as FK referencing to the ReceivedGoods's ReceivingId. Current ReceivedGoodsProperties, however, has its own PK Id and is therefore distinct from FK. So I have following:
public class ReceivedGoods
{
...
public int ReceivingId { get; set; }
...
public virtual ReceivedGoodsProperties properties { get; set; }
}
public class ReceivedGoodsProperties
{
...
public int Id { get; set; } // This is PK
public int ReceivingId { get; set; } // This is FK
...
public virtual ReceivedGoods goods { get; set; }
}
I would like to get ReceivedGoods object and have properties automatically loaded as well but I am not able to figure out, how to set up this within EF.
I've tried something like this (from the ReceivedGoodsProperties side mapping):
this.HasRequired(p => p.goods)
.WithRequiredDependent(d => d.properties)
.Map(m => m.MapKey("ReceivingId"));
but I am ending up with following error:
ReceivingId: Name: Each property name in a type must be unique. Property
name 'ReceivingId' is already defined.
When commenting out ReceivingId in ReceivedGoodsProperties, upper exception is not thrown, ReceivedGoods is loaded correctly except the properties property.
Can somebody explain me, how to do one-to-one mapping in situation like this?
Could you try:
public class ReceivedGoods
{
...
public int ReceivingId { get; set; }
...
public virtual ReceivedGoodsProperties properties { get; set; }
}
public class ReceivedGoodsProperties
{
...
public int Id { get; set; } // This is PK
[ForeignKey( "goods " )]
public int ReceivingId { get; set; } // This is FK
...
[Required]
public virtual ReceivedGoods goods { get; set; }
}
BTW, in C# the standard guidelines is to PascalCase members, so Goods and Properties
Try defining the relationship this way:
this.HasRequired(p => p.goods)
.WithRequiredDependent(p => p.properties)
.HasForeignKey(p => p.ReceivingId);
If you follow the standard EF naming conventions, it can usually figure out these relationships on its own. You only really run in to trouble when your navigation property names don't correspond to the class name, or if you have multiple FKs to the same destination in the source table.
If you want the navigation properties to get filled out "automatically", use the Include extension method on the query, as in:context.Goods.Include(g=>g.properties). You don't have to declare them as virtual unless you want to make use of lazy loading.
You may need to come at this from the other entity:
this.HasRequired(p => p.properties)
.WithRequiredPrincipal(p => p.goods)
.HasForeignKey(p => p.ReceivingId);

How to Define Foreign Key Relationships in Entity Framework other than the default method

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?
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.
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:

EF6 code first multiple 1-to-many mapping issue / "Multiplicity" error

I am receiving the following error when attempting to create the database:
One or more validation errors were detected during model generation:
Interaction_CauseElement_Source: : Multiplicity is not valid in Role
'Interaction_CauseElement_Source' in relationship
'Interaction_CauseElement'. Because the Dependent Role properties are
not the key properties, the upper bound of the multiplicity of the
Dependent Role must be '*'.
Interaction_EffectElement_Source: : Multiplicity is not valid in Role
'Interaction_EffectElement_Source' in relationship
'Interaction_EffectElement'. Because the Dependent Role properties are
not the key properties, the upper bound of the multiplicity of the
Dependent Role must be '*'.
I've seen this error in other Stack Overflow posts, but in the examples I found, the OP was trying for a 1-to-1 relationship in both directions between the tables. That is not what I am looking for.
Here is my model:
public class Element
{
[Key]
public int ID { get; set; }
[Required, MaxLength(64)]
public string Name { get; set; }
[MaxLength(200)]
public string Description { get; set; }
}
public class Interaction
{
[Key]
public int ID { get; set; }
[Index, Required]
public int CauseID { get; set; }
[Index, Required]
public int EffectID { get; set; }
[MaxLength(64)]
public string Location { get; set; }
[ForeignKey("CauseID")]
public virtual Element CauseElement { get; set; }
[ForeignKey("EffectID")]
public virtual Element EffectElement { get; set; }
}
Items in the Elements table are unique. A pair of elements can interact with each other in any number of locations. The CauseID/EffectID pair is not going to be unique.
The only other place I am changing the model is in the OnModelCreating method. I had received this error:
Introducing FOREIGN KEY constraint
'FK_dbo.Interactions_dbo.Elements_Cause' on table
'Interactions' may cause cycles or multiple cascade paths.
Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other
FOREIGN KEY constraints. Could not create constraint. See previous
errors.
And had to create a cascade policy for the model. This code fixed that error:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Prevent cyclic cascade on elements table
modelBuilder.Entity<Interaction>()
.HasRequired(i => i.CauseElement)
.WithRequiredDependent()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Interaction>()
.HasRequired(i => i.EffectElement)
.WithRequiredDependent()
.WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
But then I received the cryptic "Multiplicity" error. It seems like it wants me to make public virtual Element CauseElement into a collection like public virtual ICollection<Element> CauseElement, but that would not properly model the relationship.
I found the solution. This article on EntityFrameworkTutoral.net helped out. Because I need TWO references from the Interaction class to the Element class, this relationship is too complex to model in EF with only the attributes.
I had to update the model and then use the fluent API to tell EF how to treat the relationships. I updated my model to the following:
public class Element
{
public Element()
{
CauseElements = new List<Interaction>();
EffectElements = new List<Interaction>();
}
[Key]
public int ID { get; set; }
[Required, MaxLength(64)]
public string Name { get; set; }
#region Navigation
public virtual ICollection<Interaction> CauseElements { get; set; }
public virtual ICollection<Interaction> EffectElements { get; set; }
#endregion
}
public class Interaction
{
[Key]
public int ID { get; set; }
[Index]
public int CauseID { get; set; }
[Index]
public int EffectID { get; set; }
[MaxLength(64)]
public string Location { get; set; }
#region Navigation
[ForeignKey("CauseID")]
public virtual Element CauseElement { get; set; }
[ForeignKey("EffectID")]
public virtual Element EffectElement { get; set; }
#endregion
}
And in my DbContext class I used the fluent API to create the link between the Interaction.CauseElement and Element.CauseElements and which property was the foreign key for the Interaction table (and the same with the Effect relationship):
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Prevent cyclic cascade on elements table
modelBuilder.Entity<Interaction>()
.HasRequired(i => i.CauseElement)
.WithRequiredDependent()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Interaction>()
.HasRequired(i => i.EffectElement)
.WithRequiredDependent()
.WillCascadeOnDelete(false);
//Create the links between the element, the key, and the collection
modelBuilder.Entity<Interaction>()
.HasRequired<Element>(i => i.CauseElement)
.WithMany(e => e.CauseElements)
.HasForeignKey(i => i.CauseID);
modelBuilder.Entity<Interaction>()
.HasRequired<Element>(i => i.EffectElement)
.WithMany(e => e.EffectElements)
.HasForeignKey(i => i.EffectID);
base.OnModelCreating(modelBuilder);
}
It seems that Entity Framework tries to automatically infer the relationships between the tables when you have a simple 1-to-many relationship. If I removed EffectElement from the Interaction class (and EffectElements from Element), EF was able to create the relationship easily. But when I added it back, I received the error again.
Since that Element type showed up twice in the Interaction class, it didn't know how to create the relationship. I had to explicitly define it in the OnModelCreating method.
You reversed the responsibilities of the "ForeignKey" attribute. It goes on the ID field, specifying the property for which it serves as the foreign key. You want something as below:
// To-One on Element
[ForeignKey("Element")]
public int ElementId { get; set; }
public virtual Element Element { get; set; }
Also, this is actually a one-to-one relationship. A one-to-many relationship in this case would be:
// To-Many on Element
public virtual ICollection<Element> Elements{ get; set; }

How to configure zero or one to zero or one relationship with bi-directional navigation with FluentAPI

I'm trying to create a 0..1 to 0..1 relation in entity framework's fluent API. The objective would be to configure the relation navigable from both ends.
The ideia is: One user might be a physician and a physician may or may not have an associated user.
It would be useful to have a bi-directional relation for navigation purposes.
Also, the foreignKey side, on Pysician, should have the ForeignKey property exposed (to set a User ID).
Here's my model and fluentAPI configuration
public class Physician
{
public long Id { get; set; }
public string ProfessionalName { get; set; }
public long? PhysicianUserID { get; set; }
public virtual User PhysicianUser { get; set; }
}
public class User
{
public long Id { get; set; }
public string Email { get; set; }
public virtual Physician UserPhysician { get; set; }
}
modelBuilder.Entity<Physician>()
.HasOptional(A => A.PhysicianUser)
.WithOptionalDependent(A => A.UserPhysician);
modelBuilder.Entity<User>()
.HasOptional(A => A.UserPhysician)
.WithOptionalPrincipal(A => A.PhysicianUser);
The problem is that EF creates a property on Physician called PhysicianUser_ID (instead of using the one I provided). I've tried to add a [ForeignKey()] declaration on the physician entity but that resulted in validations errors.
How can I configure this type of relation ? (If indeed possible)
You need to tell Ef how to map the key
modelBuilder.Entity<User>()
.HasOptional(A => A.UserPhysician)
.WithOptionalPrincipal(A => A.PhysicianUser)
.Map(A => A.MapKey("PhysicianUserID"));

Relationship troubles with Entity Framework

I need help creating the relationship in entity framework as everything I have tried gives me errors when trying to add the migration or if I get passed that then I try to update the database and get an error about indexes with the same name.
public class Profile
{
public Profile()
{
Environments = new HashSet<Environment>();
}
[Key]
public int Id { get; set; }
public string VersionCreated { get; set; }
public string DiskLocation { get; set; }
public string Name { get; set; }
public DateTime DateTime { get; set; }
public virtual Product Product { get; set; }
public virtual Instance OriginalInstance { get; set; }
public virtual ICollection<Environment> Environments { get; set; }
}
public class Instance
{
public Instance()
{
TestResults = new HashSet<TestResult>();
Environments = new HashSet<Environment>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Version { get; set; }
public string UserFriendlyName { get; set; }
public virtual Product Product { get; set; }
public virtual Profile LastKnownProfile { get; set; }
public virtual Computer Computer { get; set; }
public virtual ICollection<TestResult> TestResults { get; set; }
public virtual ICollection<Environment> Environments { get; set; }
}
The problem with the above classes is that the OrginalInstance property on the Profile class and the LastKnownProfile in the Instance class are supposed to just be foreign keys to those specific tables and they probably won't be the same very often. They can also both possibly be null.
I have tried:
modelBuilder.Entity<Instance>().HasRequired(i => i.LastKnownProfile);
modelBuilder.Entity<Profile>().HasRequired(p => p.OriginalInstance);
This gave me an Unable to determine the principal end of an association between the types 'EcuWeb.Data.Entities.Instance' and 'EcuWeb.Data.Entities.Profile'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations. error.
and with:
modelBuilder.Entity<Instance>().HasRequired(i => i.LastKnownProfile).WithOptional();
modelBuilder.Entity<Profile>().HasRequired(p => p.OriginalInstance).WithOptional();
The database adds a foreign key reference back to itself.
...that the OrginalInstance property on the Profile class and the
LastKnownProfile in the Instance class are supposed to just be foreign
keys to those specific tables and they probably won't be the same very
often. They can also both possibly be null.
In this case you actually want two one-to-many relationships between Profile and Instance if I don't misunderstand your quote above. It would mean that many Profiles can have the same OriginalInstance and that many Instances can have the same LastKnownProfile. The correct mapping would look like this then:
modelBuilder.Entity<Profile>()
.HasOptional(p => p.OriginalInstance)
.WithMany()
.Map(m => m.MapKey("OriginalInstanceId"));
modelBuilder.Entity<Instance>()
.HasOptional(i => i.LastKnownProfile)
.WithMany()
.Map(m => m.MapKey("LastKnownProfileId"));
The lines with MapKey are optional. Without them EF will create a foreign key with a default name.
Also note that you must use HasOptional (instead of HasRequired) if "both can possibly be null".

Categories