Entity Framework multiple references to same table - c#

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

Related

Foreign key Entity Framework - code first migrations

I have a problem with my entities. I'm using EF code-first migrations and the migrations are failing with this error:
Introducing FOREIGN KEY constraint 'FK_OrdersChildsProducts_Orders_OrderId' on table 'OrdersChildsProducts' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Here's my PersonJceProfile entity :
[Table("PersonJceProfiles")]
public class PersonJceProfile : BaseEntity
{
[ForeignKey("Ces")]
public int? CeId { get; set; }
public ICollection<Child> Children { get; set; }
public Order Order { get; set; }
public PersonJceProfile()
{
Children = new List<Child>();
}
}
Here's my Order entity :
[Table("Orders")]
public class Order : BaseEntity
{
[Key]
public int Id { get; set; }
//ForeignKey
[Required]
[ForeignKey("PersonJceProfiles")]
public int PersonJceProfileId { get; set; }
[Required]
public int OrderStatus { get; set; }
[Required]
public bool IsSecurePayment { get; set; }
public int LeftToPayPersonOrder { get; set; }
public string Delivery { get; set; }
public ICollection<OrderChildProduct> OrderChildProduct { get; set; }
public Order()
{
OrderChildProduct = new Collection<OrderChildProduct>();
}
}
Here's my Child entity :
[Table("Childrens")]
public class Child :BaseEntity
{
[Key]
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public DateTime? BirthDate { get; set; }
[Required]
public string Gender { get; set; }
public bool? IsActif { get; set; }
public decimal AmountParticipationCe { get; set; }
public bool? IsRegrouper { get; set; }
[Required]
[ForeignKey("PersonJceProfiles")]
public int PersonJceProfileId { get; set; }
}
Here's my Product Entity
public class Product : Good
{
public string File { get; set; }
public bool? IsDisplayedOnJCE { get; set; }
public bool? IsBasicProduct { get; set; }
public int? PintelSheetId { get; set; }
public int OriginId { get; set; }
[Required]
[ForeignKey("Suppliers")]
public int SupplierId { get; set; }
}
Here's my OrderChildProduct entity :
[Table("OrdersChildsProducts")]
public class OrderChildProduct
{
public int OrderId { get; set; }
public int ChildId { get; set; }
public int ProductId { get; set; }
public int LeftToPayChildOrder { get; set; }
public Order Order { get; set; }
public Child Child { get; set; }
public Product Product { get; set; }
}
Here's my context :
modelBuilder.Entity<OrderChildProduct>().HasKey(ccp => new { ccp.OrderId, ccp.ChildId, ccp.ProductId });
I suppose i do destro a relationship like this :
modelBuilder.Entity<Entity>()
.HasRequired(c => c.ForeignKey)
.WithMany()
.WillCascadeOnDelete(false);
but I can't see between which. Because
When I delete PersonJceProfiles : Order must be deleted - OrderChildProduct must be deleted - Child must be deleted
When I delete Order : OrderChildProduct must be deleted
When I delete Order childProduct : nothing must be deleted expect himself
What am I doing wrong? Thanks
The error is self described. You must configure the relationships in the OrderChildProduct table like:
entity.HasOne(p => p.Order)
.WithMany(p => p.OrderChildProduct)
.HasForeignKey(p => p.OrderId)
.OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);
You can use either Restrict or Cascade depending on your requirements.
Also the other 2 relationships must be defined as well.
This is not an error. This is more like a warning... the way EF tells you that it doesn't fully understand the relationships and you must configure them manually.

Cannot create controller with Entity Framework

Getting the following error when trying to create a controller.
There was an error running the selected code generator: "Unable to retrieve metadata for "ProjectName.Models.Tecnologia". One or more validation errors were detected during model generation:
ProjectName.DataContexts.Estadistica: EntityType "Estadistica" has no key defined. Define the key for this EntityType.
Estadisticas: EntityType: EntitySet: "Estadisticas" is based on type "Estadistica" that has no key defined.
Class Tecnologia:
public class Tecnologia
{
public int ID { get; set; }
public string Nombre { get; set; }
public List<Usuario> TutoresCorrectores { get; set; }
public List<FichaProyecto> FichasProyecto { get; set; }
}
Class Estadistica
public class Estadistica
{
public int Cantidad { get; set; }
public int Porcentaje { get; set; }
}
Class DataContexts.GestionProyectodbContext
public class GestionProyectodbContext : DbContext
{
public GestionProyectodbContext() : base("DefaultConnection")
{
}
public DbSet<Carrera> Carreras { get; set; }
public DbSet<Comentario> Comentarios { get; set; }
public DbSet<EstadoFicha> Estados { get; set; }
public DbSet<FichaProyecto> FichasProyectos { get; set; }
public DbSet<Grupo> Grupos { get; set; }
public DbSet<InformeAvance> InformesAvance { get; set; }
public DbSet<InstanciaAcademica> InstanciasAcademicas { get; set; }
public DbSet<InstanciaEvaluacion> InstanciasEvaluacion { get; set; }
public DbSet<PropuestaProyecto> PropuestasProyectos { get; set; }
public DbSet<Reunion> Reuniones { get; set; }
public DbSet<Rol> ListaRoles { get; set; }
public DbSet<Tecnologia> Tecnologias { get; set; }
public DbSet<TipoAplicacion> TiposAplicaciones { get; set; }
public DbSet<TipoCliente> TiposClientes { get; set; }
public DbSet<TipoProyecto> TiposProyectos { get; set; }
public DbSet<Usuario> Usuarios { get; set; }
public DbSet<InformeTarea> InformesTareas { get; set; }
public DbSet<Documento> Documentos { get; set; }
public DbSet<InformeCorreccion> InformesCorreccion { get; set; }
}
As seen, class "Estadistica" does not have an "ID" prop, but that's because I don't want to persist it in database. Id isn't even in the "GestionProyectodbContext" class, so it shouldn't be a problem. But when trying to create a controller por class "Tecnologia", an error saying that "Estadistica" has no key is popping. I don't know why this error is coming out, and I would some help from you if you somehow know why this happens.
PD: class "Tecnologia" is not even referring to class "Estadistica".
PD2: I know how to solve this error, but it's not the way I should be doing it, because I don't want to add an "ID" property into a class that I don't want to persist in the database.
You must have an mapped entity referencing Estadistica apply the NotMapped attribute to it.
you can also use the fluent api to ignore it:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Ignore<Estadistica>();
}
EF only works when it knows the primary key of the table, By default it recognizes the primary key with name Id, but if you don't have Id column name in your table then decorate its primary key with [Key] attribute.
public class Estadistica
{
[Key]
public int Cantidad { get; set; }
public int Porcentaje { get; set; }
}
hope it helps.

Entity Framework Model With Multiple Same Foreign Keys

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

EF TPT generating duplicate foreign keys

I am writing an application which uses inheritance and I'm trying to map this to a SQL Server database with TPT structure.
However, for some reason EF generates duplicate foreign keys in both the superclass and subclass tables.
I have these classes:
public abstract class Answer
{
public int AnswerId { get; set; }
[Required]
[MaxLength(250, ErrorMessage = "The answer cannot contain more than 250 characters")]
public String Text { get; set; }
[Required]
[MaxLength(1500, ErrorMessage = "The description cannot contain more than 1500 characters")]
public String Description { get; set; }
public User User { get; set; }
}
public class AgendaAnswer : Answer
{
[Required]
public AgendaModule AgendaModule { get; set; }
}
public class SolutionAnswer : Answer
{
[Required]
public SolutionModule SolutionModule { get; set; }
}
public abstract class Module
{
// for some reason EF doesn't recognize this as primary key
public int ModuleId { get; set; }
[Required]
public String Question { get; set; }
public String Description { get; set; }
[Required]
public DateTime StartTime { get; set; }
[Required]
public DateTime EndTime { get; set; }
public virtual IList<Tag> Tags { get; set; }
}
public class AgendaModule : Module
{
public IList<AgendaAnswer> AgendaAnswers { get; set; }
}
public class SolutionModule : Module
{
public IList<SolutionAnswer> SolutionAnswers { get; set; }
}
public class User
{
public int UserId { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
public int Zip { get; set; }
public bool Active { get; set; }
public virtual IList<AgendaAnswer> AgendaAnswers { get; set; }
public virtual IList<SolutionAnswer> SolutionAnswers { get; set; }
}
And this is the content of my DbContext class:
public DbSet<AgendaModule> AgendaModules { get; set; }
public DbSet<SolutionModule> SolutionModules { get; set; }
public DbSet<AgendaAnswer> AgendaAnswers { get; set; }
public DbSet<SolutionAnswer> SolutionAnswers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Entity<Module>().HasKey(m => m.ModuleId);
modelBuilder.Entity<Answer>().HasKey(a => a.AnswerId);
modelBuilder.Entity<AgendaAnswer>().Map(m =>
{
m.ToTable("AgendaAnswers");
});
modelBuilder.Entity<SolutionAnswer>().Map(m =>
{
m.ToTable("SolutionAnswers");
});
}
When I run my application, EF creates the tables how I want them (TPT), but it duplicates the foreign key to users in each of them (see picture).
Thanks in advance
As noted in my comment above, the solution is to remove the AgendaAnswers and SolutionAnswers properties from the User class.
If you want to keep those collections in the User class, you might have to remove the User and UserId properties from the Answer class and instead duplicate them in the AgendaAnswer and SolutionAnswer classes. See this SO question for more information.

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

Categories

Resources