EF Core dont full reference between models - c#

My entities classes that are used in the project:
public class Game
{
public int Id { get; set; }
public int FirstTeamId { get; set; }
public Team FirstTeam { get; set; }
public int SecondTeamId { get; set; }
public Team SecondTeam { get; set; }
public Stadium Stadium { get; set; }
public DateTime Date { get; set; }
public GameStatus Result { get; set; }
public Game(DateTime date , GameStatus result )
{
Date = date;
Result = result;
}
}
public class Player
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public DateTime Birthday { get; set; }
public PlayerStatus Status { get; set; }
public PlayerHealthStatus HealthStatus { get; set; }
public int Salary { get; set; }
public int TeamId { get; set; }
public Team Team { get; set; }
public Player(string name , string surname, DateTime birthday, PlayerStatus status, PlayerHealthStatus healthStatus, int salary)
{
Name = name;
Surname = surname;
Birthday = birthday;
Status = status;
HealthStatus = healthStatus;
Salary = salary;
}
}
public class Stadium
{
public int Id { get; set; }
public string Name { get; set; }
public int Capacity { get; set; }
public int PriceForPlace { get; set; }
public Stadium(string name, int capacity, int priceForPlace)
{
Name = name;
Capacity = capacity;
PriceForPlace = priceForPlace;
}
}
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
public List<Player> Players { get; set; }
public List<Game> Games { get; set; }
public Team(string name)
{
Name = name;
}
public Team(string name, List<Player> players) : this(name)
{
Players = players;
}
}
My EF Core context class:
public class ApplicationContext : DbContext
{
public DbSet<Player> Players { get; set; }
public DbSet<Game> Games { get; set; }
public DbSet<Team> Teams { get; set; }
public DbSet<Stadium> Stadiums { get; set; }
public ApplicationContext()
{
Database.EnsureCreated();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=best-komp;Database=FootballApplicationDataBase;Trusted_Connection=True;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Player>()
.HasOne(p => p.Team)
.WithMany(t => t.Players)
.HasForeignKey(p => p.TeamId).OnDelete(DeleteBehavior.NoAction)
.HasPrincipalKey(t => t.Id);
modelBuilder.Entity<Team>()
.HasMany(t => t.Players)
.WithOne(p => p.Team)
.HasForeignKey(p => p.Id).OnDelete(DeleteBehavior.NoAction)
.HasPrincipalKey(t => t.Id);
modelBuilder.Entity<Game>()
.HasOne(g => g.HomeTeam)
.WithMany(t => t.HomeGames)
.HasForeignKey(t => t.HomeTeamId)
.HasPrincipalKey(t => t.Id);
modelBuilder.Entity<Game>()
.HasOne(g => g.AwayTeam)
.WithMany(t => t.AwayGames)
.HasForeignKey(t => t.AwayTeamId).OnDelete(DeleteBehavior.NoAction)
.HasPrincipalKey(t => t.Id);
}
}
But when I try to call my context class, like
var db = new ApplicationContext();
I can see that my team and player classes dont refer to each other.
All other classes have refer to team, like player have a team, game have a team. But my team classes dont have refer to any class, they have NULL instead a reference.
What is the problem?

Your Team configuration is incorrect. The foreign key should be TeamId not Id. As the FK on Players entity is TeamId.
modelBuilder.Entity<Team>()
.HasMany(t => t.Players)
.WithOne(p => p.Team)
.HasForeignKey(p => p.TeamId).OnDelete(DeleteBehavior.NoAction)
.HasPrincipalKey(t => t.Id);

Maybe try using ForeignKey annotations. First, add using statement:
using System.ComponentModel.DataAnnotations.Schema;
Then add a [ForeignKey("ModelName")] annotation above each foreign key. For example:
[ForeignKey("FirstTeam")]
public int FirstTeamId { get; set; }
public Team FirstTeam { get; set; }
[ForeignKey("SecondTeam")]
public int SecondTeamId { get; set; }
public Team SecondTeam { get; set; }
This makes foreign key relationships explicit that EF sometimes can't infer.

Related

EF models. Navigation properties can only participate in a single relationship

I have my entities like this, they are closely linked.
public class Game
{
public int Id { get; set; }
public int FirstTeamId { get; set; }
public Team FirstTeam { get; set; }
public int SecondTeamId { get; set; }
public Team SecondTeam { get; set; }
public Stadium Stadium { get; set; }
public DateTime Date { get; set; }
public GameStatus Result { get; set; }
public Game(DateTime date , GameStatus result )
{
Date = date;
Result = result;
}
}
public class Player
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public DateTime Birthday { get; set; }
public PlayerStatus Status { get; set; }
public PlayerHealthStatus HealthStatus { get; set; }
public int Salary { get; set; }
public int TeamId { get; set; }
public Team Team { get; set; }
public Player(string name , string surname, DateTime birthday, PlayerStatus status, PlayerHealthStatus healthStatus, int salary)
{
Name = name;
Surname = surname;
Birthday = birthday;
Status = status;
HealthStatus = healthStatus;
Salary = salary;
}
}
public class Stadium
{
public int Id { get; set; }
public string Name { get; set; }
public int Capacity { get; set; }
public int PriceForPlace { get; set; }
public Stadium(string name, int capacity, int priceForPlace)
{
Name = name;
Capacity = capacity;
PriceForPlace = priceForPlace;
}
}
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
public List<Player> Players { get; set; }
public List<Game> Games { get; set; }
public Team(string name)
{
Name = name;
}
public Team(string name, List<Player> players) : this(name)
{
Players = players;
}
}
In my Context class I'm tried to describe my relationships between classes. But something isn't correct.
public class ApplicationContext : DbContext
{
public DbSet<Player> Players { get; set; }
public DbSet<Game> Games { get; set; }
public DbSet<Team> Teams { get; set; }
public DbSet<Stadium> Stadiums { get; set; }
public ApplicationContext()
{
Database.EnsureCreated();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=best-komp;Database=FootballApplicationDataBase;Trusted_Connection=True;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Player>()
.HasOne(p => p.Team)
.WithMany(t => t.Players)
.HasForeignKey(p => p.TeamId)
.HasPrincipalKey(t => t.Id);
modelBuilder.Entity<Team>()
.HasMany(p => p.Players)
.WithOne(p => p.Team)
.HasForeignKey(p => p.TeamId)
.HasPrincipalKey(t => t.Id);
modelBuilder.Entity<Game>()
.HasOne(g => g.FirstTeam)
.WithMany(t => t.Games)
.HasForeignKey(t => t.FirstTeamId)
.HasPrincipalKey(t => t.Id);
modelBuilder.Entity<Game>()
.HasOne(g => g.SecondTeam)
.WithMany(t => t.Games)
.HasForeignKey(t => t.SecondTeamId)
.HasPrincipalKey(t => t.Id);
}
}
What wrong with this code? Because I have "Navigation properties can only participate in a single relationship." error when I try to do something with my ApplicationContext.
You can't reuse Team.Games as the inverse property for both Game.FirstTeam and Team.SecondTeam. Think of it, if you add game to Team.Games, how would EF know which team it is, first or second?
You need two collections to describe the relationships. And that's also a chance to add some more meaning to the class model. For example (only modified code):
public class Game
{
...
public int HomeTeamId { get; set; }
public Team HomeTeam { get; set; }
public int AwayTeamId { get; set; }
public Team AwayTeam { get; set; }
}
public class Team
{
...
public List<Game> HomeGames { get; set; }
public List<Game> AwayGames { get; set; }
}
For a team it's meaningful to make a distinction between home and away games, for example to compare results in both types of games.
And the mapping:
modelBuilder.Entity<Game>()
.HasOne(g => g.HomeTeam)
.WithMany(t => t.HomeGames)
.HasForeignKey(t => t.HomeTeamId)
.HasPrincipalKey(t => t.Id);
modelBuilder.Entity<Game>()
.HasOne(g => g.AwayTeam)
.WithMany(t => t.AwayGames)
.HasForeignKey(t => t.AwayTeamId).OnDelete(DeleteBehavior.NoAction)
.HasPrincipalKey(t => t.Id);
If using Sql Server, this delete behavior instruction is necessary to prevent disallowed multiple cascade paths.
The problem is that your Team model has 2 one-to-many relationships with your Game model but you only have one navigation property on the Team.
You need to have 2 navigation properties on the Team model, one for each relationship.
(Game1, Game2...).
You will also need to define these relationships in the Game model - a Team property for each relationship.
Check this answer for extra info.

EF-Model for many-to-many Junction Table

I'm trying to straighten out the EF-Model for a junctiontable OwnerCows.dbo.
There's a class Cow with an Id, a class Owner with an Id and i want to reference them both in a OwnerCows-table that has only an OwnerCowId, a CowId(FK) and a OwnerId(FK).
The error I'm getting is:
Cannot create a relationship between 'Owner.OwnerCows' and 'OwnerCow.Owner', because there already is a relationship between 'Owner.CowOwners' and 'OwnerCow.Owner'. Navigation properties can only participate in a single relationship.
Does it mean I have a circular reference? How can I solve this?
the Owner.cs:
public class Owner : EntityBase<Guid>
{
public string Name { get; set; }
[NotMapped]
public ICollection<Cow> Cows { get; set; }
= new List<Cow>();
public virtual List<OwnerCow> CowOwners { get; set; }
public Cow Cow { get; set; }
}
the Cow.cs:
public class Cow : EntityBase<Guid>
{
[MaxLength(50)]
public string Name { get; set; }
public string Breed { get; set; }
public string Color { get; set; }
public ICollection<Entities.Weight> Weights { get; set; }
= new List<Weight>();
public ICollection<Vaccination> Vaccinations { get; set; }
= new List<Vaccination>();
[NotMapped]
public ICollection<Owner> CowOwners { get; set; }
= new List<Owner>();
public List<OwnerCow> OwnerCows { get; set; }
}
the OwnerCows.cs:
public class OwnerCow
{
public Guid OwnerCowId { get; set; }
public Cow Cow { get; set; }
public Guid CowId { get; set; }
public Owner Owner { get; set; }
public Guid OwnerId { get; set; }
}
the Context-class:
public class DogFaceContext : DbContext
{
public DogFaceContext()
{
}
public DogFaceContext(DbContextOptions<DogFaceContext> options)
: base(options)
{
Database.Migrate();
}
//Entity Tables
public virtual DbSet<Owner> Owners { get; set; }
public virtual DbSet<Cow> Cows { get; set; }
public virtual DbSet<Vaccination> Vaccination { get; set; }
public virtual DbSet<Weight> Weight { get; set; }
//Junction Tables
public virtual DbSet<OwnerCow> OwnerCows { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Cow>().HasMany(x => x.CowOwners).WithOne(x => x.Cow);
builder.Entity<Owner>().HasMany(u => u.CowOwners).WithOne(X => X.Owner);
builder.Entity("DogFace.API.Entities.OwnerCow", b =>
{
b.HasOne("DogFace.API.Entities.Cow", "Cow")
.WithMany("OwnerCows")
.HasForeignKey("CowId")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("DogFace.API.Entities.Owner", "Owner")
.WithMany("OwnerCows")
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Restrict);
});
}
}
Can I get it to work with this design? Is it possible with EFCore? Any other suggestions? Thanks!
You model is very complex and has some unnecessary relationships like Owner.Cows since you decide to configure many-to-many relationship.You could just get Owner's cows using
var owner = new Owner();
List<Cow> cows = owner.OwnerCows.Where(oc => oc.OwnerId == owner.Id)
.Select(oc => oc.Cow)
.ToList();
1.To have OwnerCowId, a CowId(FK) and a OwnerId(FK) in OwnerCows, refer to my below configuration:
public class Owner : EntityBase<Guid>
{
public string Name { get; set; }
public virtual List<OwnerCow> OwnerCows { get; set; }
}
public class Cow : EntityBase<Guid>
{
[MaxLength(50)]
public string Name { get; set; }
public string Breed { get; set; }
public string Color { get; set; }
public ICollection<Entities.Weight> Weights { get; set; } = new List<Weight>();
public ICollection<Vaccination> Vaccinations { get; set; }= new List<Vaccination>();
public List<OwnerCow> OwnerCows { get; set; }
}
public class OwnerCow
{
[Key]
public Guid OwnerCowId { get; set; }
public Cow Cow { get; set; }
public Guid CowId { get; set; }
public Owner Owner { get; set; }
public Guid OwnerId { get; set; }
}
DbContext:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<OwnerCow>()
.HasOne(oc => oc.Cow)
.WithMany(c => c.OwnerCows)
.HasForeignKey(oc => oc.CowId);
builder.Entity<OwnerCow>()
.HasOne(oc => oc.Owner)
.WithMany(o => o.OwnerCows)
.HasForeignKey(oc => oc.OwnerId);
}
}
In this case, your OwnerCowId id the primary key for your OwnerCows table which is not reasonable and it may have the same record of CowId,OwnerId for OwnerCows.
2.Usually,the primary key for the join table is a composite key comprising both of the foreign key values,I suggest that you could use composite key for your OwnerCow:
public class OwnerCow
{
public Cow Cow { get; set; }
public Guid CowId { get; set; }
public Owner Owner { get; set; }
public Guid OwnerId { get; set; }
}
DbContext:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<OwnerCow>()
.HasKey(oc => new { oc.OwnerId, oc.CowId });
builder.Entity<OwnerCow>()
.HasOne(oc => oc.Cow)
.WithMany(c => c.OwnerCows)
.HasForeignKey(oc => oc.CowId);
builder.Entity<OwnerCow>()
.HasOne(oc => oc.Owner)
.WithMany(o => o.OwnerCows)
.HasForeignKey(oc => oc.OwnerId);
}
}
Refer to https://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration
Fix context builder:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Cow>().HasMany(x => x.CowOwners).WithOne(x => x.Cow);
builder.Entity<Owner>().HasMany(u => u.Cows).WithOne(X => X.Owner); // Cows instead of CowOwners
builder.Entity("DogFace.API.Entities.OwnerCow", b =>
{
b.HasOne("DogFace.API.Entities.Cow", "Cow")
.WithMany("OwnerCows")
.HasForeignKey("CowId")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("DogFace.API.Entities.Owner", "Owner")
.WithMany("CowOwners") // CowOwners instead of OwnerCows
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Restrict);
});
}
... or fix property names in classes:
public class Owner : EntityBase<Guid>
{
public string Name { get; set; }
[NotMapped]
public ICollection<Cow> CowOwners { get; set; } // CowOwners instead of Cows ?
= new List<Cow>();
public virtual List<OwnerCow> OwnerCow { get; set; } // OwnerCow instead of CowOwners ?
public Cow Cow { get; set; }
}
but don't forget to change dbcontext builder with fixed property names.

Cannot create the foreign key "FK_ApplicationInfos_UserProfiles_UserProfileId" with the SET NULL referential action

How fix the problem ?
Error Message
In Entity Framework Core, I'm attempting to create a system with 4 Db Models - User,UserProfile,Review, ApplicationInfo.
I tried a lot of things, well, I don’t understand the concept of how to fix the situation, I clearly gave cascade in modelOnCreate
Model User.
public class User
{
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string Token { get; set; }
public UserProfile UserProfile { get; set; }
}
Model UserProfile
public class UserProfile
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Phone { get; set; }
public int UserId { get; set; }
public User User { get; set; }
public IEnumerable<ApplicationInfo> ApplicationInfos { get; set; }
public IEnumerable<Review> Reviews { get; set; }
}
Model Review
public class Review
{
public int Id { get; set; }
public string Text { get; set; }
public string ImageLocation { get; set; }
public string Version { get; set; }
public string Rate { get; set; }
//One(AppInfo) to Many(Review)
public int ApplicationInfoId { get; set; }
public ApplicationInfo ApplicationInfo { get; set; }
//One(UserProfile) to Many(Review)
public int UserProfileId { get; set; }
[Required]
public UserProfile UserProfile { get; set; }
}
Model ApplicationInfo
public class ApplicationInfo
{
[Key]
public int Id { get; set; }
[Required]
public string ApplicationId { get; set; }
[Required]
public string AppName { get; set; }
[Required]
public string PublisherEmail { get; set; }
public string Genre { get; set; }
public string Price { get; set; }
public string Description { get; set; }
public string Version { get; set; }
public string IconUrl { get; set; }
public string PublisherName { get; set; }
public string AllRatingCount { get; set; }
public string AllRating { get; set; }
//One(UserProfile) To Many(AppInfo)
public int UserProfileId { get; set; }
public UserProfile UserProfile { get; set; }
public IEnumerable<Review> Reviews { get; set; }
}
OnModelCreating
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().
HasOne<UserProfile>(s=>s.UserProfile).
WithOne(s=>s.User).
HasForeignKey<UserProfile>(s=>s.UserId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<ApplicationInfo>()
.HasOne<UserProfile>(s => s.UserProfile)
.WithMany(s => s.ApplicationInfos)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<Review>()
.HasOne<UserProfile>(s => s.UserProfile)
.WithMany(s => s.Reviews)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<Review>()
.HasOne<ApplicationInfo>(s => s.ApplicationInfo)
.WithMany(s => s.Reviews)
.OnDelete(DeleteBehavior.Cascade);
}
Just Change OnModelCreating and change OnDelete for Review and AppInfo to Restrict(No Action)
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasOne<UserProfile>(s => s.Profile)
.WithOne(s => s.User)
.HasForeignKey<UserProfile>(s=>s.UserId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<ApplicationInfo>()
.HasOne<UserProfile>(s => s.UserProfile)
.WithMany(s => s.ApplicationInfos)
.HasForeignKey(s => s.UserProfileId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<Review>()
.HasOne<UserProfile>(s => s.UserProfile)
.WithMany(s => s.Reviews)
.HasForeignKey(s => s.UserProfileId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Review>()
.HasOne<ApplicationInfo>(s => s.ApplicationInfo)
.WithMany(s => s.Reviews)
.HasForeignKey(s => s.ApplicationInfoId)
.OnDelete(DeleteBehavior.Restrict);
base.OnModelCreating(modelBuilder);
}

Entity Framework Core with Multiple Foreign Key On Same Column

I have 3 tables with relationship to the same TransactionLog.DocumentId column.
I differentiate the Foreign Key with DocumentTypeId:
1 - Invoice,
2 - DebitNote,
3 - CreditNote
I scaffold the entities:
public partial class TransactionLog
{
public int TransactionLogId { get; set; }
public int? DocumentId { get; set; }
public int? DocumentTypeId { get; set; }
public decimal? Amount { get; set; }
public CreditNote CreditNote { get; set; }
public Invoice Invoice { get; set; }
public DebitNote DebitNote { get; set; }
}
public partial class Invoice
{
public Invoice()
{
TransactionLog = new HashSet<TransactionLog>();
}
public int InvoiceId { get; set; }
public string InvoiceNumber { get; set; }
public decimal Amount { get; set; }
public ICollection<TransactionLog> TransactionLog { get; set; }
}
public partial class DebitNote
{
public DebitNote()
{
TransactionLog = new HashSet<TransactionLog>();
}
public int DebitNoteId { get; set; }
public string DebitNoteNumber { get; set; }
public decimal Amount { get; set; }
public ICollection<TransactionLog> TransactionLog { get; set; }
}
public partial class CreditNote
{
public CreditNote()
{
TransactionLog = new HashSet<TransactionLog>();
}
public int CreditNoteId { get; set; }
public string CreditNoteNumber { get; set; }
public decimal Amount { get; set; }
public ICollection<TransactionLog> TransactionLog { get; set; }
}
I want to insert 1 record to each of the table Invoice, DebitNote and CreditNote, and 3 records for each transaction to TransactionLog.
And this is my code:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<CreditNote>(entity =>
{
entity.Property(e => e.Amount).HasColumnType("decimal(18, 4)");
entity.Property(e => e.CreditNoteNumber)
.HasMaxLength(50)
.IsUnicode(false);
});
modelBuilder.Entity<DebitNote>(entity =>
{
entity.Property(e => e.Amount).HasColumnType("decimal(18, 4)");
entity.Property(e => e.DebitNoteNumber)
.HasMaxLength(50)
.IsUnicode(false);
});
modelBuilder.Entity<Invoice>(entity =>
{
entity.Property(e => e.Amount).HasColumnType("decimal(18, 4)");
entity.Property(e => e.InvoiceNumber)
.HasMaxLength(50)
.IsUnicode(false);
});
modelBuilder.Entity<TransactionLog>(entity =>
{
entity.Property(e => e.Amount).HasColumnType("decimal(18, 4)");
entity.HasOne(d => d.CreditNote)
.WithMany(p => p.TransactionLog)
.HasForeignKey(d => d.DocumentId)
.HasConstraintName("FK_TransactionLog_CreditNote");
entity.HasOne(d => d.DebitNote)
.WithMany(p => p.TransactionLog)
.HasForeignKey(d => d.DocumentId)
.HasConstraintName("FK_TransactionLog_DebitNote");
entity.HasOne(d => d.Invoice)
.WithMany(p => p.TransactionLog)
.HasForeignKey(d => d.DocumentId)
.HasConstraintName("FK_TransactionLog_Invoice");
});
}
However, the DocumentId doesn't save the correct InvoiceId, CreditNoteId, DebitNoteId. I check with SQL Profiler, it will always get the first scope_identity() of the 3 inserts, which in my case is CreditNoteid.
Any idea how to get the correct Id from Invoice, CreditNote and DebitNote ?
Or shouldn't I use relationship in this case.
If not, what is the best practice of logging the transaction into the log?
Add the following configurations in your DbContext and then add a migration and update the database accordingly.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Invoice>().HasMany(i => i.TransactionLog).WithOne(tl => tl.Invoice).HasForeignKey(tl => tl.DocumentId);
modelBuilder.Entity<DebitNote>().HasMany(dn => dn.TransactionLog).WithOne(tl => tl.DebitNote).HasForeignKey(tl => tl.DocumentId);
modelBuilder.Entity<CreditNote>().HasMany(cn => cn.TransactionLog).WithOne(tl => tl.CreditNote).HasForeignKey(tl => tl.DocumentId);
}
I think maybe the Foreign Key's direction is wrong.
As your table definition, the TransactionLog.DocumentId's value must be exist in the three tables (Invoice CreditNote DebitNote) at the same time. So, if only insert two of them, there will maybe have a Exception.
I think you really want table definition is like this.
I delete TransactionLog.DocumentId column and add FKs from the PK of the three tables to TransactionLog.TransactionLogId.
the entities:
public partial class TransactionLog
{
public int TransactionLogId { get; set; }
public Nullable<int> DocumentTypeId { get; set; }
public Nullable<decimal> Amount { get; set; }
public virtual CreditNote CreditNote { get; set; }
public virtual DebitNote DebitNote { get; set; }
public virtual Invoice Invoice { get; set; }
}
public partial class Invoice
{
public int InvoiceId { get; set; }
public string InvoiceNumber { get; set; }
public decimal Amount { get; set; }
public virtual TransactionLog TransactionLog { get; set; }
}
public partial class CreditNote
{
public int CreditNoteId { get; set; }
public string CreditNoteNumber { get; set; }
public decimal Amount { get; set; }
public virtual TransactionLog TransactionLog { get; set; }
}
public partial class DebitNote
{
public int DebitNoteId { get; set; }
public string DebitNoteNumber { get; set; }
public decimal Amount { get; set; }
public virtual TransactionLog TransactionLog { get; set; }
}
code:
Invoice invoice = new Invoice() { InvoiceNumber = "Inv0100", Amount = 66m };
TransactionLog invoiceLog = new TransactionLog() { Amount = invoice.Amount, DocumentTypeId = 1 };
invoice.TransactionLog = invoiceLog;
_context.Invoices.Add(invoice);
CreditNote creditNote = new CreditNote() { CreditNoteNumber = "DN003", Amount = 99.99m };
TransactionLog creditNoteLog = new TransactionLog() { Amount = creditNote.Amount, DocumentTypeId = 2 };
creditNote.TransactionLog = creditNoteLog;
_context.CreditNotes.Add(creditNote);
DebitNote debitNote = new DebitNote() { DebitNoteNumber = "CN009", Amount = 77.77m };
TransactionLog debitNoteLog = new TransactionLog() { Amount = debitNote.Amount, DocumentTypeId = 3 };
debitNote.TransactionLog = debitNoteLog;
_context.DebitNotes.Add(debitNote);
I guess you have a wrong relationship there. like one transaction can have multiple invoices but an invoice will have only one transaction record. I might be wrong because sometimes it feels like one-to-one but anyways I gave it a try and here is what you want.
the entities:
public class TestMVCEntities : DbContext
{
public TestMVCEntities()
: base("name=TestMVCEntities")
{
}
public DbSet<Invoice> Invoices { get; set; }
public DbSet<DebitNote> DebitNotes { get; set; }
public DbSet<CreditNote> CreditNotes { get; set; }
public DbSet<TransactionLog> TransactionLogs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<TransactionLog>()
.HasRequired(p => p.Invoice)
.WithMany(p => p.InvoiceLog)
.HasForeignKey(p => p.DocumentId);
modelBuilder.Entity<TransactionLog>()
.HasRequired(p => p.DebitNote)
.WithMany(p => p.DebitLog)
.HasForeignKey(p => p.DocumentId);
modelBuilder.Entity<TransactionLog>()
.HasRequired(p => p.CreditNote)
.WithMany(p => p.CreditLog)
.HasForeignKey(p => p.DocumentId);
}
}
public partial class TransactionLog
{
public int TransactionLogId { get; set; }
public int? DocumentId { get; set; }
public int? DocumentTypeId { get; set; }
public decimal? Amount { get; set; }
public CreditNote CreditNote { get; set; }
public Invoice Invoice { get; set; }
public DebitNote DebitNote { get; set; }
}
public partial class Invoice
{
public int InvoiceId { get; set; }
public string InvoiceNumber { get; set; }
public decimal Amount { get; set; }
public ICollection<TransactionLog> InvoiceLog { get; set; }
}
public partial class DebitNote
{
public int DebitNoteId { get; set; }
public string DebitNoteNumber { get; set; }
public decimal Amount { get; set; }
public ICollection<TransactionLog> DebitLog { get; set; }
}
public partial class CreditNote
{
public int CreditNoteId { get; set; }
public string CreditNoteNumber { get; set; }
public decimal Amount { get; set; }
public ICollection<TransactionLog> CreditLog { get; set; }
}
and to insert data:
var invoice = new Invoice()
{
InvoiceNumber = "Inv099",
Amount = 66m,
InvoiceLog = new Collection<TransactionLog>()
{
new TransactionLog(){DocumentTypeId = 1, Amount = 66m}
}
};
var creditNote = new CreditNote()
{
CreditNoteNumber = "DN002",
Amount = 99.99m,
CreditLog = new Collection<TransactionLog>()
{
new TransactionLog(){DocumentTypeId = 3, Amount = 99.99m}
}
};
var debitNote = new DebitNote()
{
DebitNoteNumber = "CN008",
Amount = 77.77m,
DebitLog = new Collection<TransactionLog>()
{
new TransactionLog(){DocumentTypeId = 2, Amount = 77.77m}
}
};
using (var context = new TestMVCEntities())
{
context.Invoices.Add(invoice);
context.CreditNotes.Add(creditNote);
context.DebitNotes.Add(debitNote);
context.SaveChanges();
}
and the table will look like this:

Entity Framework : Lazy Loading Not working

I am getting all the related tables populated (multiple times /circular/loop) even for a simple include of 2 tables query..
Here are my DB details.
public interface IPhaniDbContext
{
DbSet<Data_CustomerDetail> CustomerDetails { get; set; }
DbSet<Data_DisplayStyle> DisplayStyles { get; set; }
DbSet<Data_ResumeDetail> ResumeDetails { get; set; }
int Save();
Task<int> SaveChangesAsync();
Database Database { get; }
DbEntityEntry Entity(object entity);
DbSet<TEntity> Set<TEntity>() where TEntity : class;
}
public class PhaniDbContext :DbContext , IPhaniDbContext
{
public PhaniDbContext() : base(ConnectionString())
{
Configuration.LazyLoadingEnabled = false;
}
private static string ConnectionString()
{
return ConfigurationManager.AppSettings[Constants.Appsetting_PhaniDbConnectionString];
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.AddFromAssembly(Assembly.GetAssembly(GetType()));
modelBuilder.Entity<Data_CustomerDetail>()
.HasMany(x => x.ResumeDetails).WithRequired(e => e.CustomerDetail).WillCascadeOnDelete(false);
//modelBuilder.Entity<Data_CustomerDetail>()
// .Map(x => x.Requires("ResumeDetails")).Ignore(e => e.ResumeDetails)
// .Map(x => x.Requires("DisplayStyle")).Ignore(e => e.ResumeDetails);
modelBuilder.Entity<Data_DisplayStyle>()
.HasMany(x => x.ResumeDetails).WithRequired(e => e.DisplayStyle).WillCascadeOnDelete(false);
}
public int Save()
{
return base.SaveChanges();
}
public DbEntityEntry Entity(object entity)
{
throw new NotImplementedException();
}
//TODO: No-Urget-Over ride Save just to know. and all other possible overrides.
public virtual DbSet<Data_CustomerDetail> CustomerDetails { get; set; }
public virtual DbSet<Data_DisplayStyle> DisplayStyles { get; set; }
public virtual DbSet<Data_ResumeDetail> ResumeDetails { get; set; }
}
Query:
_phaniDbContext.CustomerDetails
.Include(x => x.ResumeDetails)
.Include(x=>x.ResumeDetails.Select(y=>y.DisplayStyle))
.First(x=>x.CustomerDetailsID == customerId);
My pocos are here:
[Table("CustomerDetails")]
public class Data_CustomerDetail
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Data_CustomerDetail()
{
ResumeDetails = new HashSet<Data_ResumeDetail>();
}
[Key]
public int CustomerDetailsID { get; set; }
[StringLength(500)]
public string FirstName { get; set; }
[StringLength(500)]
public string LastName { get; set; }
[StringLength(50)]
public string phoneNumber { get; set; }
[StringLength(50)]
public string Zipcode { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Data_ResumeDetail> ResumeDetails { get; set; }
}
public class Data_DisplayStyle
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Data_DisplayStyle()
{
ResumeDetails = new HashSet<Data_ResumeDetail>();
}
public int DisplayStyleID { get; set; }
[StringLength(500)]
public string Name { get; set; }
[StringLength(500)]
public string Description { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Data_ResumeDetail> ResumeDetails { get; set; }
}
[Table("ResumeDetails")]
public class Data_ResumeDetail
{
[Key]
public int ResumeDetailsId { get; set; }
[Column("CAREER OBJECTIVE")]
[StringLength(4000)]
public string CAREER_OBJECTIVE { get; set; }
[Column("HONORS AND REWARDS")]
[StringLength(4000)]
public string HONORS_AND_REWARDS { get; set; }
[Column("PROFESSIONAL RESPONSIBILITIES")]
[StringLength(4000)]
public string PROFESSIONAL_RESPONSIBILITIES { get; set; }
[Column("RELATED EXPERIENCE")]
[StringLength(4000)]
public string RELATED_EXPERIENCE { get; set; }
public int? CustomerDetailsID { get; set; }
public int? DisplayStyleId { get; set; }
public virtual Data_CustomerDetail CustomerDetail { get; set; }
public virtual Data_DisplayStyle DisplayStyle { get; set; }
}
Could someone please help me in resolving this...
I just want CustomerDetails and ResumeDetails and displaystyle table once... not multiple times..
This is increasing my object weight
When doing a foreign key relationship, you probably need to specify the foreign key(s)
Example:
modelBuilder
.Entity<Data_CustomerDetail>()
.HasMany(x => x.ResumeDetails)
.WithRequired(e => e.CustomerDetail)
.HasForeignKey(e => e.CustomerDetailsID)
.WillCascadeOnDelete(false);
modelBuilder
.Entity<Data_DisplayStyle>()
.HasMany(x => x.ResumeDetails)
.WithRequired(e => e.DisplayStyle)
.HasForeignKey(e => e.DisplayStyleId)
.WillCascadeOnDelete(false);

Categories

Resources