I face the problem that EF creates a column in the query that does not exist in the Oracle database table.
The simplified model which is created by EF looks like this (I use DB first approach):
public partial class USER
{
public int ID { get; set; }
public string NAME { get; set; }
public int PROCESS_ID { get; set; }
public virtual PROCESS PROCESS { get; set; }
}
public partial class PROCESS
{
public PROCESS()
{
this.USER = new HashSet<User>();
}
public int ID { get; set; }
public virtual ICollection<USER> USER { get; set; }
}
I set up the foreign key constraint in the oracle sql developer.
When I try to get the Users for a selected Process like this:
var users = context.Users.Where(u => u.PROCESS_ID == 0);
It produces following error:
ORA-00904: "Extent1"."R1": invalid ID
So i took a look on the produced SQL:
SELECT
"Extent1".ID,
"Extent1".NAME,
"Extent1".R1,
FROM DB.USER "Extent1"
WHERE "Extent1".R1 = :p__linq__0
Of course this produces an error because R1 isn't a column in the table. But I can't figure out where it comes from. It seems like EF can't map the foreign key properly thats why it's also missing in the generated SQL query?
Maybe someone has a tip for me :)
To follow up my comment, here is a link to the conventions.
The convention for a foreign key is that it must have the same data type as the principal entity's primary key property and the name must follow one of these patterns:
[navigation property name][principal primary key property name]Id
[principal class name][primary key property name]Id
[principal primary key property name]Id
Your convention [navigation property name]_ID isn't on the list.
Encountered the same error recently while working with Oracle using DevArt provider. Turned out it was caused by a column name being longer than 30 chars. OP mentioned that the model posted in his question is a simplified one so it still may be the case.
Given the following set up where there are many Teams and there are many LeagueSessions. Each Team belongs to zero or more LeagueSessions but only ever one LeagueSession is active. LeagueSessions have many teams, and the teams will be repeated. Many-to-many relationship is established between Teams and LeagueSessions with a join table called TeamsSessions.
Team model looks like this:
public class Team
{
public string Id { get; set; }
public string Name { get; set; }
public League League { get; set; }
public string LeagueID { get; set; }
public bool Selected { get; set; }
public ICollection<Match> Matches { get; set; }
public virtual ICollection<TeamSession> TeamsSessions { get; set; }
}
Team model fluent api configuration:
`
public class TeamConfiguration
{
public TeamConfiguration(EntityTypeBuilder<Team> model)
{
// The data for this model will be generated inside ThePLeagueDataCore.DataBaseInitializer.DatabaseBaseInitializer.cs class
// When generating data for models in here, you have to provide it with an ID, and it became mildly problematic to consistently get
// a unique ID for all the teams. In ThePLeagueDataCore.DataBaseInitializer.DatabaseBaseInitializer.cs we can use dbContext to generate
// unique ids for us for each team.
model.HasOne(team => team.League)
.WithMany(league => league.Teams)
.HasForeignKey(team => team.LeagueID);
}
}
`
Each team belongs to a single League. League model looks like this:
`public class League
{
public string Id { get; set; }
public string Type { get; set; }
public string Name { get; set; }
public IEnumerable<Team> Teams { get; set; }
public bool Selected { get; set; }
public string SportTypeID { get; set; }
public SportType SportType { get; set; }
public IEnumerable<LeagueSessionSchedule> Sessions { get; set; }
}`
fluent API for the League:
`public LeagueConfiguration(EntityTypeBuilder<League> model)
{
model.HasOne(league => league.SportType)
.WithMany(sportType => sportType.Leagues)
.HasForeignKey(league => league.SportTypeID);
model.HasMany(league => league.Teams)
.WithOne(team => team.League)
.HasForeignKey(team => team.LeagueID);
model.HasData(leagues);
}`
SessionScheduleBase class looks like this:
public class SessionScheduleBase
{
public string LeagueID { get; set; }
public bool ByeWeeks { get; set; }
public long? NumberOfWeeks { get; set; }
public DateTime SessionStart { get; set; }
public DateTime SessionEnd { get; set; }
public ICollection<TeamSession> TeamsSessions { get; set; } = new Collection<TeamSession>();
public ICollection<GameDay> GamesDays { get; set; } = new Collection<GameDay>();
}
Note: LeagueSessionSchedule inherits from SessionScheduleBase
The TeamSession model looks like this:
`public class TeamSession
{
public string Id { get; set; }
public string TeamId { get; set; }
public Team Team { get; set; }
public string LeagueSessionScheduleId { get; set; }
public LeagueSessionSchedule LeagueSessionSchedule { get; set; }
}`
I then configure the relationship with the fluent API like this:
`public TeamSessionConfiguration(EntityTypeBuilder<TeamSession> model)
{
model.HasKey(ts => new { ts.TeamId, ts.LeagueSessionScheduleId });
model.HasOne(ts => ts.Team)
.WithMany(t => t.TeamsSessions)
.HasForeignKey(ts => ts.TeamId);
model.HasOne(ts => ts.LeagueSessionSchedule)
.WithMany(s => s.TeamsSessions)
.HasForeignKey(ts => ts.LeagueSessionScheduleId);
}`
The problem arises whenever I attempt to insert a new LeagueSessionSchedule. The way I am adding a new TeamSession object onto the new LeagueSessionSchedule is like this:
`foreach (TeamSessionViewModel teamSession in newSchedule.TeamsSessions)
{
Team team = await this._teamRepository.GetByIdAsync(teamSession.TeamId, ct);
if(team != null)
{
TeamSession newTeamSession = new TeamSession()
{
Team = team,
LeagueSessionSchedule = leagueSessionSchedule
};
leagueSessionSchedule.TeamsSessions.Add(newTeamSession);
}
}`
Saving the new LeagueSessionSchedule code:
public async Task<LeagueSessionSchedule> AddScheduleAsync(LeagueSessionSchedule newLeagueSessionSchedule, CancellationToken ct = default)
{
this._dbContext.LeagueSessions.Add(newLeagueSessionSchedule);
await this._dbContext.SaveChangesAsync(ct);
return newLeagueSessionSchedule;
}
Saving the new LeagueSessionSchedule object throws an error by Entity Framework Core that it cannot INSERT a duplicate primary key value into the dbo.Teams table. I have no idea why its attempting to add to dbo.Teams table and not into TeamsSessions table.
ERROR:
INSERT INTO [LeagueSessions] ([Id], [Active], [ByeWeeks], [LeagueID], [NumberOfWeeks], [SessionEnd], [SessionStart])
VALUES (#p0, #p1, #p2, #p3, #p4, #p5, #p6);
INSERT INTO [Teams] ([Id], [Discriminator], [LeagueID], [Name], [Selected])
VALUES (#p7, #p8, #p9, #p10, #p11),
(#p12, #p13, #p14, #p15, #p16),
(#p17, #p18, #p19, #p20, #p21),
(#p22, #p23, #p24, #p25, #p26),
(#p27, #p28, #p29, #p30, #p31),
(#p32, #p33, #p34, #p35, #p36),
(#p37, #p38, #p39, #p40, #p41),
(#p42, #p43, #p44, #p45, #p46);
System.Data.SqlClient.SqlException (0x80131904): Violation of PRIMARY KEY constraint 'PK_Teams'. Cannot insert duplicate key in object 'dbo.Teams'. The duplicate key value is (217e2e11-0603-4239-aab5-9e2f1d3ebc2c).
My goal is to create a new LeagueSessionSchedule object. Along with the creation of this object, I also have to create a new TeamSession entry to the join table (or not if join table is not necessary) to then be able to pick any given team and see what session it is currently a part of.
My entire PublishSchedule method is the following:
`
public async Task<bool> PublishSessionsSchedulesAsync(List<LeagueSessionScheduleViewModel> newLeagueSessionsSchedules, CancellationToken ct = default(CancellationToken))
{
List<LeagueSessionSchedule> leagueSessionOperations = new List<LeagueSessionSchedule>();
foreach (LeagueSessionScheduleViewModel newSchedule in newLeagueSessionsSchedules)
{
LeagueSessionSchedule leagueSessionSchedule = new LeagueSessionSchedule()
{
Active = newSchedule.Active,
LeagueID = newSchedule.LeagueID,
ByeWeeks = newSchedule.ByeWeeks,
NumberOfWeeks = newSchedule.NumberOfWeeks,
SessionStart = newSchedule.SessionStart,
SessionEnd = newSchedule.SessionEnd
};
// leagueSessionSchedule = await this._sessionScheduleRepository.AddScheduleAsync(leagueSessionSchedule, ct);
// create game day entry for all configured game days
foreach (GameDayViewModel gameDay in newSchedule.GamesDays)
{
GameDay newGameDay = new GameDay()
{
GamesDay = gameDay.GamesDay
};
// leagueSessionSchedule.GamesDays.Add(newGameDay);
// create game time entry for every game day
foreach (GameTimeViewModel gameTime in gameDay.GamesTimes)
{
GameTime newGameTime = new GameTime()
{
GamesTime = DateTimeOffset.FromUnixTimeSeconds(gameTime.GamesTime).DateTime.ToLocalTime(),
// GameDayId = newGameDay.Id
};
// newGameTime = await this._sessionScheduleRepository.AddGameTimeAsync(newGameTime, ct);
newGameDay.GamesTimes.Add(newGameTime);
}
leagueSessionSchedule.GamesDays.Add(newGameDay);
}
// update teams sessions
foreach (TeamSessionViewModel teamSession in newSchedule.TeamsSessions)
{
// retrieve the team with the corresponding id
Team team = await this._teamRepository.GetByIdAsync(teamSession.TeamId, ct);
if(team != null)
{
TeamSession newTeamSession = new TeamSession()
{
Team = team,
LeagueSessionSchedule = leagueSessionSchedule
};
leagueSessionSchedule.TeamsSessions.Add(newTeamSession);
}
}
// update matches for this session
foreach (MatchViewModel match in newSchedule.Matches)
{
Match newMatch = new Match()
{
DateTime = match.DateTime,
HomeTeamId = match.HomeTeam.Id,
AwayTeamId = match.AwayTeam.Id,
LeagueID = match.LeagueID
};
leagueSessionSchedule.Matches.Add(newMatch);
}
try
{
leagueSessionOperations.Add(await this._sessionScheduleRepository.AddScheduleAsync(leagueSessionSchedule, ct));
}
catch(Exception ex)
{
}
}
// ensure all leagueSessionOperations did not return any null values
return leagueSessionOperations.All(op => op != null);
}
`
This is not a many-to-many relationship.
It is two separate one-to-many relationships, which happen to refer to the same table on one end of the relationship.
While it is true that on the database level, both use cases are represented by three tables, i.e. Foo 1->* FooBar *<-1 Bar, these two cases are treated differently by Entity Framework's automated behavior - and this is very important.
EF only handles the cross table for you if it is a direct many-to-many, e.g.
public class Foo
{
public virtual ICollection<Bar> Bars { get; set; }
}
public class Bar
{
public virtual ICollection<Foo> Foos { get; set; }
}
EF handles the cross table behind the scenes, and you are never made aware of the existence of the cross table (from the code perspective).
Importantly, EF Core does not yet support implicit cross tables! There is currently no way to do this in EF Core, but even if there were, you're not using it anyway, so the answer to your problem remains the same regardless of whether you're using EF or EF Core.
However, you have defined your own cross table. While this is still representative of a many-to-many relationship in database terms, it has ceased to be a many-to-many relationship as far as EF is concerned, and any documentation you find on EF's many-to-many relationships no longer applies to your scenario.
Unattached but indirectly added objects are assumed to be new.
By "indirectly added", I mean you that it was added to the context as part of another entity (which you directly added to the context). In the following example, foo is directly added and bar is indirectly added:
var foo = new Foo();
var bar = new Bar();
foo.Bar = bar;
context.Foos.Add(foo); // directly adding foo
// ... but not bar
context.SaveChanges();
When you add (and commit) a new entity to the context, EF adds it for you. However, EF also looks at any related entities that the first entity contains. During the commit in the above example, EF will look at both the foo and bar entities and will handle them accordingly. EF is smart enough to realize that you want bar to be stored in the database since you put it inside the foo object and you explicitly asked EF to add foo to the database.
It is important to realize that you've told EF that foo should be created (since you called Add(), which implies a new item), but you never told EF what it should do with bar. It's unclear (to EF) what you expect EF to do with this, and thus EF is left guessing at what to do.
If you never explained to EF whether bar already exists or not, Entity Framework defaults to assuming it needs to create this entity in the database.
Saving the new LeagueSessionSchedule object throws an error by Entity Framework Core that it cannot INSERT a duplicate primary key value into the dbo.Teams table. I have no idea why its attempting to add to dbo.Teams table
Knowing what you now know, the error becomes clearer. EF is trying to add this team (which was the bar object in my example) because it has no information on this team object and what its state in the database is.
There are a few solutions here.
1. Use the FK property instead of the navigational property
This is my preferred solution because it leaves no room for error. If the team ID does not yet exist, you get an error. At no point will EF try to create a team, since it doesn't even know the team's data, it only knows the (alleged) ID you're trying to create a relationship with.
Note: I am omitting LeagueSessionSchedule as it is unrelated to the current error - but it's essentially the same behavior for both Team and LeagueSessionSchedule.
TeamSession newTeamSession = new TeamSession()
{
TeamId = team.Id
};
By using the FK property instead of the nav prop, you are informing EF that this is an existing team - and therefore EF no longer tries to (re)create this team.
2. Ensure that the team is tracked by the current context
Note: I am omitting LeagueSessionSchedule as it is unrelated to the current error - but it's essentially the same behavior for both Team and LeagueSessionSchedule.
context.Teams.Attach(team);
TeamSession newTeamSession = new TeamSession()
{
Team = team
};
By attaching the object to the context, you are informing it of its existence. The default state of a newly attached entity is Unchanged, meaning "this already exists in the database and has not been changed - so you don't need to update it when we commit the context".
If you have actually made changes to your team that you want to be updated during commit, you should instead use:
context.Entry(team).State = EntityState.Modified;
Entry() inherently also attaches the entity, and by setting its state to Modified you ensure that the new values will be committed to the database when you call SaveChanges().
Note that I prefer solution 1 over solution 2 because it's foolproof and much less likely to lead to unexpected behavior or runtime exceptions.
String primary keys are undesirable
I'm not going to say that it doesn't work, but strings cannot be autogenerated by Entity Framework, making them undesirable as the type of your entity's PK. You will need to manually set your entity PK values.
Like I said, it's not impossible, but your code shows that you're not explicitly setting PK values:
if(team != null)
{
TeamSession newTeamSession = new TeamSession()
{
Team = team,
LeagueSessionSchedule = leagueSessionSchedule
};
leagueSessionSchedule.TeamsSessions.Add(newTeamSession);
}
If you want your PK's to be automatically generated, use an appropriate type. int and Guid are by far the most commonly used types for this.
Otherwise, you're going to have to start setting your own PK values, because if you don't (and the Id value thus defaults to null), your code is going to fail when you add a second TeamSession object using the above code (even though you're doing everything else correctly), since PK null is already taken by the first entity you added to the table.
I using EF 6.0 model first. I have an Entity with a primary key. When I create the Entity using the new operator the primary key is always set to 0. I cannot perform a context save until later in the process. In other parts of the code it is necessary to reference the primary key. As a workaround I am setting the primary key to a unique value manually. Is there anyway I can get the system to generate the primary key automatically?
Thanks in advance,
Terry
You can't get Id before SaveChanges. Because primary key is set by database engine. All you can do you is to refer to realeted object not to id. Then EF do the save in proper way. Your model can look:
class Parent
{
public int Id { get; set; }
public List<Child> Children { get; set; }
}
class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
public Parent Parent { get; set; }
}
Then you can easy save using references and Ids will be fill after SaveChanges.
var parent = new Parent()
parent.Childs = new List<Child>()
var child1 = new Child();
var child2 = new Child();
parent.Childs.Add(child1);
parent.Childs.Add(child2);
dbContex.Parents.Add(parent);
dbContex.SaveChanges();
If you have to set the primary key on the client side you might want to remove DatabaseGeneratedOption.Identity to None, and switch from an int to a guid and manually set the value to Guid.NewGuid. I'm not actually a fan of this approach, from a performance standpoint you want your primary key to be the smallest viable datatype, but if you need to generate your primary key outside of the database that is the way it's typically done.
Most posts around the ObjectStateManager are true-duplicate issues based on unique primary keys. My problem is that my table does Not have a primary key, but it does have multiple foreign keys, one of which is Nullable.
class MyObject
{
int Key1;
int? Key2;
}
context.MyTable.Attach(new MyObject() { Key1 = 100; Key2 = null; });
context.MyTable.Attach(new MyObject() { Key1 = 100; Key2 = 2000; }); ****
It blows up on the second call, even though this is a unique row in the database.
Any thoughts on how to get around this? or enforce checking of BOTH keys?
As #BenAaronson mentioned, you should have a surrogate, primary key in your table in this instance. Entity Framework quite simply cannot deal with entities that have no primary key defined—in fact, I'm surprised your code even compiled/ran. Perhaps your real code with real class and property names caused EF to infer a primary key using its default conventions. For example:
public class MyClass
{
public int MyClassId { get; set; }
public int MyOtherClassId { get; set; }
}
In the code above, even without explicitly declaring it, EF would assume that the MyClassId property is the primary key for the class MyClass, even if that may not have been your intention.
If EF can't infer a primary key and one is not explicitly provided, then your code wouldn't compile (or at most, it wouldn't run).
So looking at your code, what appears to be happening is that EF inferred a primary key somehow (in your example above, Key1). You then tried to attach a new object to your context:
context.MyTable.Attach(new MyObject() { Key1 = 100; Key2 = null; });
This results in the context adding a new MyObject instance whose primary key value is 100 and whose Key2 property is null.
Next, you attempt to attach another item to the context:
context.MyTable.Attach(new MyObject() { Key1 = 100; Key2 = 2000; });
What this does is attempt to add a new item to the context whose primary key is 100, and this fails. This is because you already have an object being tracked by the context whose primary key value is 100 (executed by the first statement above).
Since you need to allow possibly null values for the Key2 property, you can't use a composite primary key, as you already stated. So you will need to follow #BenAaronson's advice and add a surrogate primary key:
public class Object
{
// Alternatively, you can use a mapping class to define the primary key
// I just wanted to make the example clear that this is the
// surrogate primary key property.
[Key]
private int ObjectID { get; set; } // IIRC, you can make this private...
public int Key1 { get; set; }
public int Key2 { get; set; }
}
Now, you can do the following:
context.MyTable.Add(new MyObject() { Key1 = 100, Key2 = null; });
context.MyTable.Add(new MyObject() { Key1 = 100, Key2 = 2000; });
Notice I used the Add method and not Attach. That's because when using Attach, the context is assuming that you're adding an object to the context which already exists in the database, but which was not brought into the context via a query; instead, you had a representation of it in memory, and at this point, you want the context to start tracking changes made to it and update the object in the database when you call context.SaveChanges(). When using the Attach property, the context adds the object in the Unmodified state. That's not what we want. We have brand new objects being added to the context. So we use Add. This tells the context to add the item in the Added state. You can make any changes you want to it. Since it's a new item, it will be in the Added state until you call context.SaveChanges() and the item is persisted to your data store, at which time, it's state will be updated to Unmodified.
One more thing to note at this point. If this is a "many-to-many" table, you should never need to manually add rows to this type of join table in EF (there are some caveats to this statement, see below). Instead, you should setup a mapping between the two objects whose relationship is many-to-many. It's possible to specify an optional many-to-many relationship, too. If the first object has no relationship to the second, there should be no row in the join table for the first object, and vice versa.
Regarding join table caveats as alluded to above: if your join-tables (i.e. many-to-many mapping tables) are simple (meaning the only columns in the table are those columns mapping one ID to the related ID), then you won't even see the join-table as part of your object model. This table is managed by EF in the background through navigation properties on the related objects. However, if the join-table contains properties other than just the ID properties of the related objects (and, this implies you have an existing database or explicitly structured your object model this way), then you will have an intermediate entity reference. For example:
public class A
{
public int ID { get; set; }
}
public class B
{
public int ID { get; set; }
}
public class AToB
{
// Composite primary key
[Key]
public int IdA { get; set; }
[Key]
public int IdB { get; set; }
public A SideA { get; set; }
public B SideB { get; set; }
// An additional property in the many-to-many join table
public DateTime Created { get; set; }
}
You would also have some mappings to tell EF how to wire up the foreign key relationships. What you'd wind up with in your object model then, is the following:
myA.AToB.SideB // Accesses the related B item to this A item.
myA.AToB.Created // Accesses the created property of AToB, telling you
// when the relationship between A and B was created.
In fact, if you have non-trivial join tables such as this example, EF will always include them in your object model when generating its model from an existing database.
I would strongly suggest that you check out Julie Lerman's and Rowan Miller's books on programming Entity Framework.
[ Using Code First DbContext with Entity Framework 5.0 RC ]
Entity with 2 Navigational Properties / 2 Foreign Keys
public class Compositon
{
public string Id { get; set; }
public string SimpletonId { get; set; }
[ForeignKey("SimpletonId")]
public Simpleton Simpleton { get; set; }
public string CompanitonId { get; set; }
[ForeignKey("CompanitonId")]
public Companiton Companiton { get; set; }
}
First Pass - SaveChanges to Empty Database Works
var composition = new Compositon();
compositon.Id = "UniquePrimaryKey";
var simpleton = new Simpleton();
// This foreign key does not exist in database yet
simpleton.Id = "Simpleton1";
composition.Simpleton = simpleton;
var companiton = new Companiton();
companiton.Id = "SomeOther1";
composition.Companiton = companiton;
// Repositor references the DbContext
Repositor.Compositons.Add(composition);
Repositor.SaveChanges();
Second Pass - Existing Child Foreign Key Leads to Parent Fault
var composition = new Compositon();
compositon.Id = "AnotherUniquePrimaryKey";
var simpleton = new Simpleton();
// This foreign key already exists in database
simpleton.Id = "Simpleton1";
composition.Simpleton = simpleton;
var companiton = new Companiton();
companiton.Id = "SomeOther2";
composition.Companiton = companiton;
Repositor.Compositons.Add(composition);
Repositor.SaveChanges();
DbUpdateException: An error occurred while updating the entries.
I need to be able save these parent classes into the database, because they are unique even though they sometimes contain a navigational property that is already stored - how can I save the parent from this child primary key conflict?
In the second pass you will need to retrieve the existing Simpleton from the DbContext. I'm guessing that you could do that like this:
`simpleton = Repositor.Simpletons.First(s => s.Id == "Simpleton1");`
Currently, you're creating a brand new one, which the Entity Framework tries to insert as such, hence the key violation.