I have a problem, I have a wizard that can update data AND insert data. So, if I have an existing list of team members, I can update their roles but If necessary, I can also add/insert a person to this team. So I can update roles and insert a new team member into the same table all during the same transaction. Data can be updated to and inserted to table teamMembers.
When I try to add a new teamMember, I also have an existing member where I simply want to update his role.
Both changes happen to the same table called TeamMember. When I debug the context, everything looks good. it shows that there are two changes that will occur for the TeamMember table. One transaction is the update and the other transaction is the insert. When I perform an update using:
var teamMember = new TeamMember
{
Name = user.FullName,
UserProfileId = user.UserProfileId,
RoleId = user.RoleId
};
TeamMemberList.Add(teamMember);
project.TeamMembers = TeamMemberList;
//And then call
this.Context.Projects.Attach(project);
this.Context.Entry(project).State = System.Data.EntityState.Modified;
it updates but the record that needs to be inserted fails.
HOW CAN I DO BOTH AN INSERT AND UPDATE TO THE SAME TABLE DURING THE SAME TRANSACTION?
CURRENT ERROR IS:
The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: A referential integrity constraint violation occurred: The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship.
I think you need to add the TeamMember entity to the context's global list. Something like:
var teamMember = new TeamMember()
{
Name = user.FullName,
UserProfileId = user.UserProfileId,
RoleId = user.RoleId
}
project.TeamMembers.Add( teamMember );
this.Context.TeamMembers.Add( teamMember );
this.Context.SaveChanges();
How about loading the existing project entity first and then adding members.
var project = this.Context.Project.Where(p => p.ID = "bar").Include("TeamMembers").FirstOrDefault();
var teamMember= new TeamMember
{
Name = user.FullName,
UserProfileId = user.UserProfileId,
RoleId = user.RoleId
};
project.TeamMembers.Add(teamMember);
this.Context.SaveChanges()
Related
I realize this must be a relatively simple thing to do, but I'm not getting what I'm looking for with Google.
I need to get the record ID of the record I just saved using the Entity Framework. With SQL queries we used "Select ##IDENTITY as 'Identity';"
If anyone can help it would be greatly appreciated.
The default behavior of Entity Framework is it sets identity fields on entities from the database right after SaveChanges is called.
In the following sample code, before SaveChanges is called, my employee has a default ID of 0. After SaveChanges my employee has a generated ID of 1.
using (TestDbEntities context = new TestDbEntities())
{
Employee e = new Employee ();
e.FirstName = "John";
e.LastName = "Doe";
context.Employee.Add(e);
context.SaveChanges();
Console.WriteLine("Generated ID: {0}", e.ID);
Console.ReadKey();
}
I am trying to get a mapped delete procedure in EF (5.0, database first) to use updated properties in the entity as parameters.
The mapped procedure takes two parameters:
DeleteRow:
#Id : int -> (Key) Id : int32
#Modifiedby : char -> Modifiedby : string
In the controller I want to change the Modifiedby value before the delete procedure is called.
Subscription subscription = context.Subscription.Find(id);
subscription.Modifiedby = "Test";
context.Subscription.Remove(subscription);
context.ChangeTracker.DetectChanges();
context.SaveChanges();
However, when the procedure is called it is always the old value of Modifiedby that is passed to the delete procedure.
I don't want to do an update call to the database before deleting the entity.
Ended up solving this by using none-tracked entities. Got the idea from this blog post: http://blogs.msdn.com/b/alexj/archive/2009/03/27/tip-9-deleting-an-object-without-retrieving-it.aspx
The AsNoTracking() method call is added and after the modifications the entities are attached to the context and then removed.
Subscription subscription = context.Subscription.AsNoTracking()
.Include(i => i.RelatedEntity)
.FirstOrDefault(c => c.SubscriptionId == id);
subscription.Modifiedby = "TEST-Subcription";
subscription.RelatedEntity.ToList().ForEach(f => f.Modifiedby = "TEST_Related");
context.Subscription.Attach(subscription);
context.Subscription.Remove(subscription);
context.SaveChanges();
This allows the new Modifiedby values to be sent to the mapped delete procedure. I can also set new values for the related entities, which also have mapped procedures and for which cascade deletion is set.
I have an Entity. Mandate. Every mandate has a required:many relation to a Person (NavigationProperty). I use the DbContext API with (LazyLoadingEnabled, AutoDetectChangesEnabled, ValidateOnSaveEnabled, ProxyCreationEnabled)
Now I like to delete a Mandate entity. The mandate entities are loaded by another context with AsNoTracking().
message.Result.
ObserveOn(On<DataComposition>.Scheduler).
Where(r => r).
Subscribe(_ =>
{
using (var unit = UnitOfWork.Begin())
{
var mandate = this.SelectedItem.OriginalEntity;
this.mandateRepository.Attach(mandate);
// mandate.Person.ToString();
this.mandateRepository.Delete(mandate);
unit.Commit();
}
this.List.RemoveOnUi(this.SelectedItem);
});
Now during committing I get the following exception: Entities in 'CodeFirstContainer.Mandates' participate in the 'Mandate_Person' relationship. 0 related 'Mandate_Person_Target' were found. 1 'Mandate_Person_Target' is expected.
The delete works if I include the Person Property during the population/selection or if I visit the Property (lazyloading), but I DONT LIKE to materialize/hold many entities only for the deletion case and I DONT LIKE to trigger more than a single DELETE query to db!
The fact that, if you have the navigation property mandate.Person populated, the following SQL statement ...
delete [dbo].[Mandates]
where (([Id] = #0) and ([PersonId] = #1))
... is sent to the database, lets me think that the navigation property indeed must be populated with a person with the correct PersonId to delete the parent.
I have no idea why Entity Framework just doesn't send a delete statement with the primary key ...
delete [dbo].[Mandates]
where ([Id] = #0)
... as I had expected.
Edit
If the Mandate entity has a foreign key property PersonId for the Person navigation property, the expected SQL (the second above) is sent to the database. In this case the Person navigation property can be null and the value of the FK property PersonId doesn't matter.
Edit 2
If you don't want to introduce a FK property the way with the least DB-roundtrip-costs would probably be to fetch the person's Id and then create a dummy person with that key in memory:
// ...
var personId = context.Mandates
.Where(m => m.Id == mandate.Id)
.Select(m => m.Person.Id)
.Single();
mandate.Person = new Person { Id = personId };
this.mandateRepository.Attach(mandate);
this.mandateRepository.Delete(mandate);
// ...
I am using DB first method, EF 4.1 with DbContext POCO code gen.
My database has a many-to-many relationship as shown below:
Employee
EmployeeId
EmployeeName
Account
AccountId
AccountName
EmployeeAccount
EmployeeId
AccountId
The problem occurs when I am trying to insert a new Employee, and assign them a pre existing account, so I am basically doing this as below:
Employee emp = new Employee();
emp.EmployeeName = "Test";
emp.Accounts.Add(MethodThatLooksUpAccountByName("SomeAccountName"));
context.Employees.Add(emp);
context.SaveChanges();
The SQL this is executing (incorrectly), is attempting to INSERT a new [Account] record, and this is failing on a constraint violation. Of course, it should not INSERT a new [Account] record, it should only insert a new [EmployeeAccount] record, after inserting the [Employee].
Any advice? Thanks.
MethodThatLooksUpAccountByName does this method return an attached or detached object? In any case, you may try to attach the object it returns to the context.
Employee emp = new Employee();
emp.EmployeeName = "Test";
var acc = MethodThatLooksUpAccountByName("SomeAccountName");
context.Attach(acc); //I don't remember if it's attach or attachobject, but intellisense should help you there.
emp.Accounts.Add(acc);
context.Employees.Add(emp);
context.SaveChanges();
Say I have two entities with about 20 properties per entity and a Many-to-Many relationship like so:
User (Id int,Name string, .......)
Issue (Id int,Name string, .......)
IssueAssignment (UserId,RoleId)
I want to create a new Issue and assign it to a number of existing Users. If I have code like so:
foreach(var userId in existingUserIds)
{
int id = userId
var user = _db.Users.First(r => r.Id == id);
issue.AssignedUsers.add(user);
}
_db.Users.AddObject(user);
_db.SaveChanges();
I noticed it seems terrribly inefficient when I run it against my SQL Database. If I look at
the SQL Profiler it's doing the following:
SELECT TOP(1) * FROM User WHERE UserId = userId
SELECT * FROM IssueAssignment ON User.Id = userId
INSERT INTO User ....
INSERT INTO IssueAssignment
My questions are:
(a) why do (1) and (2) have to happen at all?
(b) Both (1) and (2) bring back all fields do I need to do a object projection to limit the
fields, seems like unnecessary work too.
Thanks for the help
I have some possible clues for you:
This is how EF behaves. _db.Users is actaully a query and calling First on the query means executing the query in database.
I guess you are using EFv4 with T4 template and lazy loading is turned on. T4 templates create 'clever' objects which are able to fixup their navigation properties so once you add a User to an Issue it internally triggers fixup and tries to add the Issue to the User as well. This in turns triggers lazy loading of all issues related to the user.
So the trick is using dummy objects instead of real user. You know the id and you only want to create realtion between new issue and existing user. Try this (works with EFv4+ and POCOs):
foreach(var userId in existingUserIds)
{
var user = new User { Id = userId };
var _db.Users.Attach(user); // User with this Id mustn't be already loaded
issue.AssignedUsers.Add(user);
}
context.Issues.AddObject(issue);
context.SaveChanges();