I am in the process of training at an internship to understand asp.net and MVC. We're currently facing a problem that neither I nor my boss can seem to solve.
I have a pre-constructed database of two tables, Camper and Guardian. Camper has a foreign key (guardID) that maps to Guardian's ID.
In my VS project, I have two models, GuardianEntity and CamperEntity. I also used LINQ to SQL to create the database models (dbGuardian and dbCamper). My controller converts the entity model to the db model and vice versa.
Here is the foreign key assignment as LINQ generated it:
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Guardian_Camper", Storage="_Guardian", ThisKey="guardID", OtherKey="ID", IsForeignKey=true)]
public Guardian Guardian
{
get
{
return this._Guardian.Entity;
}
set
{
Guardian previousValue = this._Guardian.Entity;
if (((previousValue != value)
|| (this._Guardian.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._Guardian.Entity = null;
previousValue.Campers.Remove(this);
}
this._Guardian.Entity = value;
if ((value != null))
{
value.Campers.Add(this);
this._guardID = value.ID;
}
else
{
this._guardID = default(int);
}
this.SendPropertyChanged("Guardian");
}
}
}
So it seems to recognize 'guardID' as the foreign key mapping to 'ID'.
But when I run my program and add a camper, it fails on db.saveChanges(). The error says "Invalid column name 'Guardian_ID'."
This question is similar to mine:
entity framework 4.1 invalid column name
Except when I followed one of the solutions offered there (changed my guardID to Guardian_ID as per convention), the error was still thrown, but this time was looking for 'Guardian_ID1'.
We've also noticed that if the column name in the DB for the key is anything other than 'ID', we get an error that says there is no key assigned, even though it is clear in the .dbml that the keys are specified. From what I'm experiencing, Linq won't work well with any column names that aren't what it expects.
So I had this same problem recently and it seems that it was caused by having our migrations out of sync with our Models. What the problem was for me was a parent DataModel referencing the child through an FK. What I had to do was
Remove the references to the "Child" from the "Parent's" Model. Then added a new migration reflecting that change.
Then runUpdate-Database -Script in the Package Manager Console for Visual Studio
When you've got the SQL scripted up, only copy everything after the
INSERT clause. (This is what enters the Migration into the
_MigrationHistory table)
Once you've copied this, paste it into a new query in SSMS. Executing
the query will only add the entry into the history and your
migrations will be "in sync", if you will.
Then for good measure I ran Update-Database again. Then ran my
application again and it worked fine.
So in a nutshell, what ended up causing this problem was that we had removed references from Child to Parent but not the other way around. To LINQ it seems that it thought the relationship still existed so it made that a part of the query.
Related
I am trying to update a parent entity, GuildMemberTeam, with child entities, GuildMember, Team and GuildMemberChallenge which also has a child entities, GuildMember and Challenge but am getting the following inner exception:
Inner Exception 1: SqlException: Violation of PRIMARY KEY constraint
'PK_Challenge'. Cannot insert duplicate key in object 'dbo.Challenge'.
The duplicate key value is (15ae8798-8567-457b-812a-5820cf7843e5). The
statement has been terminated.
The only new entity is the GuildMemberTeam as all the others already exist, but these are checked and recreated as follows:
public void AddChallenge(Challenge challenge)
{
if (challenge != null)
{
var id = challenge.Id == default(Guid) ? Guid.NewGuid() : challenge.Id;
Challenge = new Challenge(id, challenge.Name, challenge.Phase, challenge.Type, challenge.Star, challenge.Gear, challenge.Level, challenge.Reward);
}
}
This works for all the other entities apart from Challenge where i get the error. Can anyone please help me understand what i am doing wrong.
It doesn't change the fact that the problem is that you are trying to insert the same row twice (same Guid=Id) into the dbo.Challenge table.
This might be due to a debugging issue or something. You can either delete the row from the table with a
DELETE FROM [Challenge] WHERE Id = '15ae8798-8567-457b-812a-5820cf7843e5' and try running the app again.
If this doesn't solve your problem your entity management is faulty and you have to revise the ID handling. Implement ID checking before you try to save your context or something like that.
The other issue might be that your classes are not defined properly and EF doesn't recognize the relations. The relationships you are talking about are not parent-child, they are either one-to-many, many-to-many, many-to-one or none. DB RELATIONS
Each of your POCO-s should contain and instance of the other class, thus you define a relationship. E.g. if your GuildMemberChallenge contains an IEnumerable and a property with type of challenge.
If none of the above are a solution I need some more code (your classes, the repository) to figure it out.
Update:
When you are adding a new GuildMemberChallenge, which I assume you are trying to do now. You should set it's Challenge property to an existing entity if it exists, if it doesn't you can create one, but at the moment you are trying to create a Challenge that already exists in the database.
You are creating new Challenge but pass id of existing Challenge if it is set.
var id = challenge.Id == default(Guid) ? Guid.NewGuid() : challenge.Id;
I think you, that if you create new entity you should always create new Id
var id = Guid.NewGuid();
I'm using Entity Framework Core to store an object graph in a database. At various times while I'm building the graph, I create an entity, store it to the database, and the release the context. However, I'm running into an issue where EFC is trying to insert an entity that has already been inserted when it is connected to a new entity. This is best explained with code. Here's a short repro piece of code (this is straight line code, but the two uses of contexts happen at different times and places in the code).
In the second call to context.SaveChanges(), I get the following exception:
SqlException:
Cannot insert explicit value for identity column in table 'Namespace' when IDENTITY_INSERT is set to OFF.
When I look at the SQL being executed, it is trying to insert the namespace entity again, presumably because myType is being saved to the DB and it has a reference to the dbNamespace entity.
// see if namespace is in the db and add it if not
string someNamespaceString = "foobar";
CodeDatabase.Models.Namespace dbNamespace;
using (var context = new CodeFactsContext())
{
dbNamespace = context.Namespace.FirstOrDefault(ns => ns.Namespace1 == someNamespaceString);
if (dbNamespace == null)
{
dbNamespace = new Namespace() { Namespace1 = someNamespaceString };
context.Namespace.Add(dbNamespace);
}
context.SaveChanges();
}
// Type entity created somewhere from the code
var myType = new CodeDatabase.Models.Type()
{
FullName = "foobar.mytype",
ShortName = "mytype",
Namespace = dbNamespace // this is already in the DB
};
// check if myType is in the db and add it if not
using (var context = new CodeFactsContext())
{
var dbType = context.Type.FirstOrDefault(t => t.FullName == myType.FullName);
if (dbType == null)
{
dbType = myType;
context.Add(dbType);
}
context.SaveChanges(); // throws exception
}
Any idea how to get EF Core to recognize (in the second context.SaveChanges()) that myType should be inserted into the database, but myType.Namespace should not because it's already there? Both of the entities have an int id that is autogenerated by the DB and the id of Namespace is set to the database value after the first call to SaveChanges. I thought EF Core would recognize that the id is not 0 and not try to save it. Any help/suggestions very welcomed.
I thought EFC would recognize that the id is not 0 and not try to save it.
The problem is that you are using Add method which marks all reachable and not tracked entities as new, regardless of the key value (this is to allow the identity insert scenarios). This is explained in the Disconnected Entities - Working with graphs - All new/all existing entities. While your screnario falls into Mix of new and existing entities.
Any idea how to get EFC to recognize (in the second context.SaveChanges) that myType should be inserted into the database, but myType.Namespace should not because it's already there? Both of the entities have an int id that is autogenerated by the DB and the id of Namespace is set to the database value after the first call to SaveChanges.
Actually there is a simple solution explained in the second documentation link:
With auto-generated keys, Update can again be used for both inserts and updates, even if the graph contains a mix of entities that require inserting and those that require updating
where "again" refers to Saving single entities:
The Update method normally marks the entity for update, not insert. However, if the entity has a auto-generated key, and no key value has been set, then the entity is instead automatically marked for insert.
Luckily your entities use auto-generated keys, so simply use Update instead of Add:
if (dbType == null)
{
dbType = myType;
context.Update(dbType); // <--
}
I am developing an Web application using C#/MVC and EF6. I am using the Database First approach, as I am attaching to an existing database (SQL Server 2008-R2).
I have one table that contains 2 foreign keys to the same target as follows:
Table Artifact:
int ArtifactId;
int AnalystId; //Employee performing analysis work on artifact
int ChampionId; //Employee servind as champion for artifact
And the target table is very straightforward.
Table Employee:
int EmployeeId;
// Employee info
I managing the Datbase access as a disconnected repository so that when I retrieve and update the information, I can manage the state.
public Candidate GetArtifact(int artifactId)
{
using (var context = new DataEntities())
{
return context.Artifacts.AsNoTracking()
.Where(x => x.ArtifactId == artifactId)
.Include(x => x.Employee) //Analyst
.Include(x => x.Employee1) //Champion
.FirstOrDefault();
}
}
public int SaveArtifact(Artifact artifact)
{
using (var context = new DataEntities())
{
if (artifact.ArtifactId > 0)
{
context.Entry(artifact).State = EntityState.Modified;
}
else
{
context.Artifacts.Add(artifact);
}
context.SaveChanges();
return artifact.CandidateId;
}
}
Everything works as I would expect, except for the case where both the Analyst and the Champion reference the same record from the Employee record. In testing the update existing code path, I get one of 2 exceptions, depending on the initial state of the data. Note, the exception only happens when the data is updated, it retrieves correctly without issue.
When I attempt to update an artifact with both the Analyst and Champion referencing the same employee record. I get the following exception:
Attaching an entity of type 'Data.DataModel.Employee' failed because another
entity of the same type already has the same primary key value. This can
happen when using the 'Attach' method or setting the state of an entity to
'Unchanged' or 'Modified' if any entities in the graph have conflicting key
values. This may be because some entities are new and have not yet received
database-generated key values. In this case use the 'Add' method or the
'Added' entity state to track the graph and then set the state of non-new
entities to 'Unchanged' or 'Modified' as appropriate.
Any suggestions on what I can do to correct this?
After doing a little additional digging, I found the solution to my particular problem. The pattern that I was following (established by one of my colleagues) was to eager load all data records. So the Fetch for the artifact included two Include statements to include the 2 different keys, as seen above in my original example.
Where this caused a problem is that, in the case where both the Champion and Analyst were referencing the same Employee record, the update saw the primary key/record from the Analyst already attached to context, so threw the exception when it attempted to attach what was actually the same record to the context again for the Champion.
My solution was to remove the eager loading from the retrieval, which for my particular design imposed no real issues since the Employee record is really referential only and never updated in conjunction with the artifact update.
so my GetArtifact method looks like the following after the fix:
public Candidate GetArtifact(int artifactId)
{
using (var context = new DataEntities())
{
return context.Artifacts.AsNoTracking()
.Where(x => x.ArtifactId == artifactId)
.FirstOrDefault();
}
}
And the couple of places that actually reference the Employee record just use the appropriate ID and directly fetches the record. This prevents the issue.
For me, the lesson learned was to understand Eager Loading and when to use it (and more importantly when NOT to use it).
Assume a simplistic data model consisting of two tables, Book and Author, related by a foreign key constraint "Book.AuthorId = Author.Id".
Now say you've got some flat list of books:
Title Author
---------------------------------------------------------
The Selfish Gene Richard Dawkins
Why Is Sex Fun Jared Diamond
The Ancestors Tale Richard Dawkins
How do you import these without making a gazillion round trips to the DB?
I'm new to EF but figured it would allow me to add entities to the object context and then they'd be available in that context even if they aren't saved yet. This seems not to be the case. In principle, I'm doing this:
void Import()
{
ctx = new Database();
foreach (...)
{
Import(bookTitle, authorName);
}
ctx.SaveChanges();
}
void Import(string bookTitle, string authorName)
{
var author = ctx.Authors.Single(a => a.Name == authorName);
if (author == null)
{
author = new Author();
author.Name = authorName;
ctx.AddToAuthors(author);
}
var book = new Book();
book.Author = author;
ctx.AddToBooks(book);
}
But EF never finds an author I've already added, so it makes a new one every time. Hence the two books by Dawkins are related to two different author objects in the graph, which is of course wrong.
Next, upon SaveChanges, EF manages to insert the first Book and Author record, but then crashes on number two with a PRIMARY KEY VIOLATION because it attempts to use Id = 0 for every object.
System.Data.UpdateException: An error occurred while updating the entries.
See the InnerException for details. --->
System.Data.SqlClient.SqlException: Violation of PRIMARY KEY
constraint 'PK_Book'. Cannot insert duplicate key in object 'dbo.Book'.
I attempted to change the definition for the id fields to INT IDENTITY(1,1) in MSSQL and then update the model. Letting the DB define the key values is anyway better than having Entity Framework (or any client) do it. But this appears not to be supported at all. EF seems not to have noticed that my keys are IDENTITY columns when I updated the model. It just leads to
System.Data.UpdateException: An error occurred while updating the entries.
See the InnerException for details. --->
System.Data.SqlClient.SqlException: Cannot insert explicit value for
identity column in table 'Book' when IDENTITY_INSERT is set to OFF.
The exact symptom is provided whether I update the model or not following the switch from "Id INT PRIMARY KEY" to "Id INT IDENTITY(1,1) PRIMARY KEY" for the column definition, which is what leads me to suspect that Microsoft didn't even remember to THINK ABOUT identity columns, though I hope I'm wrong about this!
I realize there's really multiple questions lurking in here, but since nothing in Entity Framework works as expected I choose to put the overarching question as "given this data model and these data to import, what is a good way to do it using Entity Framework 3.5?"
Please refrain from advising me to use EF 4.0 instead, as that is outside of my control. :)
My question was based on a misunderstanding - in fact adding the objects to the object context without saving does make them available on the object context.
What was really going on is this: I'm trigging logic in a .net assembly from powershell script using Reflection.Load. When I made changes in the .net project, rebuilt it and GAC-ed it, then reran my PowerShell script, I was still running the old code. Basically the code from MyAssembly.dll is linked into the powershell session, so when I rerun the script it does not matter that a new version of the code has been deployed to the GAC...
The PK violation then makes sense, since my code does not assign an ID to the entity and there is more than one author - I tried to save both with ID = 0.
The "cannot insert explicit value when IDENTITY INSERT is off" also now makes sense, because I had modified my database but was still running the code that assumed the ID from should be inserted.
In short, what fixed the problem was to close my PowerShell session and start a new one.
I'm trying to delete a record from my table using EF, and nothing is happening. The code executes with no errors, but the record just sticks around in the DB. I have nearly identical code elsewhere that's working.
using (var DB = new PTNWebConfigurationModel.PTNWebConfigurationEntities())
{
var account = DB.Accounts.Where(a => a.LoginID == loginID).FirstOrDefault();
//Load existing session for the Account, otherwise create a new one.
if (!LoadExistingSession(ip, DB, account))
{
CreateNewSession(ip, DB, account);
}
AccountsSession sessionsToDelete = DB.AccountsSessions.Where(a => a.RemoteIP == ip && a.AccountID == 1).FirstOrDefault();
if (sessionsToDelete != null)
{
DB.DeleteObject(sessionsToDelete);
DB.SaveChanges();
}
}
I've also tried it with these options:
DB.DeleteObject(sessionsToDelete);
DB.SaveChanges(System.Data.Objects.SaveOptions.DetectChangesBeforeSave);
DB.AcceptAllChanges();
I also started with no using block and just and instantiated entity object, but that didn't work either.
I love issues like this.
Most of the times that I have seen an ORM not execute a command (and without error) is due to changes to the underlying data tables that aren't represented in the generated classes for that table.
You might refresh your schema / class model and try again.
Carrying on from Chris' answer the other thing I've found in the past is if you are doing EF database first modelling then you may have created the foreign keys linking two associated tables but the key hasn't been set as delete cascade in the relationship. This can cause EF to be a little difficult. So check any FKs in your db. Hope that helps.