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.
Related
How to add multiple many-to-many relationships between Entities in EF Core 5.x, with join entity (linking tables)?
What I what is two many-to-many relationships between Card and Game. One relationship is not a problem but two (or more) I'm not able to configure it correctly.
What's working at the moment for me is that I created two different classes DeckGameCard and TableGameCard (join entity), which forces EF to create two tables. The classes are the same except for the name. Is it possible to have only one class (join entity) and two linking tables in the database?
What I want is this:
public class Game
{
[Key]
public int Id { get; set; }
public ICollection<GameCard> Deck { get; set; }
public ICollection<GameCard> OnTable { get; set; }
...
But at the moment this (the code below) is my solution (not optimal, because of code duplication in DeckGameCard and TableGameCard).
public class DeckGameCard
{
public int GameId { get; set; }
public Game Game { get; set; }
public int Order { get; set; }
public int CardId { get; set; }
public Card Card { get; set; }
}
public class TableGameCard
{
public int GameId { get; set; }
public Game Game { get; set; }
public int Order { get; set; }
public int CardId { get; set; }
public Card Card { get; set; }
}
public class Game
{
[Key]
public int Id { get; set; }
public ICollection<DeckGameCard> Deck { get; set; }
public ICollection<TableGameCard> OnTable { get; set; }
[Required]
public int CardIndex { get; set; }
[Required]
public int PlayerId { get; set; }
[Required]
public Player Player { get; set; }
}
public class Card : IEntity
{
[Key]
public int Id { get; set; }
[Required]
public Shape Shape { get; set; }
[Required]
public Fill Fill { get; set; }
[Required]
public Color Color { get; set; }
[Required]
public int NrOfShapes { get; set; }
public ICollection<DeckGameCard> Deck { get; set; }
public ICollection<TableGameCard> OnTable { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<DeckGameCard>()
.HasKey(x => new {x.GameId, x.CardId});
modelBuilder.Entity<DeckGameCard>()
.HasOne(gc => gc.Card)
.WithMany(b => b.Deck)
.HasForeignKey(gc => gc.CardId);
modelBuilder.Entity<DeckGameCard>()
.HasOne(gc => gc.Game)
.WithMany(g => g.Deck)
.HasForeignKey(gc => gc.GameId);
modelBuilder.Entity<TableGameCard>()
.HasKey(x => new {x.GameId, x.CardId});
modelBuilder.Entity<TableGameCard>()
.HasOne(gc => gc.Card)
.WithMany(b => b.OnTable)
.HasForeignKey(bc => bc.CardId);
modelBuilder.Entity<TableGameCard>()
.HasOne(gc => gc.Game)
.WithMany(g => g.OnTable)
.HasForeignKey(gc => gc.GameId);
}
Yes, it's possible by using the EF Core 5.0 introduced Shared-type entity types, but not sure it's worth since the type of an object no more uniquely identifies the entity type, so most if not all generic and non generic entity services (methods) of DbContext won't work, and you have to use the corresponding DbSet<T> methods, obtaining it using the Set<T>(name) overload. And there is no equivalent of Entry method, so you might have problems with change tracking in disconnected scenarios.
With that being said, here is how it can be done at model level.
Given the model similar to:
public class Game
{
public int Id { get; set; }
// ...
public ICollection<GameCard> Deck { get; set; }
public ICollection<GameCard> OnTable { get; set; }
}
public class Card
{
public int Id { get; set; }
// ...
public ICollection<GameCard> Deck { get; set; }
public ICollection<GameCard> OnTable { get; set; }
}
public class GameCard
{
public int GameId { get; set; }
public Game Game { get; set; }
public int Order { get; set; }
public int CardId { get; set; }
public Card Card { get; set; }
}
it can be configured as follows:
modelBuilder.SharedTypeEntity<GameCard>("DeckGameCard", builder =>
{
builder.ToTable("DeckGameCard");
builder.HasKey(e => new { e.GameId, e.CardId });
builder.HasOne(e => e.Card).WithMany(e => e.Deck);
builder.HasOne(e => e.Game).WithMany(e => e.Deck);
});
modelBuilder.SharedTypeEntity<GameCard>("TableGameCard", builder =>
{
builder.ToTable("TableGameCard");
builder.HasKey(e => new { e.GameId, e.CardId });
builder.HasOne(e => e.Card).WithMany(e => e.OnTable);
builder.HasOne(e => e.Game).WithMany(e => e.OnTable);
});
or since the only difference is the entity (table) name and the corresponding collection navigation properties, the configuration can be factored out to a method similar to
static void GameCardEntity(
ModelBuilder modelBuilder, string name,
Expression<Func<Card, IEnumerable<GameCard>>> cardCollection,
Expression<Func<Game, IEnumerable<GameCard>>> gameCollection,
string tableName = null
)
{
var builder = modelBuilder.SharedTypeEntity<GameCard>(name);
builder.ToTable(tableName ?? name);
builder.HasKey(e => new { e.GameId, e.CardId });
builder.HasOne(e => e.Card).WithMany(cardCollection);
builder.HasOne(e => e.Game).WithMany(gameCollection);
}
so the configuration becomes simply
GameCardEntity(modelBuilder, "DeckGameCard", c => c.Deck, g => g.Deck);
GameCardEntity(modelBuilder, "TableGameCard", c => c.OnTable, g => g.OnTable);
This should answer your concrete question. But again, make sure you understand the potential problems with it. And compare to "straight forward" solution achieving the same reusability with base class (not entity) without the above drawbacks, e.g.
public class Game
{
public int Id { get; set; }
// ...
public ICollection<DeskGameCard> Deck { get; set; }
public ICollection<TableGameCard> OnTable { get; set; }
}
public class Card
{
public int Id { get; set; }
// ...
public ICollection<DeskGameCard> Deck { get; set; }
public ICollection<TableGameCard> OnTable { get; set; }
}
public abstract class GameCard
{
public int GameId { get; set; }
public Game Game { get; set; }
public int Order { get; set; }
public int CardId { get; set; }
public Card Card { get; set; }
}
public class DeskGameCard : GameCard { }
public class TableGameCard : GameCard { }
with only fluent configuration needed for composite PK
modelBuilder.Entity<DeskGameCard>().HasKey(e => new { e.GameId, e.CardId });
modelBuilder.Entity<TableGameCard>().HasKey(e => new { e.GameId, e.CardId });
When I try to register a user on my .NET Core 2.1 website (using identity) I get the following error:
"InvalidOperationException: Unable to determine the relationship represented by navigation property 'City.ConnectionStartCity' of type 'ICollection'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.".
The reason this happens probably has nothing to do with identity, but registering and logging in is currently the only way I know how to trigger it.
I still want the properties City en ICollection<Connection> in my classes so I don't want to use the [NotMapped] attribute.
I searched on the internet and found that this is caused by a many-many relationship, I feel like this is not the case tho.
Class Connection:
public partial class Connection
{
public Connection()
{
ConnectionRoute = new HashSet<ConnectionRoute>();
}
public int Id { get; set; }
public int StartCityId { get; set; }
public int EndCityId { get; set; }
public int AantalMinuten { get; set; }
public double Prijs { get; set; }
public Stad StartCity { get; set; }
public Stad EndCity { get; set; }
public ICollection<ConnectionRoute> ConnectionRoute{ get; set; }
}
Class City:
public partial class City
{
public City()
{
AspNetUsers = new HashSet<AspNetUsers>();
Hotel = new HashSet<Hotel>();
ConnectionStartCity = new HashSet<Connection>();
ConnectionEndCity= new HashSet<Connection>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Country { get; set; }
public ICollection<AspNetUsers> AspNetUsers { get; set; }
public ICollection<Hotel> Hotel { get; set; }
public ICollection<Connection> ConnectionStartCity { get; set; }
public ICollection<Connection> ConnectionEndCity { get; set; }
}
Class treinrittenContext (dbContext) extract:
public virtual DbSet<City> City{ get; set; }
public virtual DbSet<Connection> Connection{ get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
...
modelBuilder.Entity<City>(entity =>
{
entity.Property(e => e.Country)
.IsRequired()
.HasMaxLength(255)
.IsUnicode(false);
entity.Property(e => e.Name)
.IsRequired()
.HasMaxLength(255)
.IsUnicode(false);
entity.HasMany(p => p.ConnectionStartcity)
.WithOne(d => d.StartCity)
.HasForeignKey(d => d.StartCityId);
entity.HasMany(p => p.ConnectionEndCity)
.WithOne(d => d.EndCity)
.HasForeignKey(d => d.EndCityId);
});
...
modelBuilder.Entity<Connection>(entity =>
{
entity.HasOne(d => d.StartCity)
.WithMany(p => p.ConnectionStartCity)
.HasForeignKey(d => d.StartCityId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_Verbinding_BeginStad");
entity.HasOne(d => d.EndCity)
.WithMany(p => p.ConnectionEndCity)
.HasForeignKey(d => d.EndCityId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_Verbinding_EindStad");
});
...
}
I expect this to work (since in my eyes it's a one-many relation), but it doesn't.
UPDATE
You have multiple options here:
Option 1 with result 1
City Class becomes:
public partial class City
{
public City()
{
Connections = new HashSet<Connection>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Country { get; set; }
public ICollection<Connection> Connections { get; set; }
}
Connection Class becomes:
public partial class Connection
{
public Connection()
{
}
public int Id { get; set; }
public int StartCityId { get; set; }
public int EndCityId { get; set; }
public int AantalMinuten { get; set; }
public double Prijs { get; set; }
}
Your OnModelCreating becomes:
modelBuilder.Entity<City>().HasMany(city => city.Connections)
.WithRequired().HasForeignKey(con => con.EndCityId);
modelBuilder.Entity<City>().HasMany(city => city.Connections)
.WithRequired().HasForeignKey(con => con.StartCityId);
OR you can do something like this as well wchich would be option 2 with results 2:
City Class becomes:
public partial class City
{
public City()
{
Connections = new HashSet<Connection>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Country { get; set; }
public ICollection<Connection> Connections { get; set; }
}
Connection Class becomes:
public partial class Connection
{
public Connection()
{
}
public int Id { get; set; }
public virtual ICollection<City> Cities { get; set; }
public int AantalMinuten { get; set; }
public double Prijs { get; set; }
}
And you don't have to do anything in your OnModelCreating.
In my case, problem occurred because when of overriding OnModelCreating in DbContext and not calling base.OnModelCreating(builder).
I'm trying to make a simple app to try Entity Framework Core, but i a have problem with setting up relations between entities. My entities:
public class Card
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Adress { get; set; }
public DateTime DoB { get; set; }
public DateTime DoS { get; set; }
public User Portal { get; set; }
public List<Reservation> Res { get; set; }
}
public class Doctor
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
public TimeSpan Start_Working { get; set; }
public TimeSpan End_Working { get; set; }
public List<Reservation> Reservations { get; set; }
public int SpecID { get; set; }
public Spec Spec { get; set; }
}
public class Reservation
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public DateTime DoR { get; set; }
public string Info { get; set; }
public int CardID { get; set; }
public Card Card_Nav_R { get; set; }
public int DoctorID { get; set; }
public Doctor Doctor { get; set; }
}
public class Spec
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public List<Doctor> Doctors { get; set; }
}
public class User
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public int CardID { get; set; }
public Card Card { get; set; }
}
And a configuration class where i tried to set up relations:
class ApplicationContext:DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Card> Cards { get; set; }
public DbSet<Reservation> Reservations { get; set; }
public DbSet<Doctor> Doctors { get; set; }
public DbSet<Spec> Specs { get; set; }
public ApplicationContext()
{
Database.EnsureCreated();
}
protected override void OnModelCreating(ModelBuilder ModelBuilder)
{
ModelBuilder.Entity<User>().HasKey(u => u.Id);
ModelBuilder.Entity<Card>().HasKey(c => c.Id);
ModelBuilder.Entity<Doctor>().HasKey(d => d.Id);
ModelBuilder.Entity<Spec>().HasKey(s => s.Id);
ModelBuilder.Entity<Reservation>().HasKey(r => r.Id);
ModelBuilder.Entity<User>().Property(u => u.Email).IsRequired();
ModelBuilder.Entity<User>().Property(u => u.Password).IsRequired();
ModelBuilder.Entity<Card>().Property(c => c.Name).IsRequired();
ModelBuilder.Entity<Card>().Property(c => c.Surname).IsRequired();
ModelBuilder.Entity<Card>().Property(c => c.DoB).IsRequired();
ModelBuilder.Entity<Card>().Property(c => c.Adress).IsRequired();
ModelBuilder.Entity<Doctor>().Property(d => d.Name).IsRequired();
ModelBuilder.Entity<Doctor>().Property(d => d.Surname).IsRequired();
ModelBuilder.Entity<Doctor>().Property(d => d.Spec).IsRequired();
ModelBuilder.Entity<Doctor>().Property(d => d.Email).IsRequired();
ModelBuilder.Entity<Doctor>().Property(d => d.Start_Working).IsRequired();
ModelBuilder.Entity<Doctor>().Property(d => d.End_Working).IsRequired();
ModelBuilder.Entity<Reservation>().Property(r => r.Info).IsRequired();
ModelBuilder.Entity<Reservation>().Property(r => r.Card_Nav_R).IsRequired();
ModelBuilder.Entity<Reservation>().Property(r => r.Doctor).IsRequired();
ModelBuilder.Entity<Reservation>().Property(r => r.DoR).IsRequired();
ModelBuilder.Entity<Spec>().Property(s => s.Name).IsRequired();
ModelBuilder.Entity<Doctor>().HasOne<Spec>(d=>d.Spec).WithMany(s => s.Doctors).HasForeignKey(d => d.SpecID);
ModelBuilder.Entity<User>().HasOne<Card>(u => u.Card).WithOne(c => c.Portal).HasForeignKey<User>(u => u.CardID);
ModelBuilder.Entity<Reservation>().HasOne<Card>(r => r.Card_Nav_R).WithMany(c => c.Res).HasForeignKey(r => r.CardID);
ModelBuilder.Entity<Reservation>().HasOne<Doctor>(r => r.Doctor).WithMany(d => d.Reservations).HasForeignKey(r => r.DoctorID);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=Simple_Try;Trusted_Connection=True;");
}
}
So, when i tried to add migration or add something to database i saw this error:
System.InvalidOperationException: 'The property or navigation 'Spec' cannot be added to the entity type 'Doctor' because a property or navigation with the same name already exists on entity type 'Doctor'.'
I really don't know how to fix this, i tried to use annotations instead of Fluent API, but had the same result.
The cause of the exception is the following line:
ModelBuilder.Entity<Doctor>().Property(d => d.Spec).IsRequired();
because Doctor.Spec is a navigation property
public class Doctor
{
// ...
public Spec Spec { get; set; }
}
and navigation properties cannot be configured via Property fluent API.
So simply remove that line. Whether reference navigation property is required or optional is controlled via relationship configuration. In this case
ModelBuilder.Entity<Doctor>()
.HasOne(d => d.Spec)
.WithMany(s => s.Doctors)
.HasForeignKey(d => d.SpecID)
.IsRequired(); // <--
although the IsRequired is automatically derived from the FK property type - since SpecID is non nullable, then the relationship is required.
For more info, see Required and Optional Properties and Required and Optional Relationships documentation topics.
I have more of a theoretical question. I have three Models. Employee, Commission and Position. Currently I created a ManyToMany relationship database EmployeeCommission (an Employee has many Commissions and Commissions have many Employees) but I also need to add Position to the relationship which would make that Commissions have many Employees that can have many Positions and so on. (Same employee can have different positions in different commissions).
How do I go about this? I know that ManyToMany relationship can only be between to models, so how do I create a ManyToMany database between three models?
Here's my Models, but I don't think you'll need them, but just for general information.
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
[Display(Name = "Phone Number")]
public int PhoneNumber { get; set; }
}
public class Commission
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
public class Position
{
public int Id { get; set;}
public string Name { get; set; }
}
Note: I am using CodeFirst approach.
You can View thisexample to do that:
public class User
{
public int Id { get; set; }
public ICollection<UserAccount> UserAccounts { get; set; }
}
public class Account
{
public int Id { get; set; }
public ICollection<UserAccount> UserAccounts { get; set; }
}
public class UserAccount
{
public int UserId { get; set; }
public int AccountId { get; set; }
public int RoleId { get; set; }
public User User { get; set; }
public Account Account { get; set; }
public Role Role { get; set; }
}
public class Role
{
public int Id { get; set; }
public string RoleName { get; set; }
public ICollection<UserAccount> UserAccounts { get; set; }
}
Configuration:
modelBuilder.Entity<UserAccount>()
.HasKey(e => new { e.UserId, e.AccountId });
modelBuilder.Entity<UserAccount>()
.HasRequired(e => e.User)
.WithMany(e => e.UserAccounts)
.HasForeignKey(e => e.UserId);
modelBuilder.Entity<UserAccount>()
.HasRequired(e => e.Account)
.WithMany(e => e.UserAccounts)
.HasForeignKey(e => e.AccountId);
modelBuilder.Entity<UserAccount>()
.HasRequired(e => e.Role)
.WithMany(e => e.UserAccounts)
.HasForeignKey(e => e.RoleId);
You can create a new UserAccount in a several ways.
I have such classes (simplicified):
public class Transaction
{
public int LocalId { get; set; }
public int MachineId { get; set; }
public virtual Machine Machine { get; set; }
public int? MoneyId { get; set; }
public virtual TransactionMoney Money { get; set; }
}
public class Machine
{
public int Id { get; set; }
public string Name { get; set; }
}
public class TransactionMoney
{
public int LocalId { get; set; }
public int MachineId { get; set; }
public virtual Machine Machine { get; set; }
public int TransactionId { get; set; }
public virtual Transaction Transaction { get; set; }
}
I would like to have relationship Transaction 1 <-> 0...1 TransactionMoney where foreign key in Money should be TransactionId and MachineId (connected to transaction's LocalId and MachineId). I need to do this in fluent API.
What I've tried is:
modelBuilder.Entity<Transaction>()
.HasOptional(t => t.Money)
.WithRequired(t => t.Transaction)
.HasForeignKey() <--- there is no such method
and in other side
modelBuilder.Entity<TransactionMoney>()
.HasRequired(t => t.Transaction)
.WithOptional(t => t.Money)
.HasForeignKey() <--- there is no such method
You can use something like this
modelBuilder.Entity<TransactionMoney>()
.HasRequired(t => t.Transaction)
.WithOptional(t => t.Money)
.Map(a => a.MapKey("TransactionId", "MachineId"));
It turns out that the design you are targeting cannot be done in EF. The closest I was able to get is as follows. But before I go, there are some things to note. MoneyId field is removed from Transaction. LocalId field is removed from TransactionMoney. Foreign key is specified with data annotation. If any of those is unacceptable, just skip the rest.
Entities:
public class Transaction
{
public int LocalId { get; set; }
public int MachineId { get; set; }
public virtual Machine Machine { get; set; }
public virtual TransactionMoney Money { get; set; }
}
public class Machine
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Transaction> Transactions { get; set; }
public virtual ICollection<TransactionMoney> Money { get; set; }
}
public class TransactionMoney
{
public int MachineId { get; set; }
public virtual Machine Machine { get; set; }
public int TransactionId { get; set; }
[ForeignKey("TransactionId,MachineId")]
public virtual Transaction Transaction { get; set; }
}
Configuration
modelBuilder.Entity<Transaction>()
.HasKey(t => new { t.LocalId, t.MachineId })
.HasRequired(t => t.Machine)
.WithMany(t => t.Transactions)
.HasForeignKey(t => t.MachineId);
modelBuilder.Entity<TransactionMoney>()
.HasRequired(t => t.Machine)
.WithMany(t => t.Money)
.HasForeignKey(t => t.MachineId);
modelBuilder.Entity<TransactionMoney>()
.HasKey(t => new { t.TransactionId, t.MachineId })
.HasRequired(t => t.Transaction)
.WithOptional(t => t.Money);