One to One Relationship on Primary Key with Entity Framework Code First - entity-framework

I'm currently getting the following error when trying to create an one to one relationship using Code First:
System.Data.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'C001_Holding_Teste_C001_Holding_Source' in relationship 'C001_Holding_Teste_C001_Holding'. Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be 1.
My entity definitions are the following:
[Table("C001_Holding", Schema = "Cad")]
public partial class C001_Holding
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int C001_Id { get; set; }
[MaxLength(16)]
public string C001_Codigo { get; set; }
[MaxLength(100)]
public string C001_Descricao { get; set; }
}
public class C001_Holding_Test
{
[Key]
public int C001_Id { get; set; }
[MaxLength(100)]
public string C001_TestInfo { get; set; }
[ForeignKey("C001_Id")]
public virtual C001_Holding C001_Holding { get; set; }
}
I didn't want to use Fluent to create these relationships, does anyone knows why this is happening?
Tks.

It is possible to place the ForeignKey attribute either on a navigation property and then specify the name of the property you want to have as the foreign key (that's what you did). Or you can place it on the foreign key property and then specify the name of the navigation property which represents the relationship. This would look like:
public class C001_Holding_Test
{
[Key]
[ForeignKey("C001_Holding")]
public int C001_Id { get; set; }
[MaxLength(100)]
public string C001_TestInfo { get; set; }
public virtual C001_Holding C001_Holding { get; set; }
}
For some reason this second option works while the first throws an error. (It feels like a bug to me because both options should represent the same relationship. Or there is actually a semantic difference which I don't see...)

Related

Entity Framework one-to-one relationship - Unable to determine the principal

I have 2 models:
public class TransactionHistory : IDbEntity {
[Key]
public int ID { get; set; }
public ItemHistory ItemHistory { get; set; }
}
public class ItemHistory : IDbEntity {
[Key]
public int ID { get; set; }
public int TransactionHistoryID { get; set; }
public TransactionHistory TransactionHistory { get; set; }
}
There's a one to one relationship between TransactionHistory and ItemHistory, ItemHistory MUST have a TransactionHistory but TransactionHistory may or may not have an ItemHistory.
I want to be able to do this in code:
var test = db.ItemHistory.Include(x => x.TransactionHistory).ToList();
As well as:
var test2 = db.TransactionHistory.Include(x => x.ItemHistory).ToList();
But I only want a single FK on the ItemHistory table.
With the code I've listed I get this error:
Unable to determine the principal end of an association between the types 'InventoryLibrary.DomainModels.TransactionHistory' and 'InventoryLibrary.DomainModels.ItemHistory'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
How is this achieved in Entity Framework code first data annotations?
Firstly, you have to mark foreign keys by virtual keyword to enable overrides.
public class TransactionHistory : IDbEntity
{
[Key]
public int ID { get; set; }
public virtual ItemHistory ItemHistory { get; set; }
}
public class ItemHistory : IDbEntity
{
[Key]
public int ID { get; set; }
public int TransactionHistoryID { get; set; }
[Required]
public virtual TransactionHistory TransactionHistory { get; set; }
}
If HistoryItem must have Transaction History, add DataAnnotation [Required], which makes it non-nullable.
Finally, wonder, if you want to have one-to-one relationship. I imagine you'd like to have many transaction history entries. Am I right? If not - let me know.
To create one-to-many relationship, use IEnumerable<> type.

EF5, Inherited FK and cardinality

I have this class structure:
public class Activity
{
[Key]
public long ActivityId { get; set; }
public string ActivityName { get; set; }
public virtual HashSet<ActivityLogMessage> ActivityLogMessages { get; set; }
public virtual HashSet<FileImportLogMessage> FileImportLogMessages { get; set; }
public virtual HashSet<RowImportLogMessage> RowImportLogMessages { get; set; }
}
public abstract class LogMessage
{
[Required]
public string Message { get; set; }
public DateTimeOffset CreateDate { get; set; }
[Required]
public long ActivityId { get; set; }
public virtual Activity Activity { get; set; }
}
public class ActivityLogMessage : LogMessage
{
public long ActivityLogMessageId { get; set; }
}
public class FileImportLogMessage : ActivityLogMessage
{
public long? StageFileId { get; set; }
}
public class RowImportLogMessage : FileImportLogMessage
{
public long? StageFileRowId { get; set; }
}
Which gives me this, model
Each Message (Activity, File or Row) must have be associated with an Activity. Why does the 2nd and 3rd level not have the same cardinality as ActivityLogMessage ? My attempts at describing the foreign key relationship (fluent via modelbuilder) have also failed.
This is really an academic exercise for me to really understand how EF is mapping to relational, and this confuses me.
Regards,
Richard
EF infers a pair of navigation properties Activity.ActivityLogMessages and ActivityLogMessage.Activity with a foreign key property ActivityLogMessage.ActivityId which is not nullable, hence the relationships is defined as required.
The other two relationships are infered from the collections Activity.FileImportLogMessages and Activity.RowImportLogMessages. They neither have an inverse navigation property on the other side nor a foreign key property which will - by default - lead to optional relationships.
You possibly expect that LogMessage.Activity and LogMessage.ActivityId is used as inverse property for all three collections. But it does not work this way. EF cannot use the same navigation property in multiple relationships. Also your current model means that RowImportLogMessage for example has three relationships to Activity, not only one.
I believe you would be closer to what you want if you remove the collections:
public virtual HashSet<FileImportLogMessage> FileImportLogMessages { get; set; }
public virtual HashSet<RowImportLogMessage> RowImportLogMessages { get; set; }
You can still filter the remaining ActivityLogMessages by the derived types (for example in not mapped properties that have only a getter):
var fileImportLogMessages = ActivityLogMessages.OfType<FileImportLogMessage>();
// fileImportLogMessages will also contain entities of type RowImportLogMessage
var rowImportLogMessage = ActivityLogMessages.OfType<RowImportLogMessage>();

EF 5.0 code first navigation property

I have got a User entity there are my users are stored in. For some users (admins) I would like to add additional details.
I have written following code.
public partial class UserProfile
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[Display(Name = "EMail")]
[Required]
public string UserName { get; set; }
[ForeignKey("AdminDetailID")]
public virtual AdminDetail AdminDetail { get; set; }
public int? AdminDetailID { get; set; }
}
public class AdminDetail
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int AdminDetailID { get; set; }
[ForeignKey("UserId")]
public virtual UserProfile UserProfile { get; set; }
public int UserId { get; set; }
}
I like to navigate from my AdminDetail table back to my User Profile table by writing eg. admin.UserProfile.UserName. However, when I run Database-Update I receive:
The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
When I delete the UserProfile property everything works great.. How can I create a "back" navigation within my AdminDetail class?
Entity Framework Code-First allows for polymorphic classes to be stored in the same table. Have you considered using a relationship like this instead?
public partial class UserProfile
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[Display(Name = "EMail")]
[Required]
public string UserName { get; set; }
}
public class AdminProfile : UserProfile
{
// AdminProfile members.
}
This results in a UserProfile table with an additional column called Discriminator that EF creates and manages for you. This column indicates whether each row in the table is a UserProfile or an AdminProfile. Rows which are of type UserProfile ignore the columns that are specific to AdminProfile when accessed by EF.
Entity framework handles all of the type discrimination for you so you don't need to worry about that directly. Your DbContext will simply have a DbSet which can also store entities of type AdminProfile.
You don't need to have a FK in your UserProfile class. To set up a proper 1:1 only the AdminDetail class would actually need to have the foreign key to the UserProfile class. You can still keep the virtual property to be able to navigate back and forth, and EF will know what it is that you're doing. Similar to this:
public partial class UserProfile
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[Display(Name = "EMail")]
[Required]
public string UserName { get; set; }
public virtual AdminDetail AdminDetail { get; set; }
}

Table Per Hierarchy & Inherited Relationships

I'm using Entity Framework 5, targeting .Net 4.5. For the life of me I can't figure out what I'm doing wrong that's causing the following error while trying to work with Table Per Hierarchy and Navigation columns:
Invalid column name 'Game_Category'.
Invalid column name 'Game_Value'.
Invalid column name 'Type_Category'.
Invalid column name 'Type_Value'.
Here's the abstract base class (note the composite PK on Category and Value):
[Table("Dictionary")]
public abstract class Lookup
{
[Key, Column(Order = 0)]
[StringLength(50)]
public string Category { get; set; }
[StringLength(100)]
public string ExtendedValue { get; set; }
[Required]
public bool IsActive { get; set; }
[Required]
[StringLength(50)]
public string Key { get; set; }
[Key, Column(Order = 1)]
public int Value { get; set; }
}
Followed by two subclasses that add no additional columns...
public class Game : Lookup {}
public class SetType : Lookup {}
Here's the class with Navigation properties to Game and SetType...
public class CardSet
{
[Required]
[StringLength(10)]
public string Abbreviation { get; set; }
public virtual Game Game { get; set; }
[Required]
public int GameId { get; set; }
[Key]
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
[Required]
public DateTime ReleaseDate { get; set; }
public virtual Lookup Type { get; set; }
[Required]
public int TypeId { get; set; }
}
From my data context...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Lookup>()
.Map<Game>(l => l.Requires("LookupType").HasValue("Game"))
.Map<SetType>(l => l.Requires("LookupType").HasValue("Set Type"));
base.OnModelCreating(modelBuilder);
}
The lookup table has a discriminator column named LookupType. I've read through several tutorials on table/inheritance. The other two - TPT and TPC using similarly built objects were a cinch. While I understand the errors above - that it's looking for FK columns by convention, I don't understand what I'm doing wrong or missing that's causing it to look for those columns. I've tried placing ForeignKey attributes over the GameId and TypeId properties, but then I get errors about dependent/principal relationship constraints and I'm not sure how to specify the category as an additional foreign key.
I have yet to find a tutorial on table/inheritance that goes over navigation properties as I'm using them. Any help would be greatly appreciated, this has been driving me nuts for over an hour.
Update:
I believe the problem lies in the use of Category as part of the key. The CardSet doesn't have two properties for the category of "Game" for that lookup or the category for "Set Type" for that lookup. I tried creating these properties but that didn't work. Is it possible to set those using the Fluent API? I've made about a dozen attempts so far without any luck.
I think that EF does not "like" the construct modelBuilder.Entity<Lookup>() to map the two sub classes. This should help:
modelBuilder.Entity<Game>()
.Map(l => l.Requires("LookupType").HasValue("Game"));
modelBuilder.Entity<SetType>()
.Map(l => l.Requires("LookupType").HasValue("Set Type"));

Relationship mapping table with additional properties?

I have three classes:
public partial class Student : Contact
{
//Inherited from Contact:
//public int ContactId { get; set; }
//public string FirstName { get; set; }
//public string LastName { get; set; }
public virtual StudentExam StudentExam { get; set; }
}
public partial class Exam
{
public int ExamId { get; set; }
public string Title { get; set; }
public DateTime Date { get; set; }
public virtual StudentExam StudentExam { get; set; }
}
public partial class StudentExam
{
public byte Score { get; set; }
public int ContactId { get; set; }
public int ExamId { get; set; }
public virtual Student Student { get; set; }
public virtual Exam Exam { get; set; }
}
When trying to initialize the DbContext, it throws a ModelValidationException:
One or more validation errors were detected during model generation:
\tSystem.Data.Entity.Edm.EdmEntityType: : EntityType 'StudentExam' has no key defined. Define the key for this EntityType.
\tSystem.Data.Entity.Edm.EdmEntitySet: EntityType: EntitySet 'StudentExams' is based on type 'StudentExam' that has no keys defined.
I tried changing the StudentExam class' properties to the following:
[Key, ForeignKey("Student"), Column(Order = 0)]
public int ContactId { get; set; }
[Key, ForeignKey("Exam"), Column(Order = 1)]
public int ExamId { get; set; }
Now I get this exception:
\tSystem.Data.Entity.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'StudentExam_Student_Source' in relationship 'StudentExam_Student'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
\tSystem.Data.Entity.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'StudentExam_Exam_Source' in relationship 'StudentExam_Exam'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
Is there any way to achieve this in with data annotations (I don't like using the fluent API when I can use data annotations; The fluent API leads to messy code.
It is not about data annotations or fluent api but about incorrectly defined classes - relations defined in your classes cannot be mapped at all because they are not valid at relational level. You must modify your classes:
public partial class Student : Contact
{
public virtual ICollection<StudentExam> StudentExams { get; set; }
}
public partial class Exam
{
...
public virtual ICollection<StudentExam> StudentExams { get; set; }
}
Once you have this relations defined you can use your data annotations for defining keys in StudentExam class and it will work.
Btw. fluent api doesn't lead to messy code. Messy code is created by programmer, not by API. Data annotations in turn violates POCO principle.