One-to–Zero-or-One relationship, with self - entity-framework

I have the following entity:
public class Category
{
public int ID { get; set; }
public int? ParentID { get; set; }
public string Name{ get; set; }
public virtual Category Parent { get; set; }
}
to which I'm trying to define a relationship: a Category can optionally have a parent category.
I've already defined the primary key:
HasKey(m => m.ID);
I've read this example, which includes a One-to–Zero-or-One relationship, but I can't grasp exactly how HasRequired implies an entity is optional in a relationship.

I think a side problem is also that you cannot reference the exact same class to itself as a parent in EF, what you can use is Projection in order to have different attributes for the same type.
HasOptional in the child reference has the same effect as saying I may have a parent or not

In the example you quote a child has a ParentId which is both a primary key and a foreign key. That is OK when parent and child are different entities (and different database tables), but not when they are self referencing. Category would have to have an ID that's both primary and foreign. But a primary key should be unique, so there's literally no room for a 1:1 child that should share the same key.
What can you do? I think the only option is to map the association as 1:n and enforce a business rule that n can't be greater than 1. Note that this is not an EF restriction. There's just no conceivable relational database model to constrain a self referencing association to 1:1.

Related

Navigation Property other than primary key in self referencing table

I have a table named Customer and it has two properties ReferrerCode and OwnReferrerCode. When a customer registers, he/she can put a referrer code of another customer and the system will assign a unique OwnReferrerCode. Multiple customers can use a specific customer's refer code.
public class Customer
{
[PrimaryKey]
public long CustomerId {get;set;}
public string ReferrerCode {get;set;}
public string OwnReferrerCode {get;set;}
public Customer ReferrerCustomer { get; set; }
public ICollection<Customer> ReferredCustomers { get; set; }
}
And in config file:
this.HasOptional<Customer>(s => s.ReferrerCustomer).WithMany(g => g.ReferredCustomers)
.HasForeignKey(s => s.ReferrerRewardCode);
It obviously returns this error
The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role.
Is there any way to do that kind of relation in EF6
As you explained, the customer entity may have one referrer customer, so the relation is like a child-parent relation. child-parent optional relationship entity-framework

EF: Unable to determine the principal end of an association between the types

Unable to determine the principal end of an association between the types. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
Models:
`
[Table("Employees")]
public class Employee : Entity
{
public string Name { get; set; }
public int? AbsenceId { get; set; }
[ForeignKey("AbsenceId")]
public virtual Absence Absence { get; set; }
}
[Table("Absences")]
public class Absence : Entity
{
public DateTime From { get; set; }
public DateTime To { get; set; }
public string Reason { get; set; }
public int? SubstituteId { get; set; }
[ForeignKey("SubstituteId")]
public virtual Employee Substitute { get; set; }
}
`
The Employee have a Absence that can have a Employee that is not same Employee that have a Absence mentioned.
Any solution for this case?
Well, first of all.. You do not need to specify ForeignKey when you are following the Entity Framework conventions. By convention, EF will reocognize the fact that your Navigation property is called Foo and your ForeignKey will be called FooId.
However, the real problem is that you are attempting to create a 1:1 association between two entities and EF does not support associations like this.
EF only supports 1:1 associations with shared primary keys, that is where both tables have the same primary key and one table's PK is a FK to the other table's PK.
If you think about this, it makes sense. There is no native 1:1 relationship in SQL that does not have a shared primary key. If you add a FK in one table to the other, it creates a 1:Many. You can simulate a 1:1 by creating a unique constraint on the FK but EF does not support constraints.
Looking at your model. Do you really want a 1:1 anyways? Can an employee really only have a single absence? Ever? Probably not. You probably want Absence to be a 1:Many. So remove AbsenceId and change Absence to:
public virtual List<Absence> Absences { get; set; }

Entity Framework 1 to 1 relationship using code first. how?

I have two classes. How can I turn these two classes into a one to one relationship using the entity framework code first method?
public class Region
{
public int RegionId { get; set; }
public string Name { get; set; }
public virtual Factory _factory { get; set; }
}
public class Factory
{
public int FactoryId { get; set; }
public string Name { get; set; }
public virtual Region _region { get; set; }
}
When I try this, I get this error:
Multiplicity is not valid in Role 'Region_Factory_Source' in relationship 'Region_Factory'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
This occurs in CodeFirst because of the virtual keyword. In effect, you are creating a relationship where creating one item requires the creation of the other. however, the virtual keyword allows lazy instantiation, which means that creating an object of one type doesn't automatically create the other type, allowing the Id on the foreign item to be null. This implies a 0..1 relationship, but since each side is virtual, what you get is a 0..0 which isn't allowed.
There are 2 methods which you can use to remedy the situation.
remove the virtual option from either one side or both sides of the navigation properties, allowing for a 0..1 or a 1..1 map.
explicitly add a property for the Foreign key from the other entity on each object. i.e. on class Region add a property for FactoryId and on Factory add a property for RegionId
There are other ways to help Entity Framework determine which object is the Dependent Object, i.e. using Entity Framework Fluent api.
from MSDN
Configuring a Relationship Where Both Ends Are Required (One-to-One)
In most cases the Entity Framework can infer which type is the dependent and which is the principal in a relationship. However, when both ends of the relationship are required or both sides are optional the Entity Framework cannot identify the dependent and principal. When both ends of the relationship are required, use WithRequiredPrincipal or WithRequiredDependent after the HasRequired method. When both ends of the relationship are optional, use WithOptionalPrincipal or WithOptionalDependent after the HasOptional method.
the following code would create a Principal Factory with a Dependent Region
// Configure the primary key for the Region
modelBuilder.Entity<Region>()
.HasKey(t => t.RegionId);
modelBuilder.Entity<Factory>()
.HasRequired(t => t.Region)
.WithRequiredPrincipal(t => t.Factory);
EF6, add attributes:
[Key]
public int RegionId { get; set; }
[Key, ForeignKey("Region")]
public int FactoryId { get; set; }

One to one OPTIONAL relationship

Traditional EF questions starts with: My models are
public class Ingredient
{
public int IngredientID { get; set; }
public virtual RequestedIngredient RequestedIngredient { get; set; }
// other stuff
}
public class RequestedIngredient
{
[Key]
string BlahBlahBlah { get; set; }
public int? IngredientID { get; set; }
public virtual Ingredient Ingredient { get; set; }
}
Somewhere in dbContext...
modelBuilder.Entity<Ingredient>()
.HasOptional<RequestedIngredient>(e => e.RequestedIngredient)
.WithOptionalPrincipal(e => e.Ingredient)
.Map(e => e.MapKey("IngredientID"))
.WillCascadeOnDelete(false);
But I get Schema specified is not valid. Errors:
(195,6) : error 0019: Each property name in a type must be unique. Property name 'IngredientID' was already defined.
If I remove IngredientID from RequestedIngredient, the db will be created just as I want to. But I have no access to IngredientID. How can I set this up to have access to foreign key?
This is not one-to-one relationship. In one-to-one relationship the foreign key must be a primary key. Which is not the case in this example. This is one-to-many, but I assumed that my app will take care of making sure there's only one association.
EF can deal with that using Independent Association. It will create foreign key, hidden from your POCO class. One can specify the name of the column using MapKey as I did. However, because I also created a property called IngredientID, just as the column used with MapKey, the EF has a problem as two properties are mapped to the same column.
So things like that are possible in EF, but you can't use foreign key anymore.

Entity Framework Code First - Entity maps to one or the other

I have Entity1 and Entity2 for example:
public class Entity1
{
public int Entity1ID { get; set; }
public string Content { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Entity2
{
public int Entity2ID { get; set; }
public string Content { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
I want to be able to add comments that are associated with each entity. This is easy enough if you were to have class Entity1_Comments and Entity2_Comments for example. Is there a way to store all the comments in one table though without confusion as to whether the record is mapping to an Entity1 record or Entity2 record?
You could for instance structure the comments table such that it has:
CommentID
Entity1_or_Entity2
ForeignKeyID
...
as the table columns. Thereby checking which record type the comments is associated with using the column Entity1_or_Entity2 and then using the ForeignKeyID to find that record. I'm not sure how you might go about this using code-first though.
Thanks,
Michael
One approach would be to set up TPH (table per hierarchy) inheritance on the Comment entity. That is, make a Comment base class, make Comment1 a derived class with FK to Entity1, and Comment2 a derived class with FK to entity 2. You end up with extra columns in your Comment table (two FKs instead of 1, plus a discriminator). See
http://www.asp.net/entity-framework/tutorials/implementing-inheritance-with-the-entity-framework-in-an-asp-net-mvc-application
Entity Framework does not support heterogeneous associations. Other frameworks handle this automatically (NHibernate, for example, provides the any mapping).
My solution is to mimic what NHibernate does, but manually.
There's a Comment entity with EntityType and EntityID fields, no foreign keys. A NoteService allows me to add/retrieve notes from any entity by storing/querying using those fields.
The only thing you will not have, of course, is direct navigation.