I have a linker table which links together two players. A player issues a challenge to another player, thus I have two of the same keys PlayerId. However, this seems to be creating issues.
I have tested out the following scenarios:
Virtual Properties:
public class WordChallenge
{
[...]
[Required]
public virtual Player IssuingPlayer { get; set; }
[Required]
public virtual Player ChallengedPlayer { get; set; }
}
Is producing the following exception during runtime:
Cannot insert duplicate key row in object 'dbo.Players' with unique index 'IX_Username'.
ForeignKey Attribute:
public class WordChallenge
{
[...]
[Required]
[ForeignKey("PlayerId")]
public virtual Player IssuingPlayer { get; set; }
[Required]
[ForeignKey("PlayerId")]
public virtual Player ChallengedPlayer { get; set; }
}
Throws an exception during the Add-Migration command
The ForeignKeyAttribute on property 'ChallengedPlayer' on type 'WhatIsThisWord.WebAPI.Models.WordChallenge' is not valid. The foreign key name 'PlayerId' was not found on the dependent type 'WhatIsThisWord.WebAPI.Models.WordChallenge'.
The goal I am trying to achieve is to be able to have both playerIds be in the table.
Player Model:
DataContract controls JSON serialization
[DataContract]
public class Player
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key, Column(Order=0)]
[DataMember]
public Guid PlayerId { get; set; }
public virtual ICollection<Player> Friends { get; set; }
[Required]
public virtual string Password { get; set; }
[MaxLength(100)]
[Index(IsUnique = true)]
[DataMember]
public string Username { get; set; }
[Required]
public string Email { get; set; }
public virtual ICollection<WordChallenge> IssuedChallenges { get; set; }
public virtual ICollection<WordChallenge> ReceivedChallenges { get; set; }
}
Insert Code:
public async Task<Models.WordChallenge> CreateChallenge(Player challenger, Player challengeReceiver, WordChallenge challenge)
{
using (var model = _modelFactory.New())
{
challenge.IssuingPlayer = challenger;
challenge.ChallengedPlayer = challengeReceiver;
model.WordChallenges.Add(challenge);
await model.SaveChangesAsync();
return challenge;
}
}
Can you try building a model like this
public class WordChallenge
{
[Required]
public Guid IssuingPlayerID { get; set; }
[Required]
public Guid ChallengedPlayerID { get; set; }
[ForeignKey("IssuingPlayerId")]
public virtual Player IssuingPlayer { get; set; }
[ForeignKey("ChallengedPlayerID")]
public virtual Player ChallengedPlayer { get; set; }
}
The model which you have created would try to duplicate the relationship.
Hope this will work
Related
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; }
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);
I'm having trouble creating my database with EF code-first. I have an entity Player and an entity friedship.
Each friendship references two players. One of the players is the sender, the other one is the receiver of the friendship.
This are my entities:
Player.cs
public class Player
{
public int PlayerId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
[InverseProperty("Receiver")]
public virtual List<Friendship> FriendshipsIncoming { get; set; }
[InverseProperty("Sender")]
public virtual List<Friendship> FriendshipsOutgoing { get; set; }
}
Friendship.cs
public class Friendship
{
public int FriendshipId { get; set; }
public int SenderId { get; set; }
public int ReceiverId { get; set; }
[ForeignKey("Sender")]
public Player Sender { get; set; }
[ForeignKey("Receiver")]
public Player Receiver { get; set; }
[Required]
public bool Confirmed { get; set; }
}
I tried implementing the relationsships the way shown in this tutorial:
http://www.entityframeworktutorial.net/code-first/inverseproperty-dataannotations-attribute-in-code-first.aspx
When trying to update the database with the "update-database" command i'm getting the following error message:
The ForeignKeyAttribute on property 'Receiver' on type 'Darta.WebApi.Models.Friendship' is not valid. The foreign key name 'Receiver' was not found on the dependent type 'Darta.WebApi.Models.Friendship'. The Name value should be a comma separated list of foreign key property names.
I also tried fixing the issue with fluent-api like shown here:
http://csharpwavenet.blogspot.sg/2013/06/multiple-foreign-keys-with-same-table.html
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Friendship>()
.HasRequired(b => b.Sender)
.WithMany(a => a.FriendshipsOutgoing)
.HasForeignKey(b=>b.SenderId);
modelBuilder.Entity<Friendship>()
.HasRequired(b => b.Receiver)
.WithMany(a => a.FriendshipsIncoming)
.HasForeignKey(b => b.ReceiverId);
}
In this case I'm getting the following error:
Introducing FOREIGN KEY constraint 'FK_dbo.Friendships_dbo.Players_SenderId' on table 'Friendships' 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 or index. See previous errors.
You should only need either the DataAnnotations or the FluentAPI. You don't need both. If you want to use the [ForeignKey] and [InverseProperty] attributes, then get rid of the FluentAPI code.
Also, note that in the [ForeignKey] and [InverseProperty] attributes, you need to specify the name of the column, not the navigation property.
public class Player
{
public int PlayerId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
[InverseProperty("ReceiverId")]
public virtual ICollection<Friendship> FriendshipsIncoming { get; set; }
[InverseProperty("SenderId")]
public virtual ICollection<Friendship> FriendshipsOutgoing { get; set; }
}
public class Friendship
{
public int FriendshipId { get; set; }
public int SenderId { get; set; }
public int ReceiverId { get; set; }
[ForeignKey("SenderId")]
public Player Sender { get; set; }
[ForeignKey("ReceiverId")]
public Player Receiver { get; set; }
[Required]
public bool Confirmed { get; set; }
}
I'll correct the answer. InverseProperty must be a valid entity type. So in this case Friendship.Receiver, Friendship.Sender
public class Player
{
public int PlayerId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
[InverseProperty("Receiver")]
public virtual ICollection<Friendship> FriendshipsIncoming { get; set; }
[InverseProperty("Sender")]
public virtual ICollection<Friendship> FriendshipsOutgoing { get; set; }
}
public class Friendship
{
public int FriendshipId { get; set; }
public int SenderId { get; set; }
public int ReceiverId { get; set; }
[ForeignKey("SenderId")]
public Player Sender { get; set; }
[ForeignKey("ReceiverId")]
public Player Receiver { get; set; }
[Required]
public bool Confirmed { get; set; }
}
I'm experiencing an unexpected error when setting up a migration after adding keys and foreign keys to my data model. I'm using VS2013 Express, with .NET framework 4.5.
When creating a data model for Entity Framework, because the relationship keys between classes aren't what is expected by convention, I'm using data annotations as outlined in the MS Data Developer Center. Here's the class code:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace BacklogTracker.Models
{
public class WorkOrder
{
[Key]
public string woNum { get; set; }
public string woClosingStatus { get; set; }
[ForeignKey("ID")]
public virtual ICollection<Note> woNotes { get; set; }
[ForeignKey("machSN")]
public virtual Machine woMachine { get; set; }
[ForeignKey("ID")]
public virtual ICollection<Segment> woSegments { get; set; }
}
public class Machine
{
[Key]
public string machSN { get; set; }
public string machLocation { get; set; }
public string machModel { get; set; }
}
public class Segment
{
[Key]
public int ID { get; set; }
public uint segNum { get; set; }
public string segRepair { get; set; }
[ForeignKey("ID")]
public virtual ICollection<Note> segNotes { get; set; }
}
public class Note
{
[Key]
public int ID { get; set; }
public DateTime notetimestamp { get; set; }
public string notestring { get; set; }
}
}
However, when I try to perform a migration after updating the model by performing enable-migrations in the package manager console, I get the following error:
The ForeignKeyAttribute on property 'woMachine' on type
'BacklogTracker.Models.WorkOrder' is not valid. The foreign key name
'machSN' was not found on the dependent type
'BacklogTracker.Models.WorkOrder'. The Name value should be a comma
separated list of foreign key property names.
Why is my foreign key name 'machSN' not being found?
I think you have some errors in your model. Default Code First convention for ForeignKey relationship expected to have declared a foreign key property in the dependend end (WorkOrder) that match with primary key property of the principal end (Machine). It is not necessary that they have the same name, check this link. So, declare a property named machSN in your WorkOrder class:
public class WorkOrder
{
[Key]
public string woNum { get; set; }
public string woClosingStatus { get; set; }
public virtual ICollection<Note> woNotes { get; set; }
public string machSN { get; set; }
[ForeignKey("machSN")]
public virtual Machine woMachine { get; set; }
public virtual ICollection<Segment> woSegments { get; set; }
}
You can find other errors in the woNotes and woSegments navigation properties. In this side of a one-to-many relationship you don't declare a FK, is in the other side, in Note and Segment classes, for example:
public class Note
{
[Key]
public int ID { get; set; }
public DateTime notetimestamp { get; set; }
public string notestring { get; set; }
[ForeignKey("Order)]
public string woNum { get; set; }
public virtual WorkOrder Order{get;set;}
}
Delete also in the Segment class the ForeignKey attribute over segNotes navigation property for the same reasons explained before.
public class Segment
{
[Key]
public int ID { get; set; }
public uint segNum { get; set; }
public string segRepair { get; set; }
public virtual ICollection<Note> segNotes { get; set; }
}
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);
}