I am using entity framework 6 code first. I am having a little trouble following what’s going on vis-a-vis navigation properties and foreign keys. Let me elaborate with an example. In this example, I am creating an object and observing what happens with regards to the navigation property when I create an entity that the FK references .
Entities:
DomainUser {AccountStatus … , UserAccount, UserAccountId} references UserAccount
UserAccount { UserAccountId ... }
1st Case - Navigation Property is null Upon Creation:
var newAccount = userAccountService.CreateAccount(userName, password, email);
var domainUser = new DomainUser
{
AccountStatus = active,
UserAccountId = newAccount.ID
};
domainUserRepository.Add(domainUser); // save changes is called on the context in this method.
return domainUser; // at this point, the UserAccount property of domainUser is null
2nd Case - InvalidOperationExceptionException Thrown
var newAccount = userAccountService.CreateAccount(userName, password, email);
var domainUser = new DomainUser
{
AccountStatus = active,
UserAccount = newAccount
};
domainUserRepository.Add(domainUser); // "An entity object cannot be referenced by multiple instances of IEntityChangeTracker."
return domainUser;
So my question is, how can I use the navigation property UserAccount?Do I have to use the UserAccountId to fetch it from the database? If so, how do I then attach it to the DomainUser object?I feel like this is something which should have been intuitive. But its not.
Related
I have two tables: Tasks and Users (ApplicationUser), there is a many to many relationship between them, I'm using the pre-defined table Users of Microsoft Identity,
When I update an existing Task and assign new users to it:
var taskToUpdate = context.Tasks.Find(taskVM.Id);
context.Entry(taskToUpdate).State = EntityState.Modified;
taskToUpdate.Users = new List<ApplicationUser>();
foreach (var user in taskVM._Users)
{
var userDB = context.Users.Find(user.Id);
context.Entry(userDB).State = EntityState.Unchanged;
taskToUpdate.Users.Add(user);
}
context.SaveChanges();
But when SaveChanges is called I got this error:
Validation failed for one or more entities. See
'EntityValidationErrors' property for more details.
in EntityValidationErrors:
ErrorMessage = "User name Sohaib is already taken."
PropertyName User
when calling the .Users.Add method EF assumes you are adding new entities thus creating the new User record and throwing the exception
possible solution is
var taskToUpdate = context.Tasks.Find(taskVM.Id);
taskToUpdate.Users = taskVM.Users;
context.SaveChanges();
I have a problem with saving changes to the database with entity framework. I'm using three tables; AspNetUsers, tblCountry, and tblCountry_AspNetUsers.
tblCountry_AspNetUsers consists of two columns; UserId and CountryId, which creates a one-to-many relationship between tblCountry and AspNetUsers.
Currently, what I want to do is change the country of a specific user, but entity framework doesn't let me access the tblCountry_AspNetUsers database, and instead creates an ICollection of AspNetUsers on tblCountry. I could assign the Id on the AspNetUser directly, but I don't want to start adding/removing columns from identity tables just yet since I'm using database first, and I've heard it can lead to problems.
Anyway, I can remove just fine from the ICollection and save those changes to the database, but when I try to add the same user to a different country, it doesn't save to the database properly, but I can find the user in the context object when debugging.
I've tried attaching and changing the entitystate to both added and modified, but when I try to do this, it breaks out of the method and doesn't update the database. (basically it freezes when I try to attach)
My code for editing a user looks like the following:
(Note that UserManager handles the identity usermodel, ApplicationUser,, which is not the same as AspNetUsers in this aspect)
(Also, tblCountry.AspNetUsers refers to the ICollection of users assigned to a specific country)
...
var aspuser = new AspNetUsers();
using (DbContext dc = new DbContext())
{
aspuser = dc.AspNetUsers.First(x => x.Id == userid);
var user = await UserManager.FindByIdAsync(userid);
user.Email = updatedUser.UserName;
user.UserName = updatedUser.Email;
var result = await UserManager.UpdateAsync(user);
aspuser.tblCountry.AspNetUsers.Remove(aspuser);
await dc.SaveChangesAsync();
}
using (DbContext dc = new DbContext())
{
var c = _country.GetById(newcountryid);
c.AspNetUsers.Add(aspuser);
await dc.SaveChangesAsync();
}
return Users(userid);
}
It would be extremely easy if it was a table I could access directly, but with an ICollection like this I'm confused as to what I should do to make it work, and I appreciate any input!
Cheers
This line aspuser = dc.AspNetUsers.First(x => x.Id == userid); is inside using (DbContext dc = new DbContext()) which means that object aspuser is detached from context when you leave that block. You should do:
var c = _country.GetById(newcountryid);
c.AspNetUsers.Add(aspuser);
await dc.SaveChangesAsync();
in the same using block as the above code, why did you separate it?
I have this entity, want to update using entityframework
EmployeeModel employee = new EmployeeModel
{
Id = 1000, //This one must
FirstName = modifiedValue,
Email = modifiedValue,
LastName = originalValue,
Phone = originalValue
};
Code to update
_db.ObjectStateManager.ChangeObjectState(employee, EntityState.Modified);
_db.SaveChanges();
This is the SQL statement got once updated
Update Employee set Id=1138,FirstName='modifiedValue',Email='modifiedValue',LastName= 'OriginalValue',phone='originalValue' where Id=1138
But I am expecting this
Update Employee set FirstName='modifiedValue', Email='modifiedValue' where Id=1138.
I dont know what I am missing here. Please let me know.
This problem is common when dealing with DTOs. An employee entity is fetched from the database, mapped to a DTO and sent over the wire. The client then modifies this DTO and sends it back to the server.
When you touch (set) a property on an EF entity, EF will assume that the value has been changed. Even if the old value and the new value are exactly the same.
The same problem occurs when you map the DTO to a new Entity and attach it to EF and updating its status to 'Modified'.
Using AutoMapper:
// This will result in the full update statement
var employee = AutoMapper.Mapper.Map<EmployeeDto, Employee>(dto);
// This will result in a smaller update statement (only actual changes)
var employee = dbContext.Employees.Find(dto.Id);
AutoMapper.Mapper.Map(dto, employee);
Or, manually (I would avoid doing this, but just for the sake of completeness):
// This will result in a smaller update statement (only actual changes)
var employee = dbContext.Employees.Find(dto.Id);
if (employee.Email != dto.Email )
employee.Email = dto.Email;
There are probably some other ways for dealing with this problem... but using AutoMapper together with Entity Framework correctly is definitely one of the easiest ways.
This is the solution I got
var entity = _db.CreateObjectSet<Employee>();
entity.Detach(employee);
entity.Attach(employee);
foreach (string modifiedPro in employeeModel.ModifiedProperties){
_db.ObjectStateManager.GetObjectStateEntry(employee).SetModifiedProperty(modifiedPro);}
_db.SaveChanges();
Only modified values in the sql update statement
Update Employee set FirstName='modifiedValue', Email='modifiedValue' where Id=1138.
If anybody knows better answer than this, Please post your suggestions
You can try this way
public update(Person model)
{
// Here model is model return from form on post
var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault();
var UpdatedObj = (Person) Entity.CheckUpdateObject(oldobj, model);
db.Entry(oldobj).CurrentValues.SetValues(UpdatedObj);
}
public static object CheckUpdateObject(object originalObj, object updateObj)
{
foreach (var property in updateObj.GetType().GetProperties())
{
if (property.GetValue(updateObj, null) == null)
{
property.SetValue(updateObj,originalObj.GetType().GetProperty(property.Name)
.GetValue(originalObj, null));
}
}
return updateObj;
}
I am using EF 4.1 in a MVC 3 project and I am having problems with inserting an object. I am using session as well to hold onto some objects. In concrete :
I have a simple class with a parent child relationship:
public class Event
{
public User Promotor {get;set;}
}
The promotor is based on the CurrentUser. In my application I store the CurrentUser in http session. Now when I add an event like this the user (and all related objects) gets inserted one more time with a new primary key.
//1st request inserts/loads the user
User user;
using (var context = new MyDbContext())
{
user = new User();
context.Users.Add(user);
context.SaveChanges();
}
//2nd request saves the event
var before = db.Users.Count();
var #event = new Event
{
Promotor = user, //user was kept in Session
};
db.Entry(#event).State = EntityState.Added;
db.SaveChanges();
When i check the state of the user it is 'added' as well although the primary key is not 0 and EF should know it is already persistent. How can fix this without adding a lot of to my persistency code. Do I have to reattach my currentuser to the new dbcontext on every request? This will lead to db code 'leaking' into my application. I want to keep the DB stuff in a data layer. I am using a repository like this :
public void Save(T entity)
{
dbContext.Entry(entity).State = IsPersistent(entity) ?
EntityState.Modified : EntityState.Added;
dbContext.SaveChanges();
}
As you've already mentioned yourself, reattaching the user to your current context should solve the problem. The only other way I know of, would be to retrieve the object once again in your context based on the primary key value.
How can I update a single property of a record without retrieving it first?
I'm asking in the context of EF Code First 4.1
Says I have a class User, mapping to table Users in Database:
class User
{
public int Id {get;set;}
[Required]
public string Name {get;set;}
public DateTime LastActivity {get;set;}
...
}
Now I want to update LastActivity of a user. I have user id. I can easily do so by querying the user record, set new value to LastActivity, then call SaveChanges(). But this would result in a redundant query.
I work around by using Attach method. But because EF throws a validation exception on Name if it's null, I set Name to a random string (will not be updated back to DB). But this doesn't seem a elegant solution:
using (var entities = new MyEntities())
{
User u = new User {Id = id, Name="this wont be updated" };
entities.Users.Attach(u);
u.LastActivity = DateTime.Now;
entities.SaveChanges();
}
I would be very appriciate if someone can provide me a better solution. And forgive me for any mistake as this is the first time I've asked a question on SO.
This is a problem of validation implementation. The validation is able to validate only a whole entity. It doesn't validate only modified properties as expected. Because of that the validation should be turned off in scenarios where you want to use incomplete dummy objects:
using (var entities = new MyEntities())
{
entities.Configuration.ValidateOnSaveEnabled = false;
User u = new User {Id = id, LastActivity = DateTime.Now };
entities.Users.Attach(u);
entities.Entry(user).Property(u => u.LastActivity).IsModified = true;
entities.SaveChanges();
}
This is obviously a problem if you want to use the same context for update of dummy objects and for update of whole entities where the validation should be used. The validation take place in SaveChanges so you can't say which objects should be validated and which don't.
I'm actually dealing with this right now. What I decided to do was override the ValidateEntity method in the DB context.
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
var result = base.ValidateEntity(entityEntry, items);
var errors = new List<DbValidationError>();
foreach (var error in result.ValidationErrors)
{
if (entityEntry.Property(error.PropertyName).IsModified)
{
errors.Add(error);
}
}
return new DbEntityValidationResult(entityEntry, errors);
}
I'm sure there's some holes that can be poked in it, but it seemed better than the alternatives.
You can try a sort of hack:
context.Database.ExecuteSqlCommand("update [dbo].[Users] set [LastActivity] = #p1 where [Id] = #p2",
new System.Data.SqlClient.SqlParameter("p1", DateTime.Now),
new System.Data.SqlClient.SqlParameter("p2", id));