TPT inheritance in entity framework code first - entity-framework

by using Table per Type (TPT) inheritance in Entity Framework codefirst we can create foreign keys like this :
public abstract class Person
{
public int id { get; set; }
public string Name { get; set; }
public string Family { get; set; }
}
[Table("Doctors")]
public class Doctor : Person
{
public string ExpertTitle { get; set; }
}
[Table("Notes")]
public class Note : Doctor
{
public string Content { get; set; }
}
in above code In addition to the creating Doctors table and relating that with Persons table, we could create Note table and create 1-to-Many relation between Doctors Table and that.
but is that standard to create all foreign keys using inheritance instead of using virtual properties like what you see bellow?!
public class Doctor : Per
{
public string ExpertTitle { get; set; }
public virtual ICollection<Note> Notes { get; set; }
}
public class Note : Doctor
{
public string Content { get; set; }
public virtual Doctor Doctor { get; set; }
}

You'd only want to derive from Doctor, when you need to expand the Doctor type by adding to it additional properties, let's say Surgeon.
In your case, whether you want to attach a single or multiple notes to a doctor, is merely another property to ANY type of doctor.
So you should wither add a nullable/non-nullable string property or complex type Note in the Doctor type for a single note, or as you suggested yourself, a collection of Note each having its own ID.

we could create Note table and create 1-to-Many relation between
Doctors Table and that.
If Note inherits from Doctor, then you have a 1/0 to 1 relationship from Note to Doctor, not a 1 to many.
Even if your goal is to create that 1/0 to 1 relationship, you should only use inheritance to do so when it truly represents a "is a" relationship. "Note is a Doctor" doesn't really make sense.
To accomplish a 1/0 to 1 relationship without inheritance, use a shared primary key which you can fidn plenty of other questions covering. Both Note and Doctor would have the same primary key. I.e. Doctor id 45 would have Note with Id 45. Since the Note is the optional 1/0 side of the relationship, then your FK constraint would be from the Note table referencing the Doctor table. Meaning you could not insert Note with Id 46 unless Doctor with Id 46 already existed.

Related

1 to many with composite key as primary key

I tried many different examples here, but i can't seem to figure out what i'm doing wrong.
I have a table with a history table to it. I have removed many of the fields to make it easier to watch. After my migration it works fine if i watch in PHPMyAdmin and watch the primaryKey there.
I want to be able to go context.ProductArtifactDocumentState.Histories so i can get all linked histories.
DBContext
//Composite Key
builder.Entity<ProductArtifactDocumentStateHistory>()
.HasKey(k => new { k.Version, k.ProductArtifactDocumentStateId});
Table
[Table("ProductArtifactDocumentsState")]
public partial class ProductArtifactDocumentState : BaseEntity
{
public virtual ICollection<ProductArtifactDocumentStateHistory> ProductArtifactDocumentStateHistories { get; set; }
}
History Table
[Table("ProductArtifactDocumentsState_History")]
public partial class ProductArtifactDocumentStateHistory
{
[Column("ProductArtifactDocumentStateId")]
public int ProductArtifactDocumentStateId { get; set; }
public virtual ProductArtifactDocumentState ProductArtifactDocumentState { get; set; }
public int Version { get; set; }
}
The error i get:
System.InvalidOperationException: 'The entity type 'ProductArtifactDocumentStateHistory' requires a primary key to be defined. If you intended to use a keyless entity type, call 'HasNoKey' in 'OnModelCreating'. For more information on keyless entity types, see https://go.microsoft.com/fwlink/?linkid=2141943.'
Why do you need all this headache? Are you trying to save 2 bytes on a primary key? But after this all your ef code will be a nightmare. Just add Id field
public partial class ProductArtifactDocumentStateHistory
{
[Key]
public int Id {get; set;}
.....
}

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; }

EF Code First Table Splitting issue when used with many-to-many relationships

I am trying to use "Table Splitting" in EF Code First and it works fine when I use one of the entities relationships of type one-to-many, but as soon as I use it in a many-to-many relationship I start getting this error:
(27,6) : error 3018: Problem in mapping fragments starting at line
27:Foreign key constraint 'Itinerary_Addresses_Target' from table
ItineraryAddress (Address_Id) to table User (Id): The columns of table
ItineraryAddress are mapped to AssociationSet Itinerary_Addresses's
End Itinerary_Addresses_Target but the key columns of table User are
not mapped to the keys of the EntitySet Addresses corresponding to
this End.
Here is the code (https://github.com/jorgef/tablesplitting):
Table Splitting
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
public int Id { get; set; }
public string Street { get; set; }
public User User { get; set; }
}
modelBuilder.Entity<User>().ToTable("Users");
modelBuilder.Entity<Address>().ToTable("Users");
modelBuilder.Entity<User>().HasRequired(u => u.Address).WithRequiredPrincipal(a => a.User);
One-To-Many Relationship
public class Itinerary
{
public int Id { get; set; }
public ICollection<Address> Addresses { get; set; }
}
With the previous code, everything works like a charm, the problem is when introducing a many-to-many relationship
Many-To-Many Relationship
public class Address
{
...
public ICollection<Itinerary> Itineraries { get; set; }
}
After adding that relationship, the app raises the mentioned exception on runtime. I managed to save to disk the generated edmx just in case that helps, here is the link: https://github.com/jorgef/tablesplitting/blob/master/TableSplitting/SavedModel.edmx
If somebody wants to play with the two versions of the app, the one working and the one not working, I have two different commits:
Table splitting working with one to many relationship
Table splitting not working with many to many relationship
Any ideas or thougths are appreciated.
Many thanks!
In case anyone else has encountered this issue:
This was a bug in Entity Framework. The bug has since been fixed, and upgrading to EF6 will resolve the issue. See the following discussion for details:
https://entityframework.codeplex.com/workitem/1385
A related bug with table splitting validation has also been discovered and fixed, planned to be released with EF6.1.0:
https://entityframework.codeplex.com/workitem/1611

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.

How do I include an unmapped field in a POCO class

I'm new to the EF and am just experimenting. Can someone tell me if the following is possible. Given a (product) table in the DB like so:
Id Cost DescriptionFK
-- ---- -------------
? ? ?
I want the corresponding POCO class (entity) to appear like:
public class Product
{
public int Id { get; set; }
public decimal Cost { get; set; }
public string Description { get; }
}
Note that the "Description" in the class is a read-only string (no setter), but it's a key in the table. I'm calling a stored procedure to pull this off (converting the key to its corresponding string and returning the above class), but if I now do something like:
// ...
product.Cost = 20;
myContext.SaveChanges();
I get an exception complaining that there's no mapping for the "Description" string. I removed the mapping because it's read-only and I don't need to include the "DescriptionFK" in the class itself. Is there some way to pull this off (POCO only). Thanks very much.
If you are just looking to have the Description property as a calculated field, add [NotMapped] to your the property to explicitly exclude it and then generate the database:
public class Product
{
public int Id { get; set; }
public decimal Cost { get; set; }
[NotMapped]
public string Description { get; }
}
AFAIU, it is not possible.
"You always need at least one navigation property to create a foreign key constraint in the database."
EF Code First foreign key without navigation property