Code First Foreign Keys between two entities - c#

OK what am I missing here or is this just able to be done with data annotation?
I have a Document Entity Model which has a Foreign Key to a User that added the document (one-to-one relationship):
[Table("Documents", Schema = "Configuration")]
public class Document : IPrimaryKey {
[Key]
public long Id { get; set; }
[Required]
public string OrginalName { get; set; }
[Required]
public DocumentTypes DocumentType { get; set; }
[Required]
public MIMETypes MIMEType { get; set; }
[Required]
public byte[] Data { get; set; }
[DefaultValue(false)]
public bool IsPublic { get; set; }
[Required]
public DateTimeOffset DateTimeAdded { get; set; }
[Required]
public long AddedByUser { get; set; }
[ForeignKey("AddedByUser")]
public virtual Details Details { get; set; }
}
I then have a User (Details) Entity that can have an image file (which is stored in the document entities model (none|one-to-one relationship):
[Table("Details", Schema = "User")]
public class Details : IPrimaryKey {
[Key]
public long Id { get; set; }
[Required]
public string UserId { get; set; }
[ForeignKey("UserId")]
public AppUser User { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[CollectionRequired(MinimumCollectionCount = 1)]
public ICollection<Address> Addresses { get; set; }
[CollectionRequired(MinimumCollectionCount = 1)]
public ICollection<Email> Emails { get; set; }
[CollectionRequired(MinimumCollectionCount = 1)]
public ICollection<PhoneNumber> PhoneNumbers { get; set; }
public ICollection<NotificationHistory> NotificationHistory { get; set; }
public long TimeZoneId { get; set; }
public long? ImageId { get; set; }
[ForeignKey("ImageId")]
public virtual Document Document { get; set; }
[ForeignKey("TimeZoneId")]
public virtual TimeZone TimeZone { get; set; }
}
When I try to create a Migration I get this error:
Unable to determine the principal end of an association between the
types 'StACS.PeoplesVoice.DataAccessLayer.EntityModels.User.Details'
and
'StACS.PeoplesVoice.DataAccessLayer.EntityModels.Configuration.Document'.
The principal end of this association must be explicitly configured
using either the relationship fluent API or data annotations.
UPDATED:
While still researching this I made two changes and was able to get around the error but this created an unexpected result in my database.
In the Document Entity I added:
public virtual ICollection<Details> Details { get; set; }
In the Details (user) Entity I added:
puflic virtual ICollection<Document> Documents { get; set; }
In my DB Tables I now have the foreign key on the field I want but I have a secondary foreign key for each respectively.
I tried just removing the single virtual reference and left ONLY the ICollection Virtual reference, now I have no foreign key at all.
UPDATED (based on Akash Kava Suggestion):
I have made the following changes
[Table("Documents", Schema = "Configuration")]
public class Document : IPrimaryKey {
[Required]
public string OrginalName { get; set; }
[Required]
public DocumentTypes DocumentType { get; set; }
[Required]
public MIMETypes MIMEType { get; set; }
[Required]
public byte[] DocumentData { get; set; }
[DefaultValue(false)]
public bool IsPublic { get; set; }
[Required]
public DateTimeOffset DateTimeAdded { get; set; }
[Required]
public long AddedByUser { get; set; }
[Key]
public long Id { get; set; }
[ForeignKey("AddedByUser")]
[InverseProperty("Image")]
public virtual Details User { get; set; }
}
[Table("Details", Schema = "User")]
public class Details : IPrimaryKey {
[Required]
public string UserId { get; set; }
[ForeignKey("UserId")]
public AppUser User { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[CollectionRequired(MinimumCollectionCount = 1)]
public ICollection<Address> Addresses { get; set; }
[CollectionRequired(MinimumCollectionCount = 1)]
public ICollection<Email> Emails { get; set; }
[CollectionRequired(MinimumCollectionCount = 1)]
public ICollection<PhoneNumber> PhoneNumbers { get; set; }
public ICollection<NotificationHistory> NotificationHistory { get; set; }
public long TimeZoneId { get; set; }
public long? ImageId { get; set; }
[ForeignKey("ImageId")]
[InverseProperty("User")]
public Document Image { get; set; }
[ForeignKey("TimeZoneId")]
public virtual TimeZone TimeZone { get; set; }
[Key]
public long Id { get; set; }
}
I have commented out the Fluent API Code
Unable to determine the principal end of an association between the
types 'StACS.PeoplesVoice.DataAccessLayer.EntityModels.User.Details'
and
'StACS.PeoplesVoice.DataAccessLayer.EntityModels.Configuration.Document'.
The principal end of this association must be explicitly configured
using either the relationship fluent API or data annotations.

You can achieve same with Data Annotation as well, you are missing InverseProperty attribute, which resolves ambiguity in this case. Conceptually, every navigation property has Inverse Navigation property, EF automatically detects and assumes inverse property based on type, but if two entities are related to each other by multiple FK properties, you have to explicitly specify InverseProperty attribute on corresponding navigation properties.
I would recommend putting InverseProperty on every navigation property, which helps reduce startup time for EF as EF does not have to determine and validate the model.
Example,
public class AccountEmail {
public long AccountID {get;set;}
// Inverse property inside Account class
// which corresponds to other end of this
// relation
[InverseProperty("AccountEmails")]
[ForeignKey("AccountID")]
public Account Account {get;set;}
}
public class Account{
// Inverse property inside AccountEmail class
// which corresponds to other end of this
// relation
[InverseProperty("Account")]
public ICollection<AccountEmail> AccountEmails {get;set;}
}
I have written a text template which generates all these navigation properties based on current schema. Download all three files from https://github.com/neurospeech/atoms-mvc.net/tree/master/db-context-tt, you might have to customize this as it adds few more things based on our framework, but it does generate pure code model from your database directly.

OK I finally figured this out. Sadly this is not very straight forward as I think Data Annotation should work BUT it does not.
You HAVE to use Fluent API:
modelBuilder.Entity<Details>()
.HasOptional(x => x.Document)
.WithMany()
.HasForeignKey(x => x.ImageId);
modelBuilder.Entity<Document>()
.HasRequired(x => x.User)
.WithMany()
.HasForeignKey(x => x.AddedByUser);

Related

Entity Framework error : Unable to determine the principal end of an association between the types

I'm using Entity Framework to build my database. My model contain two entities: Entite and ApplicationUser (see code below).
There are two relations between these entities:
One-to-Many: an Entite could contain one or many users. And a user belongs to one Entite.
One-to-One: an Entite must have one user as a responsible and a user can be responsible for only one Entite.
Entite:
public class Entite : AuditableEntity<int>
{
[Required]
[MaxLength(10)]
public String code { get; set; }
[Required]
[MaxLength(50)]
public String Libelle { get; set; }
[Required]
[MaxLength(10)]
public String type { get; set; }
[Key, ForeignKey("ResponsableId")]
public int? ResponsableId { get; set; }
public virtual ApplicationUser responsable { get; set; }
public int? RattachementEntiteId { get; set; }
[Key, ForeignKey("RattachementEntiteId")]
public virtual Entite rattachement { get; set; }
public List<Entite> Children { get; set; }
}
ApplicationUser:
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Matricule { get; set; }
public DateTime? dateRecrutement { get; set; }
public int? entiteId { get; set; }
[ForeignKey("entiteId")]
public virtual Entite entite { get; set; }
}
When I tried to build the database using the Add-Migration command, I got this error :
Unable to determine the principal end of an association between the types
Any idea about this issue?
Thanks for your help
It looks like a small typo/error in your Entite model class.
The ForeignKey should be referencing your ApplicationUser, currently it is referencing itself, and a new Key will be generated for the responsable.
If we swap the ForeignKey reference to below, then this looks like it should solve your issue:
[Key, ForeignKey("responsable")]
public int? ResponsableId { get; set; }
public virtual ApplicationUser responsable { get; set; }
Or you can swap the reference like you have done on your rattachement.
public int? ResponsableId { get; set; }
[Key, ForeignKey("ResponsableId ")]
public virtual ApplicationUser responsable { get; set; }

Entity framework Code First One-to-One relationship

I have two entities which I want to be connected 1:1 relationship. User is principal and UserActivation is dependent, but I have no idea how that works.
public class User
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public string Lastname { get; set; }
public string Username { get; set; }
public virtual UserActivation UserActivation { get; set; }
}
public class UserActivation
{
[Key]
public Guid Id { get; set; }
public Guid UserId { get; set; }
public bool Active { get; set; }
public virtual User User { get; set; }
}
I have tried to remove 'virtual' keyword, have tried to add ForeignKey("UserId") or ForeignKey("User"), I've even tried to make [Key, ForeignKey("User") and none of them helped me. I want to make 1:1 relationship using only dataannotations. Any help is really appreciated. Also my both classes has their own PKs.
Foreign keys are not supported for 1:1 try:
public class User
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public string Lastname { get; set; }
public string Username { get; set; }
public virtual UserActivation UserActivation { get; set; }
}
public class UserActivation
{
[Key]
[ForeignKey("User")]
public Guid Id { get; set; }
public bool Active { get; set; }
public virtual User User { get; set; }
}
Unable to determine the principal end of an association between the types ‘Model.PersonPhoto’ and ‘Model.Person’. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
Julie Lehrman discusses this in her Code First book:
"This problem is most easily solved by using a ForeignKey annotation
on the dependent class to identify that it contains the foreign key.
When configuring one-to-one relationships, Entity Framework requires
that the primary key of the dependent also be the foreign key. In our
case PersonPhoto is the dependent and its key, PersonPhoto.PersonId,
should also be the foreign key. Go ahead and add in the ForeignKey
annotation to the PersonPhoto.PersonId property, as shown in Example
4-21. Remember to specify the navigation property for the relationship
when adding the ForeignKey annotation."
This post is quite old so I thought I'd post the EF 6 solution
Try this...
public class User
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public string Lastname { get; set; }
public string Username { get; set; }
public virtual UserActivation UserActivation { get; set; }
}
public class UserActivation
{
[ForeignKey("User")]
public Guid Id { get; set; }
public Guid UserId { get; set; }
public bool Active { get; set; }
public virtual User User { get; set; }
}

Relationships in Entity Framework Code First

yesterday I created database in Management Studio and now I want to create it in program using EF Code First.
Here is link to my database: http://s11.postimg.org/6sv6cucgj/1462037_646961388683482_1557326399_n.jpg
And what I did:
public class GameModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreationTime { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public string TotalTime { get; set; }
public DateTime RouteStartTime { get; set; }
public DateTime RouteEndTime { get; set; }
public int MaxPlayersPerTeam { get; set; }
public int CityId { get; set; }
public int CreatorId { get; set; }
[InverseProperty("Id")]
[ForeignKey("CreatorId")]
//public int TeamId { get; set; }
//[ForeignKey("TeamId")]
public virtual UserModel Creator { get; set; }
public virtual CityModel City { get; set; }
//public virtual TeamModel WinnerTeam { get; set; }
}
public class RegionModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<CityModel> Cities { get; set; }
}
public class CityModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int RegionId { get; set; }
public virtual RegionModel Region { get; set; }
public virtual ICollection<UserModel> Users { get; set; }
public virtual ICollection<GameModel> Games { get; set; }
}
public class UserModel
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public DateTime RegistrationDate { get; set; }
public string FacebookId { get; set; }
public int CityId { get; set; }
public virtual CityModel City { get; set; }
public virtual IEnumerable<GameModel> Games { get; set; }
}
For now I wanted to create 4 tables but I have some problems... I want to make CreatorId in GameModel, but it doesn't work... When i wrote UserId instead of CreatorId it was working ( without [InverseProperty("Id")] and [ForeignKey("CreatorId")]).
This is what i get:
The view 'The property 'Id' cannot be configured as a navigation property. The property must be a valid entity type and the property should have a non-abstract getter and setter. For collection properties the type must implement ICollection where T is a valid entity type.' or its master was not found or no view engine supports the searched locations.
edit:
I changed it like this:
public int CityId { get; set; }
public int CreatorId { get; set; }
[ForeignKey("CityId")]
public virtual CityModel City { get; set; }
[ForeignKey("CreatorId")]
public virtual UserModel Creator { get; set; }
And there is another problem.
The view 'Introducing FOREIGN KEY constraint 'FK_dbo.UserModels_dbo.CityModels_CityId' on table 'UserModels' 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.' or its master was not found or no view engine supports the searched locations.
And I have no idea how to solve it.
The InversePropertyAttribute specifies, which navigation property should be used for that relation.
A navigation property must be of an entity type (the types declared in your model, GameModel for example) or some type implementing ICollection<T>, where T has to be an entity type. UserModel.Id is an int, which clearly doesn't satisfy that condition.
So, the inverse property of GameModel.Creator could be UserModel.Games if you changed the type to ICollection<GameModel>, or had to be left unspecified. If you don't specify an inverse property, EF will try to work everything out on its own (in this case it would properly recognize GameModel.Creator as a navigation property, but UserModel.Games would most likely throw an exception, as it is neither an entity type, nor does it implement ICollection<T> with T being an entity type, nor is it a primitive type from a database point of view). However, EF's work-everything-out-by-itself-magic doesn't cope too well with multiple relations between the same entity types, which is when the InversePropertyAttribute is needed.
A quick example that demonstrates the problem:
class SomePrettyImportantStuff {
[Key]
public int ID { get; set; }
public int OtherId1 { get; set; }
public int OtherId2 { get; set; }
[ForeignKey("OtherId1")]
public virtual OtherImportantStuff Nav1 { get; set; }
[ForeignKey("OtherId2")]
public virtual OtherImportantStuff Nav2 { get; set; }
}
class OtherImportantStuff {
[Key]
public int ID { get; set; }
public virtual ICollection<SomePrettyImportantStuff> SoldStuff { get; set; }
public virtual ICollection<SomePrettyImportantStuff> BoughtStuff { get; set; }
}
Here, EF knows that it has to generate 2 FKs from SomePrettyImportantStuff to OtherImportantStuff with the names Id1 and Id2, but it has no way to tell which of the IDs refers to the entity where it was sold from and which is the one it was bought from.
Edit: How to fix the cyclic reference problem
To fix that problem, your context class should override OnModelCreating and configure the foreign keys which shouldn't cascade on delete accordingly, like this:
protected override void OnModelCreating(DbModelBuilder builder)
{
builder.Entity<CityModel>().HasMany(c => c.Users).WithRequired(u => u.City)
.HasForeignKey(u => u.CityId).WillCascadeOnDelete(value: false);
// Add other non-cascading FK declarations here
base.OnModelCreating(builder);
}

How I can achieve two different foreign keys in Entity Framework?

I need to bind some relations in Entity Framework Code First but I don't know how to. I searched deeply, I tried a lot of ways but still without luck. I think I need to use the fluent API.
Scenario
Organizations has many Notes.
Projects has many Notes.
Notes has one Organization or Project.
The idea is to bind every entity (Organization or Project) to only one column in Note entity: SourceKey.
Organization
public class Organization
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ID { get; set; }
[Required(ErrorMessage = "A name is required.")]
public string Name { get; set; }
public virtual ICollection<Project> Projects { get; set; }
public virtual ICollection<Note> Notes { get; set; }
}
Project
public class Project
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ID { get; set; }
[Required(ErrorMessage = "An organization is required.")]
[Display(Name = "Organization")]
public Guid OrganizationID { get; set; }
[Required(ErrorMessage = "A name is required.")]
public string Name { get; set; }
public virtual Organization Organization { get; set; }
public virtual ICollection<Note> Notes { get; set; }
}
Note
public class Note
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ID { get; set; }
public Guid SourceKey { get; set; }
[Required(ErrorMessage = "A title is required.")]
public string Title { get; set; }
[Column(TypeName = "ntext")]
public string Value { get; set; }
public string Tags { get; set; }
}
So an example of data can be:
Organizations
7846ac27-d490-4483-8f0b-975a11333dea, Google
Projects
3446ac27-d490-4323-8121-921a11333dac, Search
7846ac27-8497-5683-213b-933a11233abc, Maps
Notes
1236ac27-d490-4323-8121-921a11333dac, 7846ac27-d490-4483-8f0b-975a11333dea, A note for Google organization
2346ac27-d490-4323-8121-921a11335aab, 7846ac27-d490-4483-8f0b-975a11333dea, Another note for Google organization
3456ac27-d490-4323-8121-921a11331bcc, 7846ac27-8497-5683-213b-933a11233abc, Just a note for Maps project
BTW
I don't need to navigate from Notes (up) to Organization or Project. If I have an Organization or Project, I will need to navigate (down) to Notes.
As mentioned above, if you don't require navigation properties from notes, you can omit them.
If you then included two fields on the Note class to use for foreign key associations:
public class Note
{
// Code
public Guid OrganizationId { get; set; }
public Guid ProjectId { get; set; }
}
I assume most notes won't belong to both an Organisation, and a Project (although they can).
You should be able to configure your mapping using the fluent API like this:
modelBuilder.Entity<Organization>()
.HasMany(o => o.Notes) // Many Notes
.WithOptional() // No navigation property on Notes
.HasForeignKey(n => n.OrganizationId ); // Use OrganizationId as a foreign key
modelBuilder.Entity<Project>()
.HasMany(o => o.Notes)
.WithOptional()
.HasForeignKey(n => n.ProjectId);
You can define the Note entity without navigation properties,
public class Note
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ID { get; set; }
public Guid SourceKey { get; set; }
[Required(ErrorMessage = "A title is required.")]
public string Title { get; set; }
[Column(TypeName = "ntext")]
public string Value { get; set; }
public string Tags { get; set; }
public int OrganizationId { get; set; }
public int ProjectId { get; set; }
}
If you don't need navigation properties, you can define it simply using the class below:
public class Note
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid NoteID { get; set; }
public Guid SourceKey { get; set; }
[Required(ErrorMessage = "A title is required.")]
public string Title { get; set; }
[Column(TypeName = "ntext")]
public string Value { get; set; }
public string Tags { get; set; }
public Guid OrganizationId { get; set; }
public Guid ProjectId { get; set; }
}
Note OrganizationId and ProjectId are of type Guid.
I think that the use of one column (SourceKey) for multiple foreign keys is not possible. It throws you an error. I was pretty sure that was possible but maybe I'm confused with the MyISAM tables in MySQL. I will use the foreign keys columns in the Note model as Chris suggest. Thanks to everybody.

EF 4.1 code first Invalid column name "User_UserId"

I can't figure out why this is causing EF error: Invalid column name 'User_UserId' when saving in EF.
Here is my model:
[DataContract(IsReference = true)]
public class User
{
[Key]
[DataMember]
public virtual Guid UserId { get; set; }
[DataMember]
public virtual string Username { get; set; }
[DataMember]
public virtual string Password { get; set; }
[DataMember]
public virtual string Email { get; set; }
[DataMember]
public virtual ICollection<FriendList> FriendLists { get; set; }
}
[DataContract(IsReference = true)]
public class FriendList
{
[Key]
[DataMember]
public virtual Guid FriendListId { get; set; }
[DataMember]
[ForeignKey("User")]
public virtual Guid UserId { get; set; }
[DataMember]
public virtual User User { get; set; }
[DataMember]
[ForeignKey("FriendUser")]
public virtual Guid FriendUserId { get; set; }
[DataMember]
public virtual User FriendUser { get; set; }
}
basically, its one to many relationship with users having a friendlists.
You have two navigation properties of type User in your FriendList class. EF cannot figure out which of these belong to User.FriendLists and then creates for all three navigation properties a separate one-to-many relationship, one of them has the default foreign key name User_UserId.
You can overwrite this convention with the InverseProperty attribute:
public class FriendList
{
// ...
[DataMember]
[InverseProperty("FriendLists")]
public virtual User User { get; set; }
// ...
}
Now, User.FriendLists and FriendList.User are the endpoints of the same one-to-many relationship and FriendList.FriendUser defines a second one-to-many relationship (but without an endpoint in the User class).
I guess:
1) The attribute ForeignKey in your case must be set as [ForeignKey("UserId")] and not as [ForeignKey("User")]
2) Or If one of these classes are not mapped you must set the attribute [NotMapped] on it;
Your ForeignKey attribute is in the wrong place.
Try this:
[DataMember]
public virtual Guid UserId { get; set; }
[DataMember]
[ForeignKey("UserId")]
public virtual User User { get; set; }
[DataMember]
public virtual Guid FriendUserId { get; set; }
[DataMember]
[ForeignKey("FriendUserId")]
public virtual User FriendUser { get; set; }
At least it worked for me.

Categories

Resources